diff options
29 files changed, 18735 insertions, 0 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk new file mode 100644 index 0000000..b84e1b6 --- /dev/null +++ b/CleanSpec.mk @@ -0,0 +1,49 @@ +# Copyright (C) 2007 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. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ diff --git a/METADATA b/METADATA new file mode 100644 index 0000000..d97975c --- /dev/null +++ b/METADATA @@ -0,0 +1,3 @@ +third_party { + license_type: NOTICE +} @@ -0,0 +1,4 @@ +arabawy@google.com +etancohen@google.com +kumachang@google.com +wangroger@google.com diff --git a/synadhd/Android.mk b/synadhd/Android.mk new file mode 100644 index 0000000..1a52017 --- /dev/null +++ b/synadhd/Android.mk @@ -0,0 +1,3 @@ +ifeq ($(BOARD_WLAN_DEVICE),synadhd) + include $(call all-subdir-makefiles) +endif diff --git a/synadhd/config/Android.mk b/synadhd/config/Android.mk new file mode 100644 index 0000000..8c3b13f --- /dev/null +++ b/synadhd/config/Android.mk @@ -0,0 +1,30 @@ +# +# Copyright (C) 2008 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. +# +LOCAL_PATH := $(call my-dir) + +######################## + +WIFI_DRIVER_SOCKET_IFACE := wlan0 +ifeq ($(strip $(WPA_SUPPLICANT_VERSION)),VER_0_8_X) + include external/wpa_supplicant_8/wpa_supplicant/wpa_supplicant_conf.mk +else +ifeq ($(strip $(WPA_SUPPLICANT_VERSION)),VER_0_6_X) + include external/wpa_supplicant_6/wpa_supplicant/wpa_supplicant_conf.mk +else + include external/wpa_supplicant/wpa_supplicant_conf.mk +endif +endif +####################### diff --git a/synadhd/config/config-syna.mk b/synadhd/config/config-syna.mk new file mode 100644 index 0000000..df3d66f --- /dev/null +++ b/synadhd/config/config-syna.mk @@ -0,0 +1,21 @@ +# +# Copyright (C) 2008 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. +# + +######################## +PRODUCT_COPY_FILES += \ + hardware/synaptics/wlan/synadhd/config/wpa_supplicant_overlay.conf:$(TARGET_COPY_OUT_VENDOR)/etc/wifi/wpa_supplicant_overlay.conf \ + hardware/synaptics/wlan/synadhd/config/p2p_supplicant_overlay.conf:$(TARGET_COPY_OUT_VENDOR)/etc/wifi/p2p_supplicant_overlay.conf +######################## diff --git a/synadhd/config/p2p_supplicant_overlay.conf b/synadhd/config/p2p_supplicant_overlay.conf new file mode 100644 index 0000000..e44c477 --- /dev/null +++ b/synadhd/config/p2p_supplicant_overlay.conf @@ -0,0 +1,5 @@ +disable_scan_offload=1 +wowlan_triggers=any +p2p_no_go_freq=5170-5740 +p2p_search_delay=0 +no_ctrl_interface=
\ No newline at end of file diff --git a/synadhd/config/wpa_supplicant_overlay.conf b/synadhd/config/wpa_supplicant_overlay.conf new file mode 100644 index 0000000..740792b --- /dev/null +++ b/synadhd/config/wpa_supplicant_overlay.conf @@ -0,0 +1,5 @@ +disable_scan_offload=1 +wowlan_triggers=any +p2p_disabled=1 +filter_rssi=-75 +no_ctrl_interface=
\ No newline at end of file diff --git a/synadhd/wifi_hal/Android.mk b/synadhd/wifi_hal/Android.mk new file mode 100755 index 0000000..a3dc4a3 --- /dev/null +++ b/synadhd/wifi_hal/Android.mk @@ -0,0 +1,69 @@ +# Copyright (C) 2011 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. + +LOCAL_PATH := $(call my-dir) + +# Make the HAL library +# ============================================================ +include $(CLEAR_VARS) + +LOCAL_CFLAGS := \ + -Wall \ + -Werror \ + -Wno-format \ + -Wno-reorder \ + -Wno-unused-function \ + -Wno-unused-parameter \ + -Wno-unused-private-field \ + -Wno-unused-variable \ + -Wno-unused-parameter + +LOCAL_C_INCLUDES += \ + external/libnl/include \ + $(call include-path-for, libhardware_legacy)/hardware_legacy \ + external/wpa_supplicant_8/src/drivers + +LOCAL_C_INCLUDES += \ + external/boringssl/include \ + external/boringssl/src/crypto/digest \ + external/boringssl/src/crypto/evp/ + +LOCAL_HEADER_LIBRARIES := libutils_headers liblog_headers + +ifneq ($(wildcard vendor/google/libraries/GoogleWifiConfigLib),) +LOCAL_SHARED_LIBRARIES += \ + google_wifi_firmware_config_version_c_wrapper +LOCAL_CFLAGS += -DGOOGLE_WIFI_FW_CONFIG_VERSION_C_WRAPPER +#only for pixel feature +LOCAL_CFLAGS += -DRING_DUMP +endif + +LOCAL_SRC_FILES := \ + wifi_hal.cpp \ + rtt.cpp \ + common.cpp \ + cpp_bindings.cpp \ + gscan.cpp \ + nan.cpp \ + link_layer_stats.cpp \ + wifi_logger.cpp \ + wifi_offload.cpp \ + twt.cpp + +LOCAL_MODULE := libwifi-hal-syna +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_PROPRIETARY_MODULE := true + +include $(BUILD_STATIC_LIBRARY) diff --git a/synadhd/wifi_hal/common.cpp b/synadhd/wifi_hal/common.cpp new file mode 100755 index 0000000..5a9d5ab --- /dev/null +++ b/synadhd/wifi_hal/common.cpp @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * 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 <stdint.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/errqueue.h> + +#include <linux/pkt_sched.h> +#include <netlink/object-api.h> +#include <netlink/netlink.h> +#include <netlink/socket.h> +#include <netlink/handlers.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +/* test mode flag for halutil only */ +bool halutil_mode = false; +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; + + /* TODO: check for multiple handlers? */ + pthread_mutex_lock(&info->cb_lock); + + wifi_error result = WIFI_ERROR_OUT_OF_MEMORY; + + 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; + ALOGV("Successfully added event handler %p:%p for command %d at %d", + arg, func, cmd, info->num_event_cb); + info->num_event_cb++; + result = WIFI_SUCCESS; + } + + 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; + + /* TODO: check for multiple handlers? */ + pthread_mutex_lock(&info->cb_lock); + + wifi_error result = WIFI_ERROR_OUT_OF_MEMORY; + + if (info->num_event_cb < info->alloc_event_cb) { + /* To avoid an unwanted duplication of the record, find first. + * Update it if the same record is already exist. + * KEY => [nl_cmd, vendor_id, vendor_subcmd] + */ + int i = 0; + bool is_update = false; + for (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)) { + is_update = true; + break; + } + } + + if (is_update) { + info->event_cb[i].cb_func = func; + info->event_cb[i].cb_arg = arg; + } else { + 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++; + } + ALOGI("%s ""event handler %p:%p for vendor 0x%0x and subcmd 0x%0x at %d", + is_update ? "Updated" : "Added", arg, func, id, subcmd, info->num_event_cb); + result = WIFI_SUCCESS; + } + + 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) { + ALOGV("Successfully removed event handler %p:%p for cmd = 0x%0x from %d", + info->event_cb[i].cb_arg, info->event_cb[i].cb_func, cmd, i); + + memmove(&info->event_cb[i], &info->event_cb[i+1], + (info->num_event_cb - i - 1) * sizeof(cb_info)); + info->num_event_cb--; + break; + } + } + + pthread_mutex_unlock(&info->cb_lock); +} + +void wifi_unregister_vendor_handler_without_lock(wifi_handle handle, uint32_t id, int subcmd) +{ + hal_info *info = (hal_info *)handle; + + 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) { + ALOGI("Successfully removed event handler %p:%p for vendor 0x%0x, subcmd 0x%0x from %d", + info->event_cb[i].cb_arg, info->event_cb[i].cb_func, id, subcmd, i); + memmove(&info->event_cb[i], &info->event_cb[i+1], + (info->num_event_cb - i - 1) * sizeof(cb_info)); + info->num_event_cb--; + break; + } + } +} + +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); + wifi_unregister_vendor_handler_without_lock(handle, id, subcmd); + pthread_mutex_unlock(&info->cb_lock); +} + +wifi_error wifi_register_cmd(wifi_handle handle, int id, WifiCommand *cmd) +{ + hal_info *info = (hal_info *)handle; + + ALOGV("registering command %d", id); + + wifi_error result = WIFI_ERROR_OUT_OF_MEMORY; + + if (info->num_cmd < info->alloc_cmd) { + info->cmd[info->num_cmd].id = id; + info->cmd[info->num_cmd].cmd = cmd; + ALOGV("Successfully added command %d: %p at %d", id, cmd, info->num_cmd); + info->num_cmd++; + result = WIFI_SUCCESS; + } else { + ALOGE("Failed to add command %d: %p at %d, reached max limit %d", + id, cmd, info->num_cmd, info->alloc_cmd); + } + + return result; +} + +WifiCommand *wifi_unregister_cmd(wifi_handle handle, int id) +{ + hal_info *info = (hal_info *)handle; + + ALOGV("un-registering command %d", id); + + WifiCommand *cmd = NULL; + + for (int i = 0; i < info->num_cmd; i++) { + if (info->cmd[i].id == id) { + cmd = info->cmd[i].cmd; + memmove(&info->cmd[i], &info->cmd[i+1], (info->num_cmd - i - 1) * sizeof(cmd_info)); + info->num_cmd--; + ALOGV("Successfully removed command %d: %p from %d", id, cmd, i); + break; + } + } + + if (!cmd) { + ALOGI("Failed to remove command %d: %p", id, cmd); + } + + return cmd; +} + +WifiCommand *wifi_get_cmd(wifi_handle handle, int id) +{ + hal_info *info = (hal_info *)handle; + + WifiCommand *cmd = NULL; + + for (int i = 0; i < info->num_cmd; i++) { + if (info->cmd[i].id == id) { + cmd = info->cmd[i].cmd; + break; + } + } + + return cmd; +} + +void wifi_unregister_cmd(wifi_handle handle, WifiCommand *cmd) +{ + hal_info *info = (hal_info *)handle; + + for (int i = 0; i < info->num_cmd; i++) { + if (info->cmd[i].cmd == cmd) { + int id = info->cmd[i].id; + memmove(&info->cmd[i], &info->cmd[i+1], (info->num_cmd - i - 1) * sizeof(cmd_info)); + info->num_cmd--; + ALOGV("Successfully removed command %d: %p from %d", id, cmd, i); + break; + } + } +} + +wifi_error wifi_cancel_cmd(wifi_request_id id, wifi_interface_handle iface) +{ + wifi_handle handle = getWifiHandle(iface); + + WifiCommand *cmd = wifi_unregister_cmd(handle, id); + ALOGV("Cancel WifiCommand = %p", cmd); + if (cmd) { + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + + return WIFI_ERROR_INVALID_ARGS; +} + +wifi_error wifi_get_cancel_cmd(wifi_request_id id, wifi_interface_handle iface) +{ + wifi_handle handle = getWifiHandle(iface); + WifiCommand *cmd = wifi_get_cmd(handle, id); + ALOGV("Get Cancel WifiCommand = %p", cmd); + if (cmd) { + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + + return WIFI_ERROR_INVALID_ARGS; +} +void set_hautil_mode(bool util_mode) +{ + halutil_mode = util_mode; +} +bool get_halutil_mode() +{ + return halutil_mode; +} diff --git a/synadhd/wifi_hal/common.h b/synadhd/wifi_hal/common.h new file mode 100755 index 0000000..ebf6cb3 --- /dev/null +++ b/synadhd/wifi_hal/common.h @@ -0,0 +1,550 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * 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 "wifi_hal.h" + +#ifndef __WIFI_HAL_COMMON_H__ +#define __WIFI_HAL_COMMON_H__ + +#define LOG_TAG "WifiHAL" + +#include <log/log.h> +#include "nl80211_copy.h" +#include "sync.h" + +#define SOCKET_BUFFER_SIZE (32768U) +#define RECV_BUF_SIZE (4096) +#define DEFAULT_EVENT_CB_SIZE (64) +#define DEFAULT_CMD_SIZE (64) +#define DOT11_OUI_LEN 3 +#define DOT11_MAX_SSID_LEN 32 + +#define ETHERTYPE_IP 0x0800 /* IP */ +#define ETHERTYPE_IPV6 0x86dd /* IP protocol version 6 */ +#define MAX_PROBE_RESP_IE_LEN 2048 +/* + Vendor OUI - This is a unique identifier that identifies organization. Lets + code Android specific functions with Google OUI; although vendors can do more + with their own OUI's as well. + */ + +const uint32_t GOOGLE_OUI = 0x001A11; +const uint32_t BRCM_OUI = 0x001018; +/* TODO: define vendor OUI here */ + + +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" + +#define NMR2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5], (a)[6], (a)[7] +#define NMRSTR "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x" +#define NAN_MASTER_RANK_LEN 8 +#define NAN_SCID_INFO_LEN 16 + +#define SAR_CONFIG_SCENARIO_COUNT 100 +#define MAX_NUM_RADIOS 3 +#define MAX_CMD_RESP_BUF_LEN 8192 + +/* + This enum defines ranges for various commands; commands themselves + can be defined in respective feature headers; i.e. find gscan command + definitions in gscan.cpp + */ + +typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg); +typedef enum { + /* don't use 0 as a valid subcommand */ + VENDOR_NL80211_SUBCMD_UNSPECIFIED, + + /* define all vendor startup commands between 0x0 and 0x0FFF */ + VENDOR_NL80211_SUBCMD_RANGE_START = 0x0001, + VENDOR_NL80211_SUBCMD_RANGE_END = 0x0FFF, + + /* define all GScan related commands between 0x1000 and 0x10FF */ + ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START = 0x1000, + ANDROID_NL80211_SUBCMD_GSCAN_RANGE_END = 0x10FF, + + /* define all NearbyDiscovery related commands between 0x1100 and 0x11FF */ + ANDROID_NL80211_SUBCMD_NBD_RANGE_START = 0x1100, + ANDROID_NL80211_SUBCMD_NBD_RANGE_END = 0x11FF, + + /* define all RTT related commands between 0x1100 and 0x11FF */ + ANDROID_NL80211_SUBCMD_RTT_RANGE_START = 0x1100, + ANDROID_NL80211_SUBCMD_RTT_RANGE_END = 0x11FF, + + ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START = 0x1200, + ANDROID_NL80211_SUBCMD_LSTATS_RANGE_END = 0x12FF, + + /* define all Logger related commands between 0x1400 and 0x14FF */ + ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START = 0x1400, + ANDROID_NL80211_SUBCMD_DEBUG_RANGE_END = 0x14FF, + + /* define all wifi offload related commands between 0x1600 and 0x16FF */ + ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START = 0x1600, + ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_END = 0x16FF, + + /* define all NAN related commands between 0x1700 and 0x17FF */ + ANDROID_NL80211_SUBCMD_NAN_RANGE_START = 0x1700, + ANDROID_NL80211_SUBCMD_NAN_RANGE_END = 0x17FF, + + /* define all Android Packet Filter related commands between 0x1800 and 0x18FF */ + ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START = 0x1800, + ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_END = 0x18FF, + + /* define all tx power related commands between 0x1900 and 0x1910 */ + ANDROID_NL80211_SUBCMD_TX_POWER_RANGE_START = 0x1900, + ANDROID_NL80211_SUBCMD_TX_POWER_RANGE_END = 0x1910, + + /* define all thermal mode related commands between 0x1920 and 0x192F */ + ANDROID_NL80211_SUBCMD_MITIGATION_RANGE_START = 0x1920, + ANDROID_NL80211_SUBCMD_MITIGATION_RANGE_END = 0x192F, + + /* define all DSCP related commands between 0x2000 and 0x20FF */ + ANDROID_NL80211_SUBCMD_DSCP_RANGE_START = 0x2000, + ANDROID_NL80211_SUBCMD_DSCP_RANGE_END = 0x20FF, + + /* define all Channel Avoidance related commands between 0x2100 and 0x211F */ + ANDROID_NL80211_SUBCMD_CHAVOID_RANGE_START = 0x2100, + ANDROID_NL80211_SUBCMD_CHAVOID_RANGE_END = 0x211F, + + /* define all OTA Download related commands between 0x2120 and 0x212F */ + ANDROID_NL80211_SUBCMD_OTA_DOWNLOAD_START = 0x2120, + ANDROID_NL80211_SUBCMD_OTA_DOWNLOAD_END = 0x212F, + + /* define all VOIP mode config related commands between 0x2130 and 0x213F */ + ANDROID_NL80211_SUBCMD_VIOP_MODE_START = 0x2130, + ANDROID_NL80211_SUBCMD_VIOP_MODE_END = 0x213F, + + /* define all TWT related commands between 0x2140 and 0x214F */ + ANDROID_NL80211_SUBCMD_TWT_START = 0x2140, + ANDROID_NL80211_SUBCMD_TWT_END = 0x214F, + + /* define all Usable Channel related commands between 0x2150 and 0x215F */ + ANDROID_NL80211_SUBCMD_USABLE_CHANNEL_START = 0x2150, + ANDROID_NL80211_SUBCMD_USABLE_CHANNEL_END = 0x215F, + + /* define all init/deinit related commands between 0x2160 and 0x216F */ + ANDROID_NL80211_SUBCMD_INIT_DEINIT_RANGE_START = 0x2160, + ANDROID_NL80211_SUBCMD_INIT_DEINIT_RANGE_END = 0x216F, + + /* This is reserved for future usage */ + +} ANDROID_VENDOR_SUB_COMMAND; + +typedef enum { + + GSCAN_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START, + + GSCAN_SUBCMD_SET_CONFIG, /* 0x1001 */ + + GSCAN_SUBCMD_SET_SCAN_CONFIG, /* 0x1002 */ + GSCAN_SUBCMD_ENABLE_GSCAN, /* 0x1003 */ + GSCAN_SUBCMD_GET_SCAN_RESULTS, /* 0x1004 */ + GSCAN_SUBCMD_SCAN_RESULTS, /* 0x1005 */ + + GSCAN_SUBCMD_SET_HOTLIST, /* 0x1006 */ + + GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_CONFIG, /* 0x1007 */ + GSCAN_SUBCMD_ENABLE_FULL_SCAN_RESULTS, /* 0x1008 */ + GSCAN_SUBCMD_GET_CHANNEL_LIST, /* 0x1009 */ + + WIFI_SUBCMD_GET_FEATURE_SET, /* 0x100A */ + WIFI_SUBCMD_GET_FEATURE_SET_MATRIX, /* 0x100B */ + WIFI_SUBCMD_SET_PNO_RANDOM_MAC_OUI, /* 0x100C */ + WIFI_SUBCMD_NODFS_SET, /* 0x100D */ + WIFI_SUBCMD_SET_COUNTRY_CODE, /* 0x100E */ + /* Add more sub commands here */ + GSCAN_SUBCMD_SET_EPNO_SSID, /* 0x100F */ + + WIFI_SUBCMD_SET_SSID_WHITE_LIST, /* 0x1010 */ + WIFI_SUBCMD_SET_ROAM_PARAMS, /* 0x1011 */ + WIFI_SUBCMD_ENABLE_LAZY_ROAM, /* 0x1012 */ + WIFI_SUBCMD_SET_BSSID_PREF, /* 0x1013 */ + WIFI_SUBCMD_SET_BSSID_BLACKLIST, /* 0x1014 */ + + GSCAN_SUBCMD_ANQPO_CONFIG, /* 0x1015 */ + WIFI_SUBCMD_SET_RSSI_MONITOR, /* 0x1016 */ + WIFI_SUBCMD_CONFIG_ND_OFFLOAD, /* 0x1017 */ + WIFI_SUBCMD_CONFIG_TCPACK_SUP, /* 0x1018 */ + WIFI_SUBCMD_FW_ROAM_POLICY, /* 0x1019 */ + WIFI_SUBCMD_ROAM_CAPABILITY, /* 0x101a */ + WIFI_SUBCMD_SET_LATENCY_MODE, /* 0x101b */ + WIFI_SUBCMD_SET_MULTISTA_PRIMARY_CONNECTION, /* 0x101c */ + WIFI_SUBCMD_SET_MULTISTA_USE_CASE, /* 0x101d */ + WIFI_SUBCMD_SET_DTIM_CONFIG, /* 0x101e */ + WIFI_SUBCMD_CONFIG_INDOOR_STATE, + + GSCAN_SUBCMD_MAX, + + /* NAN related */ + NAN_SUBCMD_ENABLE = ANDROID_NL80211_SUBCMD_NAN_RANGE_START, + NAN_SUBCMD_DISABLE, /* 0x1701 */ + NAN_SUBCMD_PUBLISH, /* 0x1702 */ + NAN_SUBCMD_SUBSCRIBE, /* 0x1703 */ + NAN_SUBCMD_PUBLISH_CANCEL, /* 0x1704 */ + NAN_SUBCMD_SUBSCRIBE_CANCEL, /* 0x1705 */ + NAN_SUBCMD_TRANSMIT_FOLLOWUP, /* 0x1706 */ + NAN_SUBCMD_CONFIG, /* 0x1707 */ + NAN_SUBCMD_TCA, /* 0x1708 */ + NAN_SUBCMD_STATS, /* 0x1709 */ + NAN_SUBCMD_GET_CAPABILITIES, /* 0x170A */ + NAN_SUBCMD_DATA_PATH_IFACE_CREATE, /* 0x170B */ + NAN_SUBCMD_DATA_PATH_IFACE_DELETE, /* 0x170C */ + NAN_SUBCMD_DATA_PATH_REQUEST, /* 0x170D */ + NAN_SUBCMD_DATA_PATH_RESPONSE, /* 0x170E */ + NAN_SUBCMD_DATA_PATH_END, /* 0x170F */ + NAN_SUBCMD_DATA_PATH_SEC_INFO, /* 0x1710 */ + NAN_SUBCMD_VERSION_INFO, /* 0x1711 */ + NAN_SUBCMD_ENABLE_MERGE, /* 0x1712 */ + APF_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START, + APF_SUBCMD_SET_FILTER, + APF_SUBCMD_READ_FILTER, + WIFI_SUBCMD_TX_POWER_SCENARIO = ANDROID_NL80211_SUBCMD_TX_POWER_RANGE_START, + WIFI_SUBCMD_THERMAL_MITIGATION = ANDROID_NL80211_SUBCMD_MITIGATION_RANGE_START, + DSCP_SUBCMD_SET_TABLE = ANDROID_NL80211_SUBCMD_DSCP_RANGE_START, + DSCP_SUBCMD_RESET_TABLE, /* 0x2001 */ + CHAVOID_SUBCMD_SET_CONFIG = ANDROID_NL80211_SUBCMD_CHAVOID_RANGE_START, + + TWT_SUBCMD_GETCAPABILITY = ANDROID_NL80211_SUBCMD_TWT_START, + TWT_SUBCMD_SETUP_REQUEST, + TWT_SUBCMD_TEAR_DOWN_REQUEST, + TWT_SUBCMD_INFO_FRAME_REQUEST, + TWT_SUBCMD_GETSTATS, + TWT_SUBCMD_CLR_STATS, + + WIFI_SUBCMD_CONFIG_VOIP_MODE = ANDROID_NL80211_SUBCMD_VIOP_MODE_START, + + WIFI_SUBCMD_GET_OTA_CURRUNT_INFO = ANDROID_NL80211_SUBCMD_OTA_DOWNLOAD_START, + WIFI_SUBCMD_OTA_UPDATE, + WIFI_SUBCMD_USABLE_CHANNEL = ANDROID_NL80211_SUBCMD_USABLE_CHANNEL_START, + WIFI_SUBCMD_TRIGGER_SSR = ANDROID_NL80211_SUBCMD_INIT_DEINIT_RANGE_START, + WIFI_SUBCMD_GET_RADIO_COMBO_MATRIX, +} WIFI_SUB_COMMAND; + +typedef enum { + BRCM_RESERVED1 = 0, + BRCM_RESERVED2 = 1, + GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS = 2, + GSCAN_EVENT_HOTLIST_RESULTS_FOUND = 3, + GSCAN_EVENT_SCAN_RESULTS_AVAILABLE = 4, + GSCAN_EVENT_FULL_SCAN_RESULTS = 5, + RTT_EVENT_COMPLETE = 6, + GSCAN_EVENT_COMPLETE_SCAN = 7, + GSCAN_EVENT_HOTLIST_RESULTS_LOST = 8, + GSCAN_EVENT_EPNO_EVENT = 9, + GOOGLE_DEBUG_RING_EVENT = 10, + GOOGLE_DEBUG_MEM_DUMP_EVENT = 11, + GSCAN_EVENT_ANQPO_HOTSPOT_MATCH = 12, + GOOGLE_RSSI_MONITOR_EVENT = 13, + GOOGLE_MKEEP_ALIVE = 14, + + /* + * BRCM specific events should be placed after the Generic events + * in order to match between the DHD and HAL + */ + NAN_EVENT_ENABLED = 15, + NAN_EVENT_DISABLED = 16, + NAN_EVENT_SUBSCRIBE_MATCH = 17, + NAN_EVENT_PUBLISH_REPLIED_IND = 18, + NAN_EVENT_PUBLISH_TERMINATED = 19, + NAN_EVENT_SUBSCRIBE_TERMINATED = 20, + NAN_EVENT_DE_EVENT = 21, + NAN_EVENT_FOLLOWUP = 22, + NAN_EVENT_TRANSMIT_FOLLOWUP_IND = 23, + NAN_EVENT_DATA_REQUEST = 24, + NAN_EVENT_DATA_CONFIRMATION = 25, + NAN_EVENT_DATA_END = 26, + NAN_EVENT_BEACON = 27, + NAN_EVENT_SDF = 28, + NAN_EVENT_TCA = 29, + NAN_EVENT_SUBSCRIBE_UNMATCH = 30, + NAN_EVENT_UNKNOWN = 31, + BRCM_VENDOR_EVENT_HANGED = 33, + ROAM_EVENT_START, + GOOGLE_FILE_DUMP_EVENT = 37, + NAN_ASYNC_RESPONSE_DISABLED = 40, + BRCM_VENDOR_EVENT_TWT = 43, + BRCM_TPUT_DUMP_EVENT = 44, + NAN_EVENT_MATCH_EXPIRY = 45 +} WIFI_EVENT; + +typedef void (*wifi_internal_event_handler) (wifi_handle handle, int events); + +class WifiCommand; + +typedef struct { + int nl_cmd; + uint32_t vendor_id; + int vendor_subcmd; + nl_recvmsg_msg_cb_t cb_func; + void *cb_arg; +} cb_info; + +typedef struct { + wifi_request_id id; + WifiCommand *cmd; +} cmd_info; + +typedef struct { + wifi_handle handle; // handle to wifi data + char name[IFNAMSIZ+1]; // interface name + trailing null + int id; // id to use when talking to driver + bool is_virtual; // mark each iface as virtual or static +} interface_info; + +typedef struct { + + struct nl_sock *cmd_sock; // command socket object + struct nl_sock *event_sock; // event socket object + int nl80211_family_id; // family id for 80211 driver + int cleanup_socks[2]; // sockets used to implement wifi_cleanup + + bool in_event_loop; // Indicates that event loop is active + bool clean_up; // Indication to exit since cleanup has started + + wifi_internal_event_handler event_handler; // default event handler + wifi_cleaned_up_handler cleaned_up_handler; // socket cleaned up handler + + cb_info *event_cb; // event callbacks + int num_event_cb; // number of event callbacks + int alloc_event_cb; // number of allocated callback objects + pthread_mutex_t cb_lock; // mutex for the event_cb access + + cmd_info *cmd; // Outstanding commands + int num_cmd; // number of commands + int alloc_cmd; // number of commands allocated + + interface_info **interfaces; // array of interfaces + int num_interfaces; // number of interfaces + int max_num_interfaces; // max number of interfaces + wifi_subsystem_restart_handler restart_handler; // trigger sub system handler + + + // add other details +} hal_info; + +#define PNO_SSID_FOUND 0x1 +#define PNO_SSID_LOST 0x2 + +typedef struct wifi_pno_result { + unsigned char ssid[DOT11_MAX_SSID_LEN]; + unsigned char ssid_len; + signed char rssi; + u16 channel; + u16 flags; + mac_addr bssid; +} wifi_pno_result_t; + +typedef struct wifi_gscan_result { + u64 ts; // Time of discovery + u8 ssid[DOT11_MAX_SSID_LEN+1]; // null terminated + mac_addr bssid; // BSSID + u32 channel; // channel frequency in MHz + s32 rssi; // in db + u64 rtt; // in nanoseconds + u64 rtt_sd; // standard deviation in rtt + u16 beacon_period; // units are Kusec + u16 capability; // Capability information + u32 pad; +} wifi_gscan_result_t; + +typedef struct wifi_gscan_full_result { + wifi_gscan_result_t fixed; + u32 scan_ch_bucket; // scan chbucket bitmask + u32 ie_length; // byte length of Information Elements + u8 ie_data[1]; // IE data to follow +} wifi_gscan_full_result_t; + +void twt_deinit_handler(); + +typedef enum { + TWT_EVENT_INVALID = 0, + TWT_SETUP_RESPONSE = 1, + TWT_TEARDOWN_COMPLETION = 2, + TWT_INFORM_FRAME = 3, + TWT_NOTIFY = 4, + TWT_EVENT_LAST +} TwtEventType; + +typedef enum { + TWT_INVALID = 0, + TWT_SETUP_REQUEST = 1, + TWT_INFO_FRAME_REQUEST = 2, + TWT_TEAR_DOWN_REQUEST = 3, + TWT_LAST +} TwtRequestType; + +typedef enum { + TWT_ATTRIBUTE_INVALID = 0, + TWT_ATTRIBUTE_CONFIG_ID = 1, + TWT_ATTRIBUTE_NEG_TYPE = 2, + TWT_ATTRIBUTE_TRIGGER_TYPE = 3, + TWT_ATTRIBUTE_WAKE_DUR_US = 4, + TWT_ATTRIBUTE_WAKE_INT_US = 5, + TWT_ATTRIBUTE_WAKE_INT_MIN_US = 6, + TWT_ATTRIBUTE_WAKE_INT_MAX_US = 7, + TWT_ATTRIBUTE_WAKE_DUR_MIN_US = 8, + TWT_ATTRIBUTE_WAKE_DUR_MAX_US = 9, + TWT_ATTRIBUTE_AVG_PKT_SIZE = 10, + TWT_ATTRIBUTE_AVG_PKT_NUM = 11, + TWT_ATTRIBUTE_WAKE_TIME_OFF_US = 12, + TWT_ATTRIBUTE_ALL_TWT = 13, + TWT_ATTRIBUTE_RESUME_TIME_US = 14, + TWT_ATTRIBUTE_AVG_EOSP_DUR = 15, + TWT_ATTRIBUTE_EOSP_COUNT = 16, + TWT_ATTRIBUTE_NUM_SP = 17, + TWT_ATTRIBUTE_DEVICE_CAP = 18, + TWT_ATTRIBUTE_PEER_CAP = 19, + TWT_ATTRIBUTE_STATUS = 20, + TWT_ATTRIBUTE_REASON_CODE = 21, + TWT_ATTRIBUTE_RESUMED = 22, + TWT_ATTRIBUTE_NOTIFICATION = 23, + TWT_ATTRIBUTE_SUB_EVENT = 24, + TWT_ATTRIBUTE_NUM_PEER_STATS = 25, + TWT_ATTRIBUTE_AVG_PKT_NUM_TX = 26, + TWT_ATTRIBUTE_AVG_PKT_SIZE_TX = 27, + TWT_ATTRIBUTE_AVG_PKT_NUM_RX = 28, + TWT_ATTRIBUTE_AVG_PKT_SIZE_RX = 29, + TWT_ATTRIBUTE_MAX +} TWT_ATTRIBUTE; + +wifi_error wifi_register_handler(wifi_handle handle, int cmd, nl_recvmsg_msg_cb_t func, void *arg); +wifi_error wifi_register_vendor_handler(wifi_handle handle, + uint32_t id, int subcmd, nl_recvmsg_msg_cb_t func, void *arg); + +void wifi_unregister_handler(wifi_handle handle, int cmd); +void wifi_unregister_vendor_handler_without_lock(wifi_handle handle, uint32_t id, int subcmd); +void wifi_unregister_vendor_handler(wifi_handle handle, uint32_t id, int subcmd); + +wifi_error wifi_register_cmd(wifi_handle handle, int id, WifiCommand *cmd); +WifiCommand *wifi_unregister_cmd(wifi_handle handle, int id); +WifiCommand *wifi_get_cmd(wifi_handle handle, int id); +void wifi_unregister_cmd(wifi_handle handle, WifiCommand *cmd); + +interface_info *getIfaceInfo(wifi_interface_handle); +wifi_handle getWifiHandle(wifi_interface_handle handle); +hal_info *getHalInfo(wifi_handle handle); +hal_info *getHalInfo(wifi_interface_handle handle); +wifi_handle getWifiHandle(hal_info *info); +wifi_interface_handle getIfaceHandle(interface_info *info); +wifi_error wifi_cancel_cmd(wifi_request_id id, wifi_interface_handle iface); +wifi_error wifi_get_cancel_cmd(wifi_request_id id, wifi_interface_handle iface); +wifi_error nan_deinit_handler(); +wifi_error wifi_start_hal(wifi_interface_handle iface); +wifi_error wifi_stop_hal(wifi_interface_handle iface); +wifi_interface_handle wifi_get_wlan_interface(wifi_handle info, + wifi_interface_handle *ifaceHandles, int numIfaceHandles); +#ifdef RING_DUMP +wifi_error wifi_start_ring_dump(wifi_interface_handle iface, + wifi_ring_buffer_data_handler ring_handle); +wifi_error wifi_stop_ring_dump(wifi_interface_handle iface); +#endif /* RING_DUMP */ +wifi_error wifi_hal_ota_update(wifi_interface_handle iface, uint32_t ota_version); +wifi_error wifi_hal_preInit(wifi_interface_handle iface); +/* API to get wake reason statistics */ +wifi_error wifi_get_wake_reason_stats(wifi_interface_handle handle, + WLAN_DRIVER_WAKE_REASON_CNT *wifi_wake_reason_cnt); +wifi_error wifi_virtual_interface_create(wifi_handle handle, const char* ifname, + wifi_interface_type iface_type); +wifi_error wifi_virtual_interface_delete(wifi_handle handle, const char* ifname); +wifi_error wifi_set_coex_unsafe_channels(wifi_handle handle, u32 num_channels, + wifi_coex_unsafe_channel channels[], u32 restrictions); +wifi_error wifi_set_voip_mode(wifi_interface_handle handle, wifi_voip_mode mode); +wifi_error wifi_set_dtim_config(wifi_interface_handle handle, u32 multiplier); +void set_hautil_mode(bool halutil_mode); +bool get_halutil_mode(); + +/* API's to support TWT */ + +/**@brief twt_get_capability + * Request TWT capability + * @param wifi_interface_handle: + * @return Synchronous wifi_error and TwtCapabilitySet + */ +wifi_error twt_get_capability(wifi_interface_handle iface, TwtCapabilitySet* twt_cap_set); + +/**@brief twt_register_handler + * Request to register TWT callback + * @param wifi_interface_handle: + * @param TwtCallbackHandler: + * @return Synchronous wifi_error + */ +wifi_error twt_register_handler(wifi_interface_handle iface, TwtCallbackHandler handler); + +/**@brief twt_setup_request + * Request to send TWT setup frame + * @param wifi_interface_handle: + * @param TwtSetupRequest: + * @return Synchronous wifi_error + * @return Asynchronous EventTwtSetupResponse CB return TwtSetupResponse + */ +wifi_error twt_setup_request(wifi_interface_handle iface, TwtSetupRequest* msg); + +/**@brief twt_teardown_request + * Request to send TWT teardown frame + * @param wifi_interface_handle: + * @param TwtTeardownRequest: + * @return Synchronous wifi_error + * @return Asynchronous EventTwtTeardownCompletion CB return TwtTeardownCompletion + * TwtTeardownCompletion may also be received due to other events + * like CSA, BTCX, TWT scheduler, MultiConnection, peer-initiated teardown, etc. + */ +wifi_error twt_teardown_request(wifi_interface_handle iface, TwtTeardownRequest* msg); + +/**@brief twt_info_frame_request + * Request to send TWT info frame + * @param wifi_interface_handle: + * @param TwtInfoFrameRequest: + * @return Synchronous wifi_error + * @return Asynchronous EventTwtInfoFrameReceived CB return TwtInfoFrameReceived + * Driver may also receive Peer-initiated TwtInfoFrame + */ +wifi_error twt_info_frame_request(wifi_interface_handle iface, TwtInfoFrameRequest* msg); + +/**@brief twt_get_stats + * Request to get TWT stats + * @param wifi_interface_handle: + * @param config_id: + * @return Synchronous wifi_error and TwtStats + */ +wifi_error twt_get_stats(wifi_interface_handle iface, u8 config_id, TwtStats* stats); + +/**@brief twt_clear_stats + * Request to clear TWT stats + * @param wifi_interface_handle: + * @param config_id: + * @return Synchronous wifi_error + */ +wifi_error twt_clear_stats(wifi_interface_handle iface, u8 config_id); + +wifi_error wifi_trigger_subsystem_restart(wifi_handle handle); +// some common macros + +#define min(x, y) ((x) < (y) ? (x) : (y)) +#define max(x, y) ((x) > (y) ? (x) : (y)) + +#define NULL_CHECK_RETURN(ptr, str, ret) \ + do { \ + if (!(ptr)) { \ + ALOGE("%s(): null pointer - #ptr (%s)\n", __FUNCTION__, str); \ + return ret; \ + } \ + } while (0) + +#endif + diff --git a/synadhd/wifi_hal/cpp_bindings.cpp b/synadhd/wifi_hal/cpp_bindings.cpp new file mode 100755 index 0000000..5311e6e --- /dev/null +++ b/synadhd/wifi_hal/cpp_bindings.cpp @@ -0,0 +1,792 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * 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 <stdint.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/errqueue.h> + +#include <linux/pkt_sched.h> +#include <netlink/object-api.h> +#include <netlink/netlink.h> +#include <netlink/socket.h> +#include <netlink/handlers.h> + +#include <ctype.h> +#include <errno.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +pthread_mutex_t ResponseMutex; +void InitResponseLock() { + pthread_mutex_init(&ResponseMutex, NULL); +} + +void DestroyResponseLock() +{ + pthread_mutex_destroy(&ResponseMutex); +} + +void appendFmt(char *buf, int &offset, const char *fmt, ...) +{ + va_list params; + va_start(params, fmt); + offset += vsprintf(buf + offset, fmt, params); + va_end(params); +} + +#define C2S(x) case x: return #x; + +static const char *cmdToString(int cmd) +{ + switch (cmd) { + C2S(NL80211_CMD_UNSPEC) + C2S(NL80211_CMD_GET_WIPHY) + C2S(NL80211_CMD_SET_WIPHY) + C2S(NL80211_CMD_NEW_WIPHY) + C2S(NL80211_CMD_DEL_WIPHY) + C2S(NL80211_CMD_GET_INTERFACE) + C2S(NL80211_CMD_SET_INTERFACE) + C2S(NL80211_CMD_NEW_INTERFACE) + C2S(NL80211_CMD_DEL_INTERFACE) + C2S(NL80211_CMD_GET_KEY) + C2S(NL80211_CMD_SET_KEY) + C2S(NL80211_CMD_NEW_KEY) + C2S(NL80211_CMD_DEL_KEY) + C2S(NL80211_CMD_GET_BEACON) + C2S(NL80211_CMD_SET_BEACON) + C2S(NL80211_CMD_START_AP) + C2S(NL80211_CMD_STOP_AP) + C2S(NL80211_CMD_GET_STATION) + C2S(NL80211_CMD_SET_STATION) + C2S(NL80211_CMD_NEW_STATION) + C2S(NL80211_CMD_DEL_STATION) + C2S(NL80211_CMD_GET_MPATH) + C2S(NL80211_CMD_SET_MPATH) + C2S(NL80211_CMD_NEW_MPATH) + C2S(NL80211_CMD_DEL_MPATH) + C2S(NL80211_CMD_SET_BSS) + C2S(NL80211_CMD_SET_REG) + C2S(NL80211_CMD_REQ_SET_REG) + C2S(NL80211_CMD_GET_MESH_CONFIG) + C2S(NL80211_CMD_SET_MESH_CONFIG) + C2S(NL80211_CMD_SET_MGMT_EXTRA_IE) + C2S(NL80211_CMD_GET_REG) + C2S(NL80211_CMD_GET_SCAN) + C2S(NL80211_CMD_TRIGGER_SCAN) + C2S(NL80211_CMD_NEW_SCAN_RESULTS) + C2S(NL80211_CMD_SCAN_ABORTED) + C2S(NL80211_CMD_REG_CHANGE) + C2S(NL80211_CMD_AUTHENTICATE) + C2S(NL80211_CMD_ASSOCIATE) + C2S(NL80211_CMD_DEAUTHENTICATE) + C2S(NL80211_CMD_DISASSOCIATE) + C2S(NL80211_CMD_MICHAEL_MIC_FAILURE) + C2S(NL80211_CMD_REG_BEACON_HINT) + C2S(NL80211_CMD_JOIN_IBSS) + C2S(NL80211_CMD_LEAVE_IBSS) + C2S(NL80211_CMD_TESTMODE) + C2S(NL80211_CMD_CONNECT) + C2S(NL80211_CMD_ROAM) + C2S(NL80211_CMD_DISCONNECT) + C2S(NL80211_CMD_SET_WIPHY_NETNS) + C2S(NL80211_CMD_GET_SURVEY) + C2S(NL80211_CMD_NEW_SURVEY_RESULTS) + C2S(NL80211_CMD_SET_PMKSA) + C2S(NL80211_CMD_DEL_PMKSA) + C2S(NL80211_CMD_FLUSH_PMKSA) + C2S(NL80211_CMD_REMAIN_ON_CHANNEL) + C2S(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL) + C2S(NL80211_CMD_SET_TX_BITRATE_MASK) + C2S(NL80211_CMD_REGISTER_FRAME) + C2S(NL80211_CMD_FRAME) + C2S(NL80211_CMD_FRAME_TX_STATUS) + C2S(NL80211_CMD_SET_POWER_SAVE) + C2S(NL80211_CMD_GET_POWER_SAVE) + C2S(NL80211_CMD_SET_CQM) + C2S(NL80211_CMD_NOTIFY_CQM) + C2S(NL80211_CMD_SET_CHANNEL) + C2S(NL80211_CMD_SET_WDS_PEER) + C2S(NL80211_CMD_FRAME_WAIT_CANCEL) + C2S(NL80211_CMD_JOIN_MESH) + C2S(NL80211_CMD_LEAVE_MESH) + C2S(NL80211_CMD_UNPROT_DEAUTHENTICATE) + C2S(NL80211_CMD_UNPROT_DISASSOCIATE) + C2S(NL80211_CMD_NEW_PEER_CANDIDATE) + C2S(NL80211_CMD_GET_WOWLAN) + C2S(NL80211_CMD_SET_WOWLAN) + C2S(NL80211_CMD_START_SCHED_SCAN) + C2S(NL80211_CMD_STOP_SCHED_SCAN) + C2S(NL80211_CMD_SCHED_SCAN_RESULTS) + C2S(NL80211_CMD_SCHED_SCAN_STOPPED) + C2S(NL80211_CMD_SET_REKEY_OFFLOAD) + C2S(NL80211_CMD_PMKSA_CANDIDATE) + C2S(NL80211_CMD_TDLS_OPER) + C2S(NL80211_CMD_TDLS_MGMT) + C2S(NL80211_CMD_UNEXPECTED_FRAME) + C2S(NL80211_CMD_PROBE_CLIENT) + C2S(NL80211_CMD_REGISTER_BEACONS) + C2S(NL80211_CMD_UNEXPECTED_4ADDR_FRAME) + C2S(NL80211_CMD_SET_NOACK_MAP) + C2S(NL80211_CMD_CH_SWITCH_NOTIFY) + C2S(NL80211_CMD_START_P2P_DEVICE) + C2S(NL80211_CMD_STOP_P2P_DEVICE) + C2S(NL80211_CMD_CONN_FAILED) + C2S(NL80211_CMD_SET_MCAST_RATE) + C2S(NL80211_CMD_SET_MAC_ACL) + C2S(NL80211_CMD_RADAR_DETECT) + C2S(NL80211_CMD_GET_PROTOCOL_FEATURES) + C2S(NL80211_CMD_UPDATE_FT_IES) + C2S(NL80211_CMD_FT_EVENT) + C2S(NL80211_CMD_CRIT_PROTOCOL_START) + C2S(NL80211_CMD_CRIT_PROTOCOL_STOP) + C2S(NL80211_CMD_GET_COALESCE) + C2S(NL80211_CMD_SET_COALESCE) + C2S(NL80211_CMD_CHANNEL_SWITCH) + C2S(NL80211_CMD_VENDOR) + C2S(NL80211_CMD_SET_QOS_MAP) + default: + return "NL80211_CMD_UNKNOWN"; + } +} + +const char *attributeToString(int attribute) +{ + switch (attribute) { + C2S(NL80211_ATTR_UNSPEC) + + C2S(NL80211_ATTR_WIPHY) + C2S(NL80211_ATTR_WIPHY_NAME) + + C2S(NL80211_ATTR_IFINDEX) + C2S(NL80211_ATTR_IFNAME) + C2S(NL80211_ATTR_IFTYPE) + + C2S(NL80211_ATTR_MAC) + + C2S(NL80211_ATTR_KEY_DATA) + C2S(NL80211_ATTR_KEY_IDX) + C2S(NL80211_ATTR_KEY_CIPHER) + C2S(NL80211_ATTR_KEY_SEQ) + C2S(NL80211_ATTR_KEY_DEFAULT) + + C2S(NL80211_ATTR_BEACON_INTERVAL) + C2S(NL80211_ATTR_DTIM_PERIOD) + C2S(NL80211_ATTR_BEACON_HEAD) + C2S(NL80211_ATTR_BEACON_TAIL) + + C2S(NL80211_ATTR_STA_AID) + C2S(NL80211_ATTR_STA_FLAGS) + C2S(NL80211_ATTR_STA_LISTEN_INTERVAL) + C2S(NL80211_ATTR_STA_SUPPORTED_RATES) + C2S(NL80211_ATTR_STA_VLAN) + C2S(NL80211_ATTR_STA_INFO) + + C2S(NL80211_ATTR_WIPHY_BANDS) + + C2S(NL80211_ATTR_MNTR_FLAGS) + + C2S(NL80211_ATTR_MESH_ID) + C2S(NL80211_ATTR_STA_PLINK_ACTION) + C2S(NL80211_ATTR_MPATH_NEXT_HOP) + C2S(NL80211_ATTR_MPATH_INFO) + + C2S(NL80211_ATTR_BSS_CTS_PROT) + C2S(NL80211_ATTR_BSS_SHORT_PREAMBLE) + C2S(NL80211_ATTR_BSS_SHORT_SLOT_TIME) + + C2S(NL80211_ATTR_HT_CAPABILITY) + + C2S(NL80211_ATTR_SUPPORTED_IFTYPES) + + C2S(NL80211_ATTR_REG_ALPHA2) + C2S(NL80211_ATTR_REG_RULES) + + C2S(NL80211_ATTR_MESH_CONFIG) + + C2S(NL80211_ATTR_BSS_BASIC_RATES) + + C2S(NL80211_ATTR_WIPHY_TXQ_PARAMS) + C2S(NL80211_ATTR_WIPHY_FREQ) + C2S(NL80211_ATTR_WIPHY_CHANNEL_TYPE) + + C2S(NL80211_ATTR_KEY_DEFAULT_MGMT) + + C2S(NL80211_ATTR_MGMT_SUBTYPE) + C2S(NL80211_ATTR_IE) + + C2S(NL80211_ATTR_MAX_NUM_SCAN_SSIDS) + + C2S(NL80211_ATTR_SCAN_FREQUENCIES) + C2S(NL80211_ATTR_SCAN_SSIDS) + C2S(NL80211_ATTR_GENERATION) /* replaces old SCAN_GENERATION */ + C2S(NL80211_ATTR_BSS) + + C2S(NL80211_ATTR_REG_INITIATOR) + C2S(NL80211_ATTR_REG_TYPE) + + C2S(NL80211_ATTR_SUPPORTED_COMMANDS) + + C2S(NL80211_ATTR_FRAME) + C2S(NL80211_ATTR_SSID) + C2S(NL80211_ATTR_AUTH_TYPE) + C2S(NL80211_ATTR_REASON_CODE) + + C2S(NL80211_ATTR_KEY_TYPE) + + C2S(NL80211_ATTR_MAX_SCAN_IE_LEN) + C2S(NL80211_ATTR_CIPHER_SUITES) + + C2S(NL80211_ATTR_FREQ_BEFORE) + C2S(NL80211_ATTR_FREQ_AFTER) + + C2S(NL80211_ATTR_FREQ_FIXED) + + + C2S(NL80211_ATTR_WIPHY_RETRY_SHORT) + C2S(NL80211_ATTR_WIPHY_RETRY_LONG) + C2S(NL80211_ATTR_WIPHY_FRAG_THRESHOLD) + C2S(NL80211_ATTR_WIPHY_RTS_THRESHOLD) + + C2S(NL80211_ATTR_TIMED_OUT) + + C2S(NL80211_ATTR_USE_MFP) + + C2S(NL80211_ATTR_STA_FLAGS2) + + C2S(NL80211_ATTR_CONTROL_PORT) + + C2S(NL80211_ATTR_TESTDATA) + + C2S(NL80211_ATTR_PRIVACY) + + C2S(NL80211_ATTR_DISCONNECTED_BY_AP) + C2S(NL80211_ATTR_STATUS_CODE) + + C2S(NL80211_ATTR_CIPHER_SUITES_PAIRWISE) + C2S(NL80211_ATTR_CIPHER_SUITE_GROUP) + C2S(NL80211_ATTR_WPA_VERSIONS) + C2S(NL80211_ATTR_AKM_SUITES) + + C2S(NL80211_ATTR_REQ_IE) + C2S(NL80211_ATTR_RESP_IE) + + C2S(NL80211_ATTR_PREV_BSSID) + + C2S(NL80211_ATTR_KEY) + C2S(NL80211_ATTR_KEYS) + + C2S(NL80211_ATTR_PID) + + C2S(NL80211_ATTR_4ADDR) + + C2S(NL80211_ATTR_SURVEY_INFO) + + C2S(NL80211_ATTR_PMKID) + C2S(NL80211_ATTR_MAX_NUM_PMKIDS) + + C2S(NL80211_ATTR_DURATION) + + C2S(NL80211_ATTR_COOKIE) + + C2S(NL80211_ATTR_WIPHY_COVERAGE_CLASS) + + C2S(NL80211_ATTR_TX_RATES) + + C2S(NL80211_ATTR_FRAME_MATCH) + + C2S(NL80211_ATTR_ACK) + + C2S(NL80211_ATTR_PS_STATE) + + C2S(NL80211_ATTR_CQM) + + C2S(NL80211_ATTR_LOCAL_STATE_CHANGE) + + C2S(NL80211_ATTR_AP_ISOLATE) + + C2S(NL80211_ATTR_WIPHY_TX_POWER_SETTING) + C2S(NL80211_ATTR_WIPHY_TX_POWER_LEVEL) + + C2S(NL80211_ATTR_TX_FRAME_TYPES) + C2S(NL80211_ATTR_RX_FRAME_TYPES) + C2S(NL80211_ATTR_FRAME_TYPE) + + C2S(NL80211_ATTR_CONTROL_PORT_ETHERTYPE) + C2S(NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT) + + C2S(NL80211_ATTR_SUPPORT_IBSS_RSN) + + C2S(NL80211_ATTR_WIPHY_ANTENNA_TX) + C2S(NL80211_ATTR_WIPHY_ANTENNA_RX) + + C2S(NL80211_ATTR_MCAST_RATE) + + C2S(NL80211_ATTR_OFFCHANNEL_TX_OK) + + C2S(NL80211_ATTR_BSS_HT_OPMODE) + + C2S(NL80211_ATTR_KEY_DEFAULT_TYPES) + + C2S(NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION) + + C2S(NL80211_ATTR_MESH_SETUP) + + C2S(NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX) + C2S(NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX) + + C2S(NL80211_ATTR_SUPPORT_MESH_AUTH) + C2S(NL80211_ATTR_STA_PLINK_STATE) + + C2S(NL80211_ATTR_WOWLAN_TRIGGERS) + C2S(NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED) + + C2S(NL80211_ATTR_SCHED_SCAN_INTERVAL) + + C2S(NL80211_ATTR_INTERFACE_COMBINATIONS) + C2S(NL80211_ATTR_SOFTWARE_IFTYPES) + + C2S(NL80211_ATTR_REKEY_DATA) + + C2S(NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS) + C2S(NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN) + + C2S(NL80211_ATTR_SCAN_SUPP_RATES) + + C2S(NL80211_ATTR_HIDDEN_SSID) + + C2S(NL80211_ATTR_IE_PROBE_RESP) + C2S(NL80211_ATTR_IE_ASSOC_RESP) + + C2S(NL80211_ATTR_STA_WME) + C2S(NL80211_ATTR_SUPPORT_AP_UAPSD) + + C2S(NL80211_ATTR_ROAM_SUPPORT) + + C2S(NL80211_ATTR_SCHED_SCAN_MATCH) + C2S(NL80211_ATTR_MAX_MATCH_SETS) + + C2S(NL80211_ATTR_PMKSA_CANDIDATE) + + C2S(NL80211_ATTR_TX_NO_CCK_RATE) + + C2S(NL80211_ATTR_TDLS_ACTION) + C2S(NL80211_ATTR_TDLS_DIALOG_TOKEN) + C2S(NL80211_ATTR_TDLS_OPERATION) + C2S(NL80211_ATTR_TDLS_SUPPORT) + C2S(NL80211_ATTR_TDLS_EXTERNAL_SETUP) + + C2S(NL80211_ATTR_DEVICE_AP_SME) + + C2S(NL80211_ATTR_DONT_WAIT_FOR_ACK) + + C2S(NL80211_ATTR_FEATURE_FLAGS) + + C2S(NL80211_ATTR_PROBE_RESP_OFFLOAD) + + C2S(NL80211_ATTR_PROBE_RESP) + + C2S(NL80211_ATTR_DFS_REGION) + + C2S(NL80211_ATTR_DISABLE_HT) + C2S(NL80211_ATTR_HT_CAPABILITY_MASK) + + C2S(NL80211_ATTR_NOACK_MAP) + + C2S(NL80211_ATTR_INACTIVITY_TIMEOUT) + + C2S(NL80211_ATTR_RX_SIGNAL_DBM) + + C2S(NL80211_ATTR_BG_SCAN_PERIOD) + + C2S(NL80211_ATTR_WDEV) + + C2S(NL80211_ATTR_USER_REG_HINT_TYPE) + + C2S(NL80211_ATTR_CONN_FAILED_REASON) + + C2S(NL80211_ATTR_SAE_DATA) + + C2S(NL80211_ATTR_VHT_CAPABILITY) + + C2S(NL80211_ATTR_SCAN_FLAGS) + + C2S(NL80211_ATTR_CHANNEL_WIDTH) + C2S(NL80211_ATTR_CENTER_FREQ1) + C2S(NL80211_ATTR_CENTER_FREQ2) + + C2S(NL80211_ATTR_P2P_CTWINDOW) + C2S(NL80211_ATTR_P2P_OPPPS) + + C2S(NL80211_ATTR_LOCAL_MESH_POWER_MODE) + + C2S(NL80211_ATTR_ACL_POLICY) + + C2S(NL80211_ATTR_MAC_ADDRS) + + C2S(NL80211_ATTR_MAC_ACL_MAX) + + C2S(NL80211_ATTR_RADAR_EVENT) + + C2S(NL80211_ATTR_EXT_CAPA) + C2S(NL80211_ATTR_EXT_CAPA_MASK) + + C2S(NL80211_ATTR_STA_CAPABILITY) + C2S(NL80211_ATTR_STA_EXT_CAPABILITY) + + C2S(NL80211_ATTR_PROTOCOL_FEATURES) + C2S(NL80211_ATTR_SPLIT_WIPHY_DUMP) + + C2S(NL80211_ATTR_DISABLE_VHT) + C2S(NL80211_ATTR_VHT_CAPABILITY_MASK) + + C2S(NL80211_ATTR_MDID) + C2S(NL80211_ATTR_IE_RIC) + + C2S(NL80211_ATTR_CRIT_PROT_ID) + C2S(NL80211_ATTR_MAX_CRIT_PROT_DURATION) + + C2S(NL80211_ATTR_PEER_AID) + + C2S(NL80211_ATTR_COALESCE_RULE) + + C2S(NL80211_ATTR_CH_SWITCH_COUNT) + C2S(NL80211_ATTR_CH_SWITCH_BLOCK_TX) + C2S(NL80211_ATTR_CSA_IES) + C2S(NL80211_ATTR_CSA_C_OFF_BEACON) + C2S(NL80211_ATTR_CSA_C_OFF_PRESP) + + C2S(NL80211_ATTR_RXMGMT_FLAGS) + + C2S(NL80211_ATTR_STA_SUPPORTED_CHANNELS) + + C2S(NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES) + + C2S(NL80211_ATTR_HANDLE_DFS) + + C2S(NL80211_ATTR_SUPPORT_5_MHZ) + C2S(NL80211_ATTR_SUPPORT_10_MHZ) + + C2S(NL80211_ATTR_OPMODE_NOTIF) + + C2S(NL80211_ATTR_VENDOR_ID) + C2S(NL80211_ATTR_VENDOR_SUBCMD) + C2S(NL80211_ATTR_VENDOR_DATA) + C2S(NL80211_ATTR_VENDOR_EVENTS) + + C2S(NL80211_ATTR_QOS_MAP) + default: + return "NL80211_ATTR_UNKNOWN"; + } +} + +void WifiEvent::log() { + parse(); + + byte *data = (byte *)genlmsg_attrdata(mHeader, 0); + int len = genlmsg_attrlen(mHeader, 0); + ALOGD("cmd = %s, len = %d", get_cmdString(), len); + ALOGD("vendor_id = %04x, vendor_subcmd = %d", get_vendor_id(), get_vendor_subcmd()); + + for (int i = 0; i < len; i += 16) { + char line[81]; + int linelen = min(16, len - i); + int offset = 0; + appendFmt(line, offset, "%02x", data[i]); + for (int j = 1; j < linelen; j++) { + appendFmt(line, offset, " %02x", data[i+j]); + } + + for (int j = linelen; j < 16; j++) { + appendFmt(line, offset, " "); + } + + line[23] = '-'; + + appendFmt(line, offset, " "); + + for (int j = 0; j < linelen; j++) { + if (isprint(data[i+j])) { + appendFmt(line, offset, "%c", data[i+j]); + } else { + appendFmt(line, offset, "-"); + } + } + + ALOGD("%s", line); + } + + for (unsigned i = 0; i < NL80211_ATTR_MAX_INTERNAL; i++) { + if (mAttributes[i] != NULL) { + ALOGD("found attribute %s", attributeToString(i)); + } + } + + ALOGD("-- End of message --"); +} + +const char *WifiEvent::get_cmdString() { + return cmdToString(get_cmd()); +} + + +int WifiEvent::parse() { + if (mHeader != NULL) { + return WIFI_SUCCESS; + } + mHeader = (genlmsghdr *)nlmsg_data(nlmsg_hdr(mMsg)); + int result = nla_parse(mAttributes, NL80211_ATTR_MAX_INTERNAL, genlmsg_attrdata(mHeader, 0), + genlmsg_attrlen(mHeader, 0), NULL); + + // ALOGD("event len = %d", nlmsg_hdr(mMsg)->nlmsg_len); + return result; +} + +int WifiRequest::create(int family, uint8_t cmd, int flags, int hdrlen) { + + destroy(); + + mMsg = nlmsg_alloc(); + if (mMsg != NULL) { + genlmsg_put(mMsg, /* pid = */ 0, /* seq = */ 0, family, + hdrlen, flags, cmd, /* version = */ 0); + return WIFI_SUCCESS; + } else { + return WIFI_ERROR_OUT_OF_MEMORY; + } +} + +static int mapErrorCodes(int err) +{ + int ret; + if (!err) { + return WIFI_SUCCESS; + } + switch (err) { + case -EOPNOTSUPP: + ret = WIFI_ERROR_NOT_SUPPORTED; + break; + case -ETIMEDOUT: + ret = WIFI_ERROR_TIMED_OUT; + break; + case -EINVAL: + ret = WIFI_ERROR_INVALID_ARGS; + break; + case -ENOMEM: + ret = WIFI_ERROR_OUT_OF_MEMORY; + break; + case -EBUSY: + ret = WIFI_ERROR_BUSY; + break; + default: + ret = WIFI_ERROR_UNKNOWN; + } + ALOGD("error code %d mapped to %d", err, ret); + return ret; +} + +int WifiRequest::create(uint32_t id, int subcmd) { + int res = create(NL80211_CMD_VENDOR); + if (res < 0) { + return mapErrorCodes(res); + } + + res = put_u32(NL80211_ATTR_VENDOR_ID, id); + if (res < 0) { + return mapErrorCodes(res); + } + + res = put_u32(NL80211_ATTR_VENDOR_SUBCMD, subcmd); + if (res < 0) { + return mapErrorCodes(res); + } + + if (mIface != -1) { + res = set_iface_id(mIface); + } + + return mapErrorCodes(res); +} + + +static int no_seq_check(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + +int WifiCommand::requestResponse() { + int err = create(); /* create the message */ + if (err < 0) { + return err; + } + + return requestResponse(mMsg); +} + +int WifiCommand::requestResponse(WifiRequest& request) { + pthread_mutex_lock(&ResponseMutex); + int err = 0; + + struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!cb) + goto out; + + err = nl_send_auto_complete(mInfo->cmd_sock, request.getMessage()); /* send message */ + if (err < 0) + goto out; + + err = 1; + + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, response_handler, this); + + while (err > 0) { /* wait for reply */ + int res = nl_recvmsgs(mInfo->cmd_sock, cb); + if (res) { + ALOGE("nl80211: %s->nl_recvmsgs failed: %d", __func__, res); + } + } +out: + nl_cb_put(cb); + pthread_mutex_unlock(&ResponseMutex); + return mapErrorCodes(err); +} + +int WifiCommand::requestEvent(int cmd) { + + ALOGD("requesting event %d", cmd); + + int res = wifi_register_handler(wifiHandle(), cmd, event_handler, this); + if (res < 0) { + return res; + } + + res = create(); /* create the message */ + if (res < 0) + goto out; + + ALOGD("waiting for response %d", cmd); + + res = nl_send_auto_complete(mInfo->cmd_sock, mMsg.getMessage()); /* send message */ + if (res < 0) + goto out; + + ALOGD("waiting for event %d", cmd); + res = mCondition.wait(); + if (res < 0) + goto out; + +out: + wifi_unregister_handler(wifiHandle(), cmd); + return mapErrorCodes(res); +} + +int WifiCommand::requestVendorEvent(uint32_t id, int subcmd) { + + int res = wifi_register_vendor_handler(wifiHandle(), id, subcmd, event_handler, this); + if (res < 0) { + return res; + } + + res = create(); /* create the message */ + if (res < 0) + goto out; + + res = nl_send_auto_complete(mInfo->cmd_sock, mMsg.getMessage()); /* send message */ + if (res < 0) + goto out; + + res = mCondition.wait(); + if (res < 0) + goto out; + +out: + wifi_unregister_vendor_handler(wifiHandle(), id, subcmd); + return mapErrorCodes(res); +} + +/* Event handlers */ +int WifiCommand::response_handler(struct nl_msg *msg, void *arg) { + // ALOGD("response_handler called"); + WifiCommand *cmd = (WifiCommand *)arg; + WifiEvent reply(msg); + int res = reply.parse(); + if (res < 0) { + ALOGE("Failed to parse reply message = %d", res); + return NL_SKIP; + } else { + // reply.log(); + return cmd->handleResponse(reply); + } +} + +int WifiCommand::event_handler(struct nl_msg *msg, void *arg) { + WifiCommand *cmd = (WifiCommand *)arg; + WifiEvent event(msg); + int res = event.parse(); + if (res < 0) { + ALOGE("Failed to parse event = %d", res); + res = NL_SKIP; + } else { + res = cmd->handleEvent(event); + } + + cmd->mCondition.signal(); + return res; +} + +/* Other event handlers */ +int WifiCommand::valid_handler(struct nl_msg *msg, void *arg) { + // ALOGD("valid_handler called"); + int *err = (int *)arg; + *err = 0; + return NL_SKIP; +} + +int WifiCommand::ack_handler(struct nl_msg *msg, void *arg) { + // ALOGD("ack_handler called"); + int *err = (int *)arg; + *err = 0; + return NL_STOP; +} + +int WifiCommand::finish_handler(struct nl_msg *msg, void *arg) { + // ALOGD("finish_handler called"); + int *ret = (int *)arg; + *ret = 0; + return NL_SKIP; +} + +int WifiCommand::error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { + int *ret = (int *)arg; + *ret = err->error; + + // ALOGD("error_handler received : %d", err->error); + return NL_SKIP; +} diff --git a/synadhd/wifi_hal/cpp_bindings.h b/synadhd/wifi_hal/cpp_bindings.h new file mode 100755 index 0000000..8b5600a --- /dev/null +++ b/synadhd/wifi_hal/cpp_bindings.h @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * 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 "wifi_hal.h" +#include "common.h" +#include "sync.h" + +class WifiEvent +{ + /* TODO: remove this when nl headers are updated */ + static const unsigned NL80211_ATTR_MAX_INTERNAL = 256; +private: + struct nl_msg *mMsg; + struct genlmsghdr *mHeader; + struct nlattr *mAttributes[NL80211_ATTR_MAX_INTERNAL + 1]; + +public: + WifiEvent(nl_msg *msg) { + mMsg = msg; + mHeader = NULL; + memset(mAttributes, 0, sizeof(mAttributes)); + } + ~WifiEvent() { + /* don't destroy mMsg; it doesn't belong to us */ + } + + void log(); + + int parse(); + + genlmsghdr *header() { + return mHeader; + } + + int get_cmd() { + return mHeader->cmd; + } + + int get_vendor_id() { + return get_u32(NL80211_ATTR_VENDOR_ID); + } + + int get_vendor_subcmd() { + return get_u32(NL80211_ATTR_VENDOR_SUBCMD); + } + + void *get_vendor_data() { + return get_data(NL80211_ATTR_VENDOR_DATA); + } + + int get_vendor_data_len() { + return get_len(NL80211_ATTR_VENDOR_DATA); + } + + const char *get_cmdString(); + + nlattr ** attributes() { + return mAttributes; + } + + nlattr *get_attribute(int attribute) { + return mAttributes[attribute]; + } + + uint8_t get_u8(int attribute) { + return mAttributes[attribute] ? nla_get_u8(mAttributes[attribute]) : 0; + } + + uint16_t get_u16(int attribute) { + return mAttributes[attribute] ? nla_get_u16(mAttributes[attribute]) : 0; + } + + uint32_t get_u32(int attribute) { + return mAttributes[attribute] ? nla_get_u32(mAttributes[attribute]) : 0; + } + + uint64_t get_u64(int attribute) { + return mAttributes[attribute] ? nla_get_u64(mAttributes[attribute]) : 0; + } + + int get_len(int attribute) { + return mAttributes[attribute] ? nla_len(mAttributes[attribute]) : 0; + } + + void *get_data(int attribute) { + return mAttributes[attribute] ? nla_data(mAttributes[attribute]) : NULL; + } + + void *get_string(int attribute) { + return mAttributes[attribute] ? nla_get_string(mAttributes[attribute]) : NULL; + } +private: + WifiEvent(const WifiEvent&); // hide copy constructor to prevent copies +}; + +class nl_iterator { + struct nlattr *pos; + int rem; +public: + nl_iterator(struct nlattr *attr) { + pos = (struct nlattr *)nla_data(attr); + rem = nla_len(attr); + } + bool has_next() { + return nla_ok(pos, rem); + } + void next() { + pos = (struct nlattr *)nla_next(pos, &(rem)); + } + struct nlattr *get() { + return pos; + } + uint16_t get_type() { + return nla_type(pos); + } + uint8_t get_u8() { + return nla_get_u8(pos); + } + uint16_t get_u16() { + return nla_get_u16(pos); + } + uint32_t get_u32() { + return nla_get_u32(pos); + } + uint64_t get_u64() { + return nla_get_u64(pos); + } + void* get_data() { + return nla_data(pos); + } + int get_len() { + return nla_len(pos); + } + void* get_string() { + return nla_get_string(pos); + } +private: + nl_iterator(const nl_iterator&); // hide copy constructor to prevent copies +}; + +class WifiRequest +{ +private: + int mFamily; + int mIface; + struct nl_msg *mMsg; + +public: + WifiRequest(int family) { + mMsg = NULL; + mFamily = family; + mIface = -1; + } + + WifiRequest(int family, int iface) { + mMsg = NULL; + mFamily = family; + mIface = iface; + } + + ~WifiRequest() { + destroy(); + } + + void destroy() { + if (mMsg) { + nlmsg_free(mMsg); + mMsg = NULL; + } + } + + nl_msg *getMessage() { + return mMsg; + } + + /* Command assembly helpers */ + int create(int family, uint8_t cmd, int flags, int hdrlen); + int create(uint8_t cmd) { + return create(mFamily, cmd, 0, 0); + } + + int create(uint32_t id, int subcmd); + + int put(int attribute, void *ptr, unsigned len) { + return nla_put(mMsg, attribute, len, ptr); + } + int put_s8(int attribute, int8_t value) { + return nla_put(mMsg, attribute, sizeof(value), &value); + } + int put_u8(int attribute, uint8_t value) { + return nla_put(mMsg, attribute, sizeof(value), &value); + } + int put_u16(int attribute, uint16_t value) { + return nla_put(mMsg, attribute, sizeof(value), &value); + } + int put_u32(int attribute, uint32_t value) { + return nla_put(mMsg, attribute, sizeof(value), &value); + } + int put_u64(int attribute, uint64_t value) { + return nla_put(mMsg, attribute, sizeof(value), &value); + } + int put_string(int attribute, const char *value) { + return nla_put(mMsg, attribute, strlen(value) + 1, value); + } + int put_addr(int attribute, mac_addr value) { + return nla_put(mMsg, attribute, sizeof(mac_addr), value); + } + + struct nlattr * attr_start(int attribute) { + return nla_nest_start(mMsg, attribute); + } + void attr_end(struct nlattr *attr) { + nla_nest_end(mMsg, attr); + } + + int set_iface_id(int ifindex) { + return put_u32(NL80211_ATTR_IFINDEX, ifindex); + } +private: + WifiRequest(const WifiRequest&); // hide copy constructor to prevent copies + +}; + +class WifiCommand +{ +protected: + const char *mType; + hal_info *mInfo; + WifiRequest mMsg; + Condition mCondition; + wifi_request_id mId; + interface_info *mIfaceInfo; + int mRefs; +public: + WifiCommand(const char *type, wifi_handle handle, wifi_request_id id) + : mType(type), mMsg(getHalInfo(handle)->nl80211_family_id), mId(id), mRefs(1) + { + mIfaceInfo = NULL; + mInfo = getHalInfo(handle); + // ALOGD("WifiCommand %p created, mInfo = %p, mIfaceInfo = %p", this, mInfo, mIfaceInfo); + } + + WifiCommand(const char *type, wifi_interface_handle iface, wifi_request_id id) + : mType(type), mMsg(getHalInfo(iface)->nl80211_family_id, getIfaceInfo(iface)->id), + mId(id), mRefs(1) + { + mIfaceInfo = getIfaceInfo(iface); + mInfo = getHalInfo(iface); + // ALOGD("WifiCommand %p created, mInfo = %p, mIfaceInfo = %p", this, mInfo, mIfaceInfo); + } + + virtual ~WifiCommand() { + // ALOGD("WifiCommand %p destroyed", this); + } + + wifi_request_id id() { + return mId; + } + + const char *getType() { + return mType; + } + + virtual void addRef() { + int refs = __sync_add_and_fetch(&mRefs, 1); + ALOGV("addRef: WifiCommand %p has %d references", this, refs); + } + + virtual void releaseRef() { + int refs = __sync_sub_and_fetch(&mRefs, 1); + if (refs == 0) { + delete this; + } else { + // ALOGD("releaseRef: WifiCommand %p has %d references", this, refs); + } + } + + virtual int create() { + /* by default there is no way to cancel */ + ALOGD("WifiCommand %p can't be created", this); + return WIFI_ERROR_NOT_SUPPORTED; + } + + virtual int cancel() { + /* by default there is no way to cancel */ + return WIFI_ERROR_NOT_SUPPORTED; + } + + int requestResponse(); + int requestEvent(int cmd); + int requestVendorEvent(uint32_t id, int subcmd); + int requestResponse(WifiRequest& request); + +protected: + wifi_handle wifiHandle() { + return getWifiHandle(mInfo); + } + + wifi_interface_handle ifaceHandle() { + return getIfaceHandle(mIfaceInfo); + } + + int familyId() { + return mInfo->nl80211_family_id; + } + + int ifaceId() { + return mIfaceInfo->id; + } + + /* Override this method to parse reply and dig out data; save it in the object */ + virtual int handleResponse(WifiEvent& reply) { + ALOGI("skipping a response"); + return NL_SKIP; + } + + /* Override this method to parse event and dig out data; save it in the object */ + virtual int handleEvent(WifiEvent& event) { + ALOGI("skipping an event"); + return NL_SKIP; + } + + int registerHandler(int cmd) { + return wifi_register_handler(wifiHandle(), cmd, &event_handler, this); + } + + void unregisterHandler(int cmd) { + wifi_unregister_handler(wifiHandle(), cmd); + } + + int registerVendorHandler(uint32_t id, int subcmd) { + return wifi_register_vendor_handler(wifiHandle(), id, subcmd, &event_handler, this); + } + + void unregisterVendorHandlerWithoutLock(uint32_t id, int subcmd) { + wifi_unregister_vendor_handler_without_lock(wifiHandle(), id, subcmd); + } + + void unregisterVendorHandler(uint32_t id, int subcmd) { + wifi_unregister_vendor_handler(wifiHandle(), id, subcmd); + } + +private: + WifiCommand(const WifiCommand& ); // hide copy constructor to prevent copies + + /* Event handling */ + static int response_handler(struct nl_msg *msg, void *arg); + + static int event_handler(struct nl_msg *msg, void *arg); + + /* Other event handlers */ + static int valid_handler(struct nl_msg *msg, void *arg); + + static int ack_handler(struct nl_msg *msg, void *arg); + + static int finish_handler(struct nl_msg *msg, void *arg); + + static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg); +}; + +/* nl message processing macros (required to pass C++ type checks) */ + +#define for_each_attr(pos, nla, rem) \ + for (pos = (nlattr *)nla_data(nla), rem = nla_len(nla); \ + nla_ok(pos, rem); \ + pos = (nlattr *)nla_next(pos, &(rem))) + +extern void InitResponseLock(); +extern void DestroyResponseLock(); diff --git a/synadhd/wifi_hal/gscan.cpp b/synadhd/wifi_hal/gscan.cpp new file mode 100755 index 0000000..16772ff --- /dev/null +++ b/synadhd/wifi_hal/gscan.cpp @@ -0,0 +1,2010 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * 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 <stdint.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/errqueue.h> + +#include <linux/pkt_sched.h> +#include <netlink/object-api.h> +#include <netlink/netlink.h> +#include <netlink/socket.h> +#include <netlink/handlers.h> + +#include "sync.h" + +#define LOG_TAG "WifiHAL" +//#define LOG_NDEBUG 0 //uncomment to enable verbose logging + +#include <log/log.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +typedef enum { + + GSCAN_ATTRIBUTE_NUM_BUCKETS = 10, + GSCAN_ATTRIBUTE_BASE_PERIOD, + GSCAN_ATTRIBUTE_BUCKETS_BAND, + GSCAN_ATTRIBUTE_BUCKET_ID, + GSCAN_ATTRIBUTE_BUCKET_PERIOD, + GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS, + GSCAN_ATTRIBUTE_BUCKET_CHANNELS, + GSCAN_ATTRIBUTE_NUM_AP_PER_SCAN, + GSCAN_ATTRIBUTE_REPORT_THRESHOLD, + GSCAN_ATTRIBUTE_NUM_SCANS_TO_CACHE, + GSCAN_ATTRIBUTE_BAND = GSCAN_ATTRIBUTE_BUCKETS_BAND, + + GSCAN_ATTRIBUTE_ENABLE_FEATURE = 20, + GSCAN_ATTRIBUTE_SCAN_RESULTS_COMPLETE, /* indicates no more results */ + GSCAN_ATTRIBUTE_FLUSH_FEATURE, /* Flush all the configs */ + GSCAN_ENABLE_FULL_SCAN_RESULTS, + GSCAN_ATTRIBUTE_REPORT_EVENTS, + + /* remaining reserved for additional attributes */ + GSCAN_ATTRIBUTE_NUM_OF_RESULTS = 30, + GSCAN_ATTRIBUTE_FLUSH_RESULTS, + GSCAN_ATTRIBUTE_SCAN_RESULTS, /* flat array of wifi_scan_result */ + GSCAN_ATTRIBUTE_SCAN_ID, /* indicates scan number */ + GSCAN_ATTRIBUTE_SCAN_FLAGS, /* indicates if scan was aborted */ + GSCAN_ATTRIBUTE_AP_FLAGS, /* flags on significant change event */ + GSCAN_ATTRIBUTE_NUM_CHANNELS, + GSCAN_ATTRIBUTE_CHANNEL_LIST, + GSCAN_ATTRIBUTE_CH_BUCKET_BITMASK, + /* remaining reserved for additional attributes */ + + GSCAN_ATTRIBUTE_SSID = 40, + GSCAN_ATTRIBUTE_BSSID, + GSCAN_ATTRIBUTE_CHANNEL, + GSCAN_ATTRIBUTE_RSSI, + GSCAN_ATTRIBUTE_TIMESTAMP, + GSCAN_ATTRIBUTE_RTT, + GSCAN_ATTRIBUTE_RTTSD, + + /* remaining reserved for additional attributes */ + + GSCAN_ATTRIBUTE_HOTLIST_BSSIDS = 50, + GSCAN_ATTRIBUTE_RSSI_LOW, + GSCAN_ATTRIBUTE_RSSI_HIGH, + GSCAN_ATTRIBUTE_HOTLIST_ELEM, + GSCAN_ATTRIBUTE_HOTLIST_FLUSH, + GSCAN_ATTRIBUTE_HOTLIST_BSSID_COUNT, + + /* remaining reserved for additional attributes */ + GSCAN_ATTRIBUTE_RSSI_SAMPLE_SIZE = 60, + GSCAN_ATTRIBUTE_LOST_AP_SAMPLE_SIZE, + GSCAN_ATTRIBUTE_MIN_BREACHING, + GSCAN_ATTRIBUTE_SIGNIFICANT_CHANGE_BSSIDS, + GSCAN_ATTRIBUTE_SIGNIFICANT_CHANGE_FLUSH, + + /* EPNO */ + GSCAN_ATTRIBUTE_EPNO_SSID_LIST = 70, + GSCAN_ATTRIBUTE_EPNO_SSID, + GSCAN_ATTRIBUTE_EPNO_SSID_LEN, + GSCAN_ATTRIBUTE_EPNO_RSSI, + GSCAN_ATTRIBUTE_EPNO_FLAGS, + GSCAN_ATTRIBUTE_EPNO_AUTH, + GSCAN_ATTRIBUTE_EPNO_SSID_NUM, + GSCAN_ATTRIBUTE_EPNO_FLUSH, + + /* remaining reserved for additional attributes */ + + GSCAN_ATTRIBUTE_WHITELIST_SSID = 80, + GSCAN_ATTRIBUTE_NUM_WL_SSID, + GSCAN_ATTRIBUTE_WL_SSID_LEN, + GSCAN_ATTRIBUTE_WL_SSID_FLUSH, + GSCAN_ATTRIBUTE_WHITELIST_SSID_ELEM, + GSCAN_ATTRIBUTE_NUM_BSSID, + GSCAN_ATTRIBUTE_BSSID_PREF_LIST, + GSCAN_ATTRIBUTE_BSSID_PREF_FLUSH, + GSCAN_ATTRIBUTE_BSSID_PREF, + GSCAN_ATTRIBUTE_RSSI_MODIFIER, + + /* remaining reserved for additional attributes */ + + GSCAN_ATTRIBUTE_A_BAND_BOOST_THRESHOLD = 90, + GSCAN_ATTRIBUTE_A_BAND_PENALTY_THRESHOLD, + GSCAN_ATTRIBUTE_A_BAND_BOOST_FACTOR, + GSCAN_ATTRIBUTE_A_BAND_PENALTY_FACTOR, + GSCAN_ATTRIBUTE_A_BAND_MAX_BOOST, + GSCAN_ATTRIBUTE_LAZY_ROAM_HYSTERESIS, + GSCAN_ATTRIBUTE_ALERT_ROAM_RSSI_TRIGGER, + GSCAN_ATTRIBUTE_LAZY_ROAM_ENABLE, + + /* BSSID blacklist */ + GSCAN_ATTRIBUTE_BSSID_BLACKLIST_FLUSH = 100, + GSCAN_ATTRIBUTE_BLACKLIST_BSSID, + + /* ANQPO */ + GSCAN_ATTRIBUTE_ANQPO_HS_LIST = 110, + GSCAN_ATTRIBUTE_ANQPO_HS_LIST_SIZE, + GSCAN_ATTRIBUTE_ANQPO_HS_NETWORK_ID, + GSCAN_ATTRIBUTE_ANQPO_HS_NAI_REALM, + GSCAN_ATTRIBUTE_ANQPO_HS_ROAM_CONSORTIUM_ID, + GSCAN_ATTRIBUTE_ANQPO_HS_PLMN, + + /* Adaptive scan attributes */ + GSCAN_ATTRIBUTE_BUCKET_STEP_COUNT = 120, + GSCAN_ATTRIBUTE_BUCKET_MAX_PERIOD, + + /* ePNO cfg */ + GSCAN_ATTRIBUTE_EPNO_5G_RSSI_THR = 130, + GSCAN_ATTRIBUTE_EPNO_2G_RSSI_THR, + GSCAN_ATTRIBUTE_EPNO_INIT_SCORE_MAX, + GSCAN_ATTRIBUTE_EPNO_CUR_CONN_BONUS, + GSCAN_ATTRIBUTE_EPNO_SAME_NETWORK_BONUS, + GSCAN_ATTRIBUTE_EPNO_SECURE_BONUS, + GSCAN_ATTRIBUTE_EPNO_5G_BONUS, + + /* Roaming features */ + GSCAN_ATTRIBUTE_ROAM_STATE_SET = 140, + GSCAN_ATTRIBUTE_MAX + +} GSCAN_ATTRIBUTE; + +typedef struct { + int num_bssid; // number of blacklisted BSSIDs + mac_addr bssids[MAX_BLACKLIST_BSSID]; // blacklisted BSSIDs +} wifi_bssid_params; + +// helper methods +wifi_error wifi_enable_full_scan_results(wifi_request_id id, wifi_interface_handle iface, + wifi_scan_result_handler handler); +wifi_error wifi_disable_full_scan_results(wifi_request_id id, wifi_interface_handle iface); +int wifi_handle_full_scan_event(wifi_request_id id, WifiEvent& event, + wifi_scan_result_handler handler); +void convert_to_hal_result(wifi_scan_result *to, wifi_gscan_result_t *from); + + +void convert_to_hal_result(wifi_scan_result *to, wifi_gscan_result_t *from) +{ + to->ts = from->ts; + to->channel = from->channel; + to->rssi = from->rssi; + to->rtt = from->rtt; + to->rtt_sd = from->rtt_sd; + to->beacon_period = from->beacon_period; + to->capability = from->capability; + memcpy(to->ssid, from->ssid, (DOT11_MAX_SSID_LEN+1)); + memcpy(&to->bssid, &from->bssid, sizeof(mac_addr)); +} + +///////////////////////////////////////////////////////////////////////////// + +class GetCapabilitiesCommand : public WifiCommand +{ + void *mCapabilities; + uint16_t mRequesttype; + int mRequestsize; + public: + GetCapabilitiesCommand(wifi_interface_handle iface, void *capabitlites, uint16_t request_type, + int request_size) + : WifiCommand("GetGscanCapabilitiesCommand", iface, 0), mCapabilities(capabitlites), mRequesttype(request_type), + mRequestsize(request_size) + { + memset(mCapabilities, 0, mRequestsize); + } + + virtual int create() { + ALOGV("Creating message to get scan capabilities; iface = %d", mIfaceInfo->id); + + int ret = mMsg.create(GOOGLE_OUI, mRequesttype); + if (ret < 0) { + return ret; + } + + return ret; + } + + protected: + virtual int handleResponse(WifiEvent& reply) { + + ALOGV("In GetCapabilities::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + + void *data = reply.get_vendor_data(); + int len = reply.get_vendor_data_len(); + + ALOGV("Id = %0x, subcmd = 0x%x, len = %d, expected len = %d", id, subcmd, len, + mRequestsize); + + memcpy(mCapabilities, data, min(len, mRequestsize)); + + return NL_OK; + } +}; + +wifi_error wifi_get_gscan_capabilities(wifi_interface_handle handle, + wifi_gscan_capabilities *capabilities) +{ + GetCapabilitiesCommand command(handle, capabilities, GSCAN_SUBCMD_GET_CAPABILITIES, + (int)sizeof(wifi_gscan_capabilities)); + return (wifi_error) command.requestResponse(); +} + +/* Function to get chipset supported roaming capabilities */ +wifi_error wifi_get_roaming_capabilities(wifi_interface_handle handle, + wifi_roaming_capabilities *capabilities) +{ + GetCapabilitiesCommand command(handle, capabilities, WIFI_SUBCMD_ROAM_CAPABILITY, + (int)sizeof(wifi_roaming_capabilities)); + return (wifi_error) command.requestResponse(); +} + +class GetChannelListCommand : public WifiCommand +{ + wifi_channel *channels; + int max_channels; + int *num_channels; + int band; +public: + GetChannelListCommand(wifi_interface_handle iface, wifi_channel *channel_buf, int *ch_num, + int num_max_ch, int band) + : WifiCommand("GetChannelListCommand", iface, 0), channels(channel_buf), + max_channels(num_max_ch), num_channels(ch_num), band(band) + { + memset(channels, 0, sizeof(wifi_channel) * max_channels); + } + virtual int create() { + ALOGV("Creating message to get channel list; iface = %d", mIfaceInfo->id); + + int ret = mMsg.create(GOOGLE_OUI, GSCAN_SUBCMD_GET_CHANNEL_LIST); + if (ret < 0) { + return ret; + } + + nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); + ret = mMsg.put_u32(GSCAN_ATTRIBUTE_BAND, band); + if (ret < 0) { + return ret; + } + + mMsg.attr_end(data); + + return ret; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + + ALOGV("In GetChannelList::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + int num_channels_to_copy = 0; + + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + ALOGV("Id = %0x, subcmd = %d, len = %d", id, subcmd, len); + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in GetChannelList response; ignoring it"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == GSCAN_ATTRIBUTE_NUM_CHANNELS) { + num_channels_to_copy = it.get_u32(); + ALOGI("Got channel list with %d channels", num_channels_to_copy); + if(num_channels_to_copy > max_channels) + num_channels_to_copy = max_channels; + *num_channels = num_channels_to_copy; + } else if (it.get_type() == GSCAN_ATTRIBUTE_CHANNEL_LIST && num_channels_to_copy) { + memcpy(channels, it.get_data(), sizeof(int) * num_channels_to_copy); + } else { + ALOGW("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + + return NL_OK; + } +}; + +wifi_error wifi_get_valid_channels(wifi_interface_handle handle, + int band, int max_channels, wifi_channel *channels, int *num_channels) +{ + GetChannelListCommand command(handle, channels, num_channels, + max_channels, band); + return (wifi_error) command.requestResponse(); +} +///////////////////////////////////////////////////////////////////////////// + +/* helper functions */ +int createFeatureRequest(WifiRequest& request, int subcmd, int enable) { + + int result = request.create(GOOGLE_OUI, subcmd); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(GSCAN_ATTRIBUTE_ENABLE_FEATURE, enable); + if (result < 0) { + return result; + } + + request.attr_end(data); + return WIFI_SUCCESS; +} + +///////////////////////////////////////////////////////////////////////////// +class FullScanResultsCommand : public WifiCommand +{ + int *mParams; + wifi_scan_result_handler mHandler; +public: + FullScanResultsCommand(wifi_interface_handle iface, int id, int *params, + wifi_scan_result_handler handler) + : WifiCommand("FullScanResultsCommand", iface, id), mParams(params), mHandler(handler) + {*mParams = 0;} + + int createRequest(WifiRequest& request, int subcmd, int enable) { + int result = request.create(GOOGLE_OUI, subcmd); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(GSCAN_ENABLE_FULL_SCAN_RESULTS, enable); + if (result < 0) { + return result; + } + + request.attr_end(data); + return WIFI_SUCCESS; + + } + + int start() { + ALOGV("Enabling Full scan results"); + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request, GSCAN_SUBCMD_ENABLE_FULL_SCAN_RESULTS, 1); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create request; result = %d", result); + return result; + } + + registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_FULL_SCAN_RESULTS); + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_FULL_SCAN_RESULTS); + ALOGE("failed to enable full scan results; result = %d", result); + return result; + } + + return result; + } + + virtual int cancel() { + ALOGV("Disabling Full scan results"); + + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request, GSCAN_SUBCMD_ENABLE_FULL_SCAN_RESULTS, 0); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create request; result = %d", result); + } else { + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to disable full scan results;result = %d", result); + } + } + + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_FULL_SCAN_RESULTS); + return WIFI_SUCCESS; + } + + virtual int handleResponse(WifiEvent& reply) { + ALOGD("Request complete!"); + /* Nothing to do on response! */ + return NL_SKIP; + } + + virtual int handleEvent(WifiEvent& event) { + ALOGV("Full scan results: Got an event"); + return wifi_handle_full_scan_event(id(), event, mHandler); + } + +}; +///////////////////////////////////////////////////////////////////////////// + +class ScanCommand : public WifiCommand +{ + wifi_scan_cmd_params *mParams; + wifi_scan_result_handler mHandler; +public: + ScanCommand(wifi_interface_handle iface, int id, wifi_scan_cmd_params *params, + wifi_scan_result_handler handler) + : WifiCommand("ScanCommand", iface, id), mParams(params), mHandler(handler) + { } + + int createSetupRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_SET_CONFIG); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(GSCAN_ATTRIBUTE_BASE_PERIOD, mParams->base_period); + if (result < 0) { + return result; + } + + result = request.put_u32(GSCAN_ATTRIBUTE_NUM_BUCKETS, mParams->num_buckets); + if (result < 0) { + return result; + } + + for (int i = 0; i < mParams->num_buckets; i++) { + nlattr * bucket = request.attr_start(i); // next bucket + result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_ID, mParams->buckets[i].bucket); + if (result < 0) { + return result; + } + result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_PERIOD, mParams->buckets[i].period); + if (result < 0) { + return result; + } + result = request.put_u32(GSCAN_ATTRIBUTE_BUCKETS_BAND, + mParams->buckets[i].band); + if (result < 0) { + return result; + } + result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_STEP_COUNT, + mParams->buckets[i].step_count); + if (result < 0) { + return result; + } + result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_MAX_PERIOD, + mParams->buckets[i].max_period); + if (result < 0) { + return result; + } + result = request.put_u32(GSCAN_ATTRIBUTE_REPORT_EVENTS, + mParams->buckets[i].report_events); + if (result < 0) { + return result; + } + + result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS, + mParams->buckets[i].num_channels); + if (result < 0) { + return result; + } + + if (mParams->buckets[i].num_channels) { + nlattr *channels = request.attr_start(GSCAN_ATTRIBUTE_BUCKET_CHANNELS); + ALOGV(" channels: "); + for (int j = 0; j < mParams->buckets[i].num_channels; j++) { + result = request.put_u32(j, mParams->buckets[i].channels[j].channel); + ALOGV(" %u", mParams->buckets[i].channels[j].channel); + + if (result < 0) { + return result; + } + } + request.attr_end(channels); + } + + request.attr_end(bucket); + } + + request.attr_end(data); + return WIFI_SUCCESS; + } + + int createScanConfigRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_SET_SCAN_CONFIG); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(GSCAN_ATTRIBUTE_NUM_AP_PER_SCAN, mParams->max_ap_per_scan); + if (result < 0) { + return result; + } + + result = request.put_u32(GSCAN_ATTRIBUTE_REPORT_THRESHOLD, + mParams->report_threshold_percent); + if (result < 0) { + return result; + } + + int num_scans = mParams->report_threshold_num_scans; + + result = request.put_u32(GSCAN_ATTRIBUTE_NUM_SCANS_TO_CACHE, num_scans); + if (result < 0) { + return result; + } + + request.attr_end(data); + return WIFI_SUCCESS; + } + + int createStartRequest(WifiRequest& request) { + return createFeatureRequest(request, GSCAN_SUBCMD_ENABLE_GSCAN, 1); + } + + int createStopRequest(WifiRequest& request) { + return createFeatureRequest(request, GSCAN_SUBCMD_ENABLE_GSCAN, 0); + } + + void unregisterVendorHandlerAll() { + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_COMPLETE_SCAN); + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SCAN_RESULTS_AVAILABLE); + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_FULL_SCAN_RESULTS); + } + + int start() { + ALOGV("GSCAN start"); + WifiRequest request(familyId(), ifaceId()); + int result = createSetupRequest(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create setup request; result = %d", result); + return result; + } + + registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SCAN_RESULTS_AVAILABLE); + registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_COMPLETE_SCAN); + registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_FULL_SCAN_RESULTS); + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + unregisterVendorHandlerAll(); + ALOGE("failed to configure setup; result = %d", result); + return result; + } + + request.destroy(); + + result = createScanConfigRequest(request); + if (result != WIFI_SUCCESS) { + unregisterVendorHandlerAll(); + ALOGE("failed to create scan config request; result = %d", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + unregisterVendorHandlerAll(); + ALOGE("failed to configure scan; result = %d", result); + return result; + } + + ALOGV(" ....starting scan"); + + result = createStartRequest(request); + if (result != WIFI_SUCCESS) { + unregisterVendorHandlerAll(); + ALOGE("failed to create start request; result = %d", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + unregisterVendorHandlerAll(); + ALOGE("failed to start scan; result = %d", result); + return result; + } + return result; + } + + virtual int cancel() { + ALOGV("Stopping scan"); + + WifiRequest request(familyId(), ifaceId()); + int result = createStopRequest(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create stop request; result = %d", result); + } else { + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to stop scan; result = %d", result); + } + } + + unregisterVendorHandlerAll(); + return WIFI_SUCCESS; + } + + virtual int handleResponse(WifiEvent& reply) { + /* Nothing to do on response! */ + return NL_SKIP; + } + + virtual int handleEvent(WifiEvent& event) { + ALOGV("Got a scan results event"); + //event.log(); + + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = event.get_vendor_data_len(); + int event_id = event.get_vendor_subcmd(); + + if ((event_id == GSCAN_EVENT_COMPLETE_SCAN) || + (event_id == GSCAN_EVENT_SCAN_RESULTS_AVAILABLE)) { + if (vendor_data == NULL || len != 4) { + ALOGI("Bad event data!"); + return NL_SKIP; + } + wifi_scan_event evt_type; + evt_type = (wifi_scan_event) event.get_u32(NL80211_ATTR_VENDOR_DATA); + ALOGV("Received event type %d", evt_type); + if(*mHandler.on_scan_event) + (*mHandler.on_scan_event)(id(), evt_type); + } else if (event_id == GSCAN_EVENT_FULL_SCAN_RESULTS) { + wifi_handle_full_scan_event(id(), event, mHandler); + } + return NL_SKIP; + } +}; + +wifi_error wifi_start_gscan( + wifi_request_id id, + wifi_interface_handle iface, + wifi_scan_cmd_params params, + wifi_scan_result_handler handler) +{ + wifi_handle handle = getWifiHandle(iface); + + ALOGV("Starting GScan, halHandle = %p", handle); + + ScanCommand *cmd = new ScanCommand(iface, id, ¶ms, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + +wifi_error wifi_stop_gscan(wifi_request_id id, wifi_interface_handle iface) +{ + wifi_handle handle = getWifiHandle(iface); + ALOGV("Stopping GScan, wifi_request_id = %d, halHandle = %p", id, handle); + + if (id == -1) { + wifi_scan_result_handler handler; + wifi_scan_cmd_params dummy_params; + memset(&handler, 0, sizeof(handler)); + + ScanCommand *cmd = new ScanCommand(iface, id, &dummy_params, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + + return wifi_cancel_cmd(id, iface); +} + +wifi_error wifi_enable_full_scan_results( + wifi_request_id id, + wifi_interface_handle iface, + wifi_scan_result_handler handler) +{ + wifi_handle handle = getWifiHandle(iface); + int params_dummy; + + ALOGV("Enabling full scan results, halHandle = %p", handle); + + FullScanResultsCommand *cmd = new FullScanResultsCommand(iface, id, ¶ms_dummy, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + +int wifi_handle_full_scan_event( + wifi_request_id id, + WifiEvent& event, + wifi_scan_result_handler handler) +{ + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + unsigned int len = event.get_vendor_data_len(); + + if (vendor_data == NULL || len < sizeof(wifi_gscan_full_result_t)) { + ALOGI("Full scan results: No scan results found"); + return NL_SKIP; + } + + wifi_gscan_full_result_t *drv_res = (wifi_gscan_full_result_t *)event.get_vendor_data(); + /* To protect against corrupted data, put a ceiling */ + int ie_len = min(MAX_PROBE_RESP_IE_LEN, drv_res->ie_length); + wifi_scan_result *full_scan_result; + wifi_gscan_result_t *fixed = &drv_res->fixed; + + if ((ie_len + offsetof(wifi_gscan_full_result_t, ie_data)) > len) { + ALOGE("BAD event data, len %d ie_len %d fixed length %lu!\n", len, + ie_len, offsetof(wifi_gscan_full_result_t, ie_data)); + return NL_SKIP; + } + full_scan_result = (wifi_scan_result *) malloc((ie_len + offsetof(wifi_scan_result, ie_data))); + if (!full_scan_result) { + ALOGE("Full scan results: Can't malloc!\n"); + return NL_SKIP; + } + convert_to_hal_result(full_scan_result, fixed); + full_scan_result->ie_length = ie_len; + memcpy(full_scan_result->ie_data, drv_res->ie_data, ie_len); + if(handler.on_full_scan_result) + handler.on_full_scan_result(id, full_scan_result, drv_res->scan_ch_bucket); + + ALOGV("Full scan result: %-32s %02x:%02x:%02x:%02x:%02x:%02x %d %d %ld %lx %lx %x %d\n", + fixed->ssid, fixed->bssid[0], fixed->bssid[1], fixed->bssid[2], fixed->bssid[3], + fixed->bssid[4], fixed->bssid[5], fixed->rssi, fixed->channel, fixed->ts, + fixed->rtt, fixed->rtt_sd, drv_res->scan_ch_bucket, drv_res->ie_length); + free(full_scan_result); + return NL_SKIP; +} + + +wifi_error wifi_disable_full_scan_results(wifi_request_id id, wifi_interface_handle iface) +{ + ALOGV("Disabling full scan results"); + + if(id == -1) { + wifi_scan_result_handler handler; + int params_dummy; + + memset(&handler, 0, sizeof(handler)); + FullScanResultsCommand *cmd = new FullScanResultsCommand(iface, 0, ¶ms_dummy, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + + return wifi_cancel_cmd(id, iface); +} + + +///////////////////////////////////////////////////////////////////////////// + +class GetScanResultsCommand : public WifiCommand { + wifi_cached_scan_results *mScans; + int mMax; + int *mNum; + int mRetrieved; + byte mFlush; + int mCompleted; +public: + GetScanResultsCommand(wifi_interface_handle iface, byte flush, + wifi_cached_scan_results *results, int max, int *num) + : WifiCommand("GetScanResultsCommand", iface, -1), mScans(results), mMax(max), mNum(num), + mRetrieved(0), mFlush(flush), mCompleted(0) + { } + + int createRequest(WifiRequest& request, int num, byte flush) { + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_GET_SCAN_RESULTS); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(GSCAN_ATTRIBUTE_NUM_OF_RESULTS, num); + if (result < 0) { + return result; + } + + result = request.put_u8(GSCAN_ATTRIBUTE_FLUSH_RESULTS, flush); + if (result < 0) { + return result; + } + + request.attr_end(data); + return WIFI_SUCCESS; + } + + int execute() { + WifiRequest request(familyId(), ifaceId()); + ALOGV("retrieving %d scan results", mMax); + + for (int i = 0; i < 10 && mRetrieved < mMax; i++) { + int num_to_retrieve = mMax - mRetrieved; + // ALOGI("retrieving %d scan results in one shot", num_to_retrieve); + int result = createRequest(request, num_to_retrieve, mFlush); + if (result < 0) { + ALOGE("failed to create request"); + return result; + } + + int prev_retrieved = mRetrieved; + + result = requestResponse(request); + + if (result != WIFI_SUCCESS) { + ALOGE("failed to retrieve scan results; result = %d", result); + return result; + } + + if (mRetrieved == prev_retrieved || mCompleted) { + /* no more items left to retrieve */ + break; + } + + request.destroy(); + } + + ALOGV("GetScanResults read %d results", mRetrieved); + *mNum = mRetrieved; + return WIFI_SUCCESS; + } + + virtual int handleResponse(WifiEvent& reply) { + ALOGV("In GetScanResultsCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + + ALOGV("Id = %0x, subcmd = %d", id, subcmd); + + /* + if (subcmd != GSCAN_SUBCMD_SCAN_RESULTS) { + ALOGE("Invalid response to GetScanResultsCommand; ignoring it"); + return NL_SKIP; + } + */ + + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in GetScanResults response; ignoring it"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == GSCAN_ATTRIBUTE_SCAN_RESULTS_COMPLETE) { + mCompleted = it.get_u8(); + ALOGV("retrieved mCompleted flag : %d", mCompleted); + } else if (it.get_type() == GSCAN_ATTRIBUTE_SCAN_RESULTS || it.get_type() == 0) { + int scan_id = 0, flags = 0, num = 0, scan_ch_bucket_mask = 0; + for (nl_iterator it2(it.get()); it2.has_next(); it2.next()) { + if (it2.get_type() == GSCAN_ATTRIBUTE_SCAN_ID) { + scan_id = it2.get_u32(); + ALOGV("retrieved scan_id : 0x%0x", scan_id); + } else if (it2.get_type() == GSCAN_ATTRIBUTE_SCAN_FLAGS) { + flags = it2.get_u8(); + ALOGV("retrieved scan_flags : 0x%0x", flags); + } else if (it2.get_type() == GSCAN_ATTRIBUTE_NUM_OF_RESULTS) { + num = it2.get_u32(); + ALOGV("retrieved num_results: %d", num); + } else if (it2.get_type() == GSCAN_ATTRIBUTE_CH_BUCKET_BITMASK) { + scan_ch_bucket_mask = it2.get_u32(); + ALOGD("retrieved scan_ch_bucket_mask: %x", scan_ch_bucket_mask); + } else if (it2.get_type() == GSCAN_ATTRIBUTE_SCAN_RESULTS && num) { + if (mRetrieved >= mMax) { + ALOGW("Stored %d scans, ignoring excess results", mRetrieved); + break; + } + num = min(num, (int)(it2.get_len()/sizeof(wifi_gscan_result))); + num = min(num, (int)MAX_AP_CACHE_PER_SCAN); + ALOGV("Copying %d scan results", num); + wifi_gscan_result_t *results = (wifi_gscan_result_t *)it2.get_data(); + wifi_scan_result *mScanResults = mScans[mRetrieved].results; + + for (int i = 0; i < num; i++) { + wifi_gscan_result_t *result = &results[i]; + convert_to_hal_result(&mScanResults[i], result); + mScanResults[i].ie_length = 0; + ALOGV("%02d %-32s %02x:%02x:%02x:%02x:%02x:%02x %04d", i, + result->ssid, result->bssid[0], result->bssid[1], result->bssid[2], + result->bssid[3], result->bssid[4], result->bssid[5], + result->rssi); + } + mScans[mRetrieved].scan_id = scan_id; + mScans[mRetrieved].flags = flags; + mScans[mRetrieved].num_results = num; + mScans[mRetrieved].buckets_scanned = scan_ch_bucket_mask; + ALOGV("Setting result of scan_id : 0x%0x", mScans[mRetrieved].scan_id); + mRetrieved++; + } else { + ALOGW("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + } else { + ALOGW("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + ALOGV("GetScanResults read %d results", mRetrieved); + return NL_OK; + } +}; + +wifi_error wifi_get_cached_gscan_results(wifi_interface_handle iface, byte flush, + int max, wifi_cached_scan_results *results, int *num) { + ALOGV("Getting cached scan results, iface handle = %p, num = %d", iface, *num); + + GetScanResultsCommand *cmd = new GetScanResultsCommand(iface, flush, results, max, num); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error err = (wifi_error)cmd->execute(); + cmd->releaseRef(); + return err; +} + +///////////////////////////////////////////////////////////////////////////// + +class BssidHotlistCommand : public WifiCommand +{ +private: + wifi_bssid_hotlist_params mParams; + wifi_hotlist_ap_found_handler mHandler; + static const int MAX_RESULTS = 64; + wifi_scan_result mResults[MAX_RESULTS]; +public: + BssidHotlistCommand(wifi_interface_handle handle, int id, + wifi_bssid_hotlist_params params, wifi_hotlist_ap_found_handler handler) + : WifiCommand("BssidHotlistCommand", handle, id), mParams(params), mHandler(handler) + { } + + int createSetupRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_SET_HOTLIST); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u8(GSCAN_ATTRIBUTE_HOTLIST_FLUSH, 1); + if (result < 0) { + return result; + } + + result = request.put_u32(GSCAN_ATTRIBUTE_LOST_AP_SAMPLE_SIZE, mParams.lost_ap_sample_size); + if (result < 0) { + return result; + } + + result = request.put_u32(GSCAN_ATTRIBUTE_HOTLIST_BSSID_COUNT, mParams.num_bssid); + if (result < 0) { + return result; + } + + struct nlattr * attr = request.attr_start(GSCAN_ATTRIBUTE_HOTLIST_BSSIDS); + for (int i = 0; i < mParams.num_bssid; i++) { + nlattr *attr2 = request.attr_start(GSCAN_ATTRIBUTE_HOTLIST_ELEM); + if (attr2 == NULL) { + return WIFI_ERROR_OUT_OF_MEMORY; + } + result = request.put_addr(GSCAN_ATTRIBUTE_BSSID, mParams.ap[i].bssid); + if (result < 0) { + return result; + } + result = request.put_u8(GSCAN_ATTRIBUTE_RSSI_HIGH, mParams.ap[i].high); + if (result < 0) { + return result; + } + result = request.put_u8(GSCAN_ATTRIBUTE_RSSI_LOW, mParams.ap[i].low); + if (result < 0) { + return result; + } + request.attr_end(attr2); + } + + request.attr_end(attr); + request.attr_end(data); + return result; + } + + int createTeardownRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_SET_HOTLIST); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u8(GSCAN_ATTRIBUTE_HOTLIST_FLUSH, 1); + if (result < 0) { + return result; + } + + struct nlattr * attr = request.attr_start(GSCAN_ATTRIBUTE_HOTLIST_BSSIDS); + request.attr_end(attr); + request.attr_end(data); + return result; + } + + void unregisterVendorHandlerAll() { + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_HOTLIST_RESULTS_FOUND); + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_HOTLIST_RESULTS_LOST); + } + + int start() { + ALOGI("Executing hotlist setup request, num = %d", mParams.num_bssid); + WifiRequest request(familyId(), ifaceId()); + int result = createSetupRequest(request); + if (result < 0) { + return result; + } + + registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_HOTLIST_RESULTS_FOUND); + registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_HOTLIST_RESULTS_LOST); + + result = requestResponse(request); + if (result < 0) { + unregisterVendorHandlerAll(); + ALOGI("Failed to execute hotlist setup request, result = %d", result); + return result; + } + + ALOGI("Successfully set %d APs in the hotlist ", mParams.num_bssid); + result = createFeatureRequest(request, GSCAN_SUBCMD_ENABLE_GSCAN, 1); + if (result < 0) { + unregisterVendorHandlerAll(); + return result; + } + + result = requestResponse(request); + if (result < 0) { + unregisterVendorHandlerAll(); + return result; + } + + ALOGI("successfully restarted the scan"); + return result; + } + + virtual int cancel() { + /* unregister event handler */ + unregisterVendorHandlerAll(); + /* create set hotlist message with empty hotlist */ + WifiRequest request(familyId(), ifaceId()); + int result = createTeardownRequest(request); + if (result < 0) { + return result; + } + + result = requestResponse(request); + if (result < 0) { + return result; + } + + ALOGI("Successfully reset APs in current hotlist"); + return result; + } + + virtual int handleResponse(WifiEvent& reply) { + /* Nothing to do on response! */ + return NL_SKIP; + } + + virtual int handleEvent(WifiEvent& event) { + ALOGI("Hotlist AP event"); + int event_id = event.get_vendor_subcmd(); + // event.log(); + + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = event.get_vendor_data_len(); + + if (vendor_data == NULL || len == 0) { + ALOGI("No scan results found"); + return NL_SKIP; + } + + memset(mResults, 0, sizeof(wifi_scan_result) * MAX_RESULTS); + + int num = len / sizeof(wifi_gscan_result_t); + wifi_gscan_result_t *inp = (wifi_gscan_result_t *)event.get_vendor_data(); + num = min(MAX_RESULTS, num); + for (int i = 0; i < num; i++, inp++) { + convert_to_hal_result(&(mResults[i]), inp); + } + + if (event_id == GSCAN_EVENT_HOTLIST_RESULTS_FOUND) { + ALOGI("FOUND %d hotlist APs", num); + if (*mHandler.on_hotlist_ap_found) + (*mHandler.on_hotlist_ap_found)(id(), num, mResults); + } else if (event_id == GSCAN_EVENT_HOTLIST_RESULTS_LOST) { + ALOGI("LOST %d hotlist APs", num); + if (*mHandler.on_hotlist_ap_lost) + (*mHandler.on_hotlist_ap_lost)(id(), num, mResults); + } + return NL_SKIP; + } +}; + +class ePNOCommand : public WifiCommand +{ +private: + wifi_epno_params epno_params; + wifi_epno_handler mHandler; + wifi_scan_result mResults[MAX_EPNO_NETWORKS]; +public: + ePNOCommand(wifi_interface_handle handle, int id, + const wifi_epno_params *params, wifi_epno_handler handler) + : WifiCommand("ePNOCommand", handle, id), mHandler(handler) + { + if (params != NULL) { + memcpy(&epno_params, params, sizeof(wifi_epno_params)); + } else { + memset(&epno_params, 0, sizeof(wifi_epno_params)); + } + } + int createSetupRequest(WifiRequest& request) { + char tmp_buf[DOT11_MAX_SSID_LEN + 1]; + if (epno_params.num_networks > MAX_EPNO_NETWORKS) { + ALOGE("wrong epno num_networks:%d", epno_params.num_networks); + return WIFI_ERROR_INVALID_ARGS; + } + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_SET_EPNO_SSID); + if (result < 0) { + return result; + } + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u8(GSCAN_ATTRIBUTE_EPNO_FLUSH, 1); + if (result < 0) { + return result; + } + + result = request.put_u8(GSCAN_ATTRIBUTE_EPNO_5G_RSSI_THR, + (u8)epno_params.min5GHz_rssi); + if (result < 0) { + return result; + } + result = request.put_u8(GSCAN_ATTRIBUTE_EPNO_2G_RSSI_THR, + (u8)epno_params.min24GHz_rssi); + if (result < 0) { + return result; + } + result = request.put_u16(GSCAN_ATTRIBUTE_EPNO_INIT_SCORE_MAX, + epno_params.initial_score_max); + if (result < 0) { + return result; + } + result = request.put_u16(GSCAN_ATTRIBUTE_EPNO_CUR_CONN_BONUS, + epno_params.current_connection_bonus); + if (result < 0) { + return result; + } + result = request.put_u16(GSCAN_ATTRIBUTE_EPNO_SAME_NETWORK_BONUS, + epno_params.same_network_bonus); + if (result < 0) { + return result; + } + result = request.put_u16(GSCAN_ATTRIBUTE_EPNO_SECURE_BONUS, + epno_params.secure_bonus); + if (result < 0) { + return result; + } + result = request.put_u16(GSCAN_ATTRIBUTE_EPNO_5G_BONUS, + epno_params.band5GHz_bonus); + if (result < 0) { + return result; + } + result = request.put_u8(GSCAN_ATTRIBUTE_EPNO_SSID_NUM, + epno_params.num_networks); + if (result < 0) { + return result; + } + struct nlattr * attr = request.attr_start(GSCAN_ATTRIBUTE_EPNO_SSID_LIST); + wifi_epno_network *ssid_list = epno_params.networks; + for (int i = 0; i < epno_params.num_networks; i++) { + nlattr *attr2 = request.attr_start(i); + if (attr2 == NULL) { + return WIFI_ERROR_OUT_OF_MEMORY; + } + strlcpy(tmp_buf, ssid_list[i].ssid, sizeof(tmp_buf)); + result = request.put(GSCAN_ATTRIBUTE_EPNO_SSID, tmp_buf, + strlen(tmp_buf)); + ALOGI("PNO network: SSID %s flags %x auth %x", tmp_buf, + ssid_list[i].flags, + ssid_list[i].auth_bit_field); + if (result < 0) { + return result; + } + result = request.put_u32(GSCAN_ATTRIBUTE_EPNO_SSID_LEN, + strlen(tmp_buf)); + if (result < 0) { + return result; + } + result = request.put_u32(GSCAN_ATTRIBUTE_EPNO_FLAGS, ssid_list[i].flags); + if (result < 0) { + return result; + } + result = request.put_u32(GSCAN_ATTRIBUTE_EPNO_AUTH, ssid_list[i].auth_bit_field); + if (result < 0) { + return result; + } + request.attr_end(attr2); + } + request.attr_end(attr); + request.attr_end(data); + return result; + } + + int createTeardownRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_SET_EPNO_SSID); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u8(GSCAN_ATTRIBUTE_EPNO_FLUSH, 1); + if (result < 0) { + return result; + } + request.attr_end(data); + return result; + } + + int start() { + ALOGI("Executing ePNO setup request, num = %d", epno_params.num_networks); + WifiRequest request(familyId(), ifaceId()); + int result = createSetupRequest(request); + if (result < 0) { + return result; + } + + registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_EPNO_EVENT); + result = requestResponse(request); + if (result < 0) { + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_EPNO_EVENT); + ALOGI("Failed to execute ePNO setup request, result = %d", result); + return result; + } + + ALOGI("Successfully set %d SSIDs for ePNO", epno_params.num_networks); + ALOGI("successfully restarted the scan"); + return result; + } + + virtual int cancel() { + /* unregister event handler */ + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_EPNO_EVENT); + /* create set hotlist message with empty hotlist */ + WifiRequest request(familyId(), ifaceId()); + int result = createTeardownRequest(request); + if (result < 0) { + return result; + } + + result = requestResponse(request); + if (result < 0) { + return result; + } + + ALOGI("Successfully reset APs in current hotlist"); + return result; + } + + virtual int handleResponse(WifiEvent& reply) { + /* Nothing to do on response! */ + return NL_SKIP; + } + + virtual int handleEvent(WifiEvent& event) { + ALOGI("ePNO event"); + // event.log(); + + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = event.get_vendor_data_len(); + + if (vendor_data == NULL || len == 0) { + ALOGI("No scan results found"); + return NL_SKIP; + } + + memset(mResults, 0, sizeof(wifi_scan_result) * MAX_EPNO_NETWORKS); + + unsigned int num = len / sizeof(wifi_pno_result_t); + unsigned int i; + num = min(MAX_EPNO_NETWORKS, num); + wifi_pno_result_t *res = (wifi_pno_result_t *) event.get_vendor_data(); + for (i = 0; i < num; i++) { + if (res[i].flags == PNO_SSID_FOUND) { + memcpy(mResults[i].ssid, res[i].ssid, res[i].ssid_len); + memcpy(mResults[i].bssid, res[i].bssid, sizeof(mac_addr)); + + mResults[i].ssid[res[i].ssid_len] = '\0'; + mResults[i].channel = res[i].channel; + mResults[i].rssi = res[i].rssi; + } + } + if (*mHandler.on_network_found) + (*mHandler.on_network_found)(id(), num, mResults); + return NL_SKIP; + } +}; + +wifi_error wifi_set_bssid_hotlist(wifi_request_id id, wifi_interface_handle iface, + wifi_bssid_hotlist_params params, wifi_hotlist_ap_found_handler handler) +{ + wifi_handle handle = getWifiHandle(iface); + + BssidHotlistCommand *cmd = new BssidHotlistCommand(iface, id, params, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + +wifi_error wifi_reset_bssid_hotlist(wifi_request_id id, wifi_interface_handle iface) +{ + return wifi_cancel_cmd(id, iface); +} + + +///////////////////////////////////////////////////////////////////////////// + +class SignificantWifiChangeCommand : public WifiCommand +{ + typedef struct { + mac_addr bssid; // BSSID + wifi_channel channel; // channel frequency in MHz + int num_rssi; // number of rssi samples + wifi_rssi rssi[8]; // RSSI history in db + } wifi_significant_change_result_internal; + +private: + wifi_significant_change_params mParams; + wifi_significant_change_handler mHandler; + static const int MAX_RESULTS = 64; + wifi_significant_change_result_internal mResultsBuffer[MAX_RESULTS]; + wifi_significant_change_result *mResults[MAX_RESULTS]; +public: + SignificantWifiChangeCommand(wifi_interface_handle handle, int id, + wifi_significant_change_params params, wifi_significant_change_handler handler) + : WifiCommand("SignificantWifiChangeCommand", handle, id), mParams(params), + mHandler(handler) + { } + + int createSetupRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_CONFIG); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u8(GSCAN_ATTRIBUTE_SIGNIFICANT_CHANGE_FLUSH, 1); + if (result < 0) { + return result; + } + result = request.put_u16(GSCAN_ATTRIBUTE_RSSI_SAMPLE_SIZE, mParams.rssi_sample_size); + if (result < 0) { + return result; + } + result = request.put_u16(GSCAN_ATTRIBUTE_LOST_AP_SAMPLE_SIZE, mParams.lost_ap_sample_size); + if (result < 0) { + return result; + } + result = request.put_u16(GSCAN_ATTRIBUTE_MIN_BREACHING, mParams.min_breaching); + if (result < 0) { + return result; + } + result = request.put_u16(GSCAN_ATTRIBUTE_NUM_BSSID, mParams.num_bssid); + if (result < 0) { + return result; + } + if (mParams.num_bssid != 0) { + nlattr* attr = request.attr_start(GSCAN_ATTRIBUTE_SIGNIFICANT_CHANGE_BSSIDS); + if (attr == NULL) { + return WIFI_ERROR_OUT_OF_MEMORY; + } + + for (int i = 0; i < mParams.num_bssid; i++) { + nlattr* attr2 = request.attr_start(i); + if (attr2 == NULL) { + return WIFI_ERROR_OUT_OF_MEMORY; + } + result = request.put_addr(GSCAN_ATTRIBUTE_BSSID, mParams.ap[i].bssid); + if (result < 0) { + return result; + } + result = request.put_u8(GSCAN_ATTRIBUTE_RSSI_HIGH, mParams.ap[i].high); + if (result < 0) { + return result; + } + result = request.put_u8(GSCAN_ATTRIBUTE_RSSI_LOW, mParams.ap[i].low); + if (result < 0) { + return result; + } + request.attr_end(attr2); + } + + request.attr_end(attr); + } + request.attr_end(data); + + return result; + } + + int createTeardownRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_CONFIG); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u16(GSCAN_ATTRIBUTE_SIGNIFICANT_CHANGE_FLUSH, 1); + if (result < 0) { + return result; + } + + request.attr_end(data); + return result; + } + + int start() { + ALOGI("Set significant wifi change config"); + WifiRequest request(familyId(), ifaceId()); + + int result = createSetupRequest(request); + if (result < 0) { + return result; + } + + registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS); + + result = requestResponse(request); + if (result < 0) { + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS); + ALOGI("failed to set significant wifi change config %d", result); + return result; + } + + ALOGI("successfully set significant wifi change config"); + + result = createFeatureRequest(request, GSCAN_SUBCMD_ENABLE_GSCAN, 1); + if (result < 0) { + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS); + return result; + } + + result = requestResponse(request); + if (result < 0) { + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS); + return result; + } + + ALOGI("successfully restarted the scan"); + return result; + } + + virtual int cancel() { + /* unregister event handler */ + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS); + + /* create set significant change monitor message with empty hotlist */ + WifiRequest request(familyId(), ifaceId()); + + int result = createTeardownRequest(request); + if (result < 0) { + return result; + } + + result = requestResponse(request); + if (result < 0) { + return result; + } + + ALOGI("successfully reset significant wifi change config"); + return result; + } + + virtual int handleResponse(WifiEvent& reply) { + /* Nothing to do on response! */ + return NL_SKIP; + } + + virtual int handleEvent(WifiEvent& event) { + ALOGV("Got a significant wifi change event"); + + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = event.get_vendor_data_len(); + + if (vendor_data == NULL || len == 0) { + ALOGI("No scan results found"); + return NL_SKIP; + } + + typedef struct { + uint16_t flags; + uint16_t channel; + mac_addr bssid; + s8 rssi_history[8]; + } ChangeInfo; + + int num = min(len / sizeof(ChangeInfo), MAX_RESULTS); + ChangeInfo *ci = (ChangeInfo *)event.get_vendor_data(); + + for (int i = 0; i < num; i++) { + memcpy(mResultsBuffer[i].bssid, ci[i].bssid, sizeof(mac_addr)); + mResultsBuffer[i].channel = ci[i].channel; + mResultsBuffer[i].num_rssi = 8; + for (int j = 0; j < mResultsBuffer[i].num_rssi; j++) + mResultsBuffer[i].rssi[j] = (int) ci[i].rssi_history[j]; + mResults[i] = reinterpret_cast<wifi_significant_change_result *>(&(mResultsBuffer[i])); + } + + ALOGV("Retrieved %d scan results", num); + + if (num != 0) { + (*mHandler.on_significant_change)(id(), num, mResults); + } else { + ALOGW("No significant change reported"); + } + + return NL_SKIP; + } +}; + +wifi_error wifi_set_significant_change_handler(wifi_request_id id, wifi_interface_handle iface, + wifi_significant_change_params params, wifi_significant_change_handler handler) +{ + wifi_handle handle = getWifiHandle(iface); + + SignificantWifiChangeCommand *cmd = new SignificantWifiChangeCommand( + iface, id, params, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + +wifi_error wifi_reset_significant_change_handler(wifi_request_id id, wifi_interface_handle iface) +{ + return wifi_cancel_cmd(id, iface); +} + +wifi_error wifi_reset_epno_list(wifi_request_id id, wifi_interface_handle iface) +{ + if (id == -1) { + wifi_epno_handler handler; + + memset(&handler, 0, sizeof(handler)); + ePNOCommand *cmd = new ePNOCommand(iface, id, NULL, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + return wifi_cancel_cmd(id, iface); +} + +wifi_error wifi_set_epno_list(wifi_request_id id, wifi_interface_handle iface, + const wifi_epno_params *params, wifi_epno_handler handler) +{ + wifi_handle handle = getWifiHandle(iface); + if (handler.on_network_found == NULL) { + return WIFI_ERROR_INVALID_ARGS; + } + + ePNOCommand *cmd = new ePNOCommand(iface, id, params, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + +class BssidBlacklistCommand : public WifiCommand +{ + private: + wifi_bssid_params *mParams; + public: + BssidBlacklistCommand(wifi_interface_handle handle, int id, + wifi_bssid_params *params) + : WifiCommand("BssidBlacklistCommand", handle, id), mParams(params) + { } + int createRequest(WifiRequest& request) { + if ((mParams->num_bssid < 0) || (mParams->num_bssid > MAX_BLACKLIST_BSSID)) { + return WIFI_ERROR_INVALID_ARGS; + } + int result = request.create(GOOGLE_OUI, WIFI_SUBCMD_SET_BSSID_BLACKLIST); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + if (!mParams->num_bssid) { + result = request.put_u32(GSCAN_ATTRIBUTE_BSSID_BLACKLIST_FLUSH, 1); + if (result < 0) { + return result; + } + } else { + result = request.put_u32(GSCAN_ATTRIBUTE_NUM_BSSID, mParams->num_bssid); + if (result < 0) { + return result; + } + for (int i = 0; i < mParams->num_bssid; i++) { + result = request.put_addr(GSCAN_ATTRIBUTE_BLACKLIST_BSSID, mParams->bssids[i]); + if (result < 0) { + return result; + } + } + } + request.attr_end(data); + return result; + } + + int start() { + ALOGV("Executing bssid blacklist request, num = %d", mParams->num_bssid); + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request); + if (result < 0) { + return result; + } + + result = requestResponse(request); + if (result < 0) { + ALOGE("Failed to execute bssid blacklist request, result = %d", result); + return result; + } + + ALOGI("Successfully added %d blacklist bssids", mParams->num_bssid); + return result; + } + + virtual int handleResponse(WifiEvent& reply) { + /* Nothing to do on response! */ + return NL_SKIP; + } +}; + +wifi_error wifi_set_bssid_blacklist(wifi_request_id id, wifi_interface_handle iface, + wifi_bssid_params params) +{ + BssidBlacklistCommand *cmd = new BssidBlacklistCommand(iface, id, ¶ms); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + //release the reference of command as well + cmd->releaseRef(); + return result; +} + +wifi_error wifi_configure_roaming(wifi_interface_handle iface, + wifi_roaming_config *roam_config) +{ + wifi_error ret; + wifi_bssid_params bssid_params; + unsigned int i; + wifi_request_id id = 0; + + /* Set bssid blacklist */ + if (roam_config->num_blacklist_bssid == 0) { + /* Flush request */ + ALOGI("%s: num_blacklist_bssid == 0 (flush)", __FUNCTION__); + } + + bssid_params.num_bssid = roam_config->num_blacklist_bssid; + + for (i = 0; i < roam_config->num_blacklist_bssid; i++) { + mac_addr &addr1 = roam_config->blacklist_bssid[i]; + memcpy(&bssid_params.bssids[i], &roam_config->blacklist_bssid[i], + sizeof(mac_addr)); + ALOGI("%02x:%02x:%02x:%02x:%02x:%02x\n", addr1[0], + addr1[1], addr1[2], addr1[3], addr1[4], addr1[5]); + } + ret = wifi_set_bssid_blacklist(id, iface, bssid_params); + if (ret != WIFI_SUCCESS) { + ALOGE("%s: Failed to configure blacklist bssids", __FUNCTION__); + return ret; + } + + return ret; +} + +class FirmwareRoamingStateCommand : public WifiCommand +{ + private: + fw_roaming_state_t roam_state; + public: + FirmwareRoamingStateCommand(wifi_interface_handle handle, + fw_roaming_state_t state) + : WifiCommand("FirmwareRoamingStateCommand", handle, -1), roam_state(state) + { } + int createRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, WIFI_SUBCMD_FW_ROAM_POLICY); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(GSCAN_ATTRIBUTE_ROAM_STATE_SET, roam_state); + if (result < 0) { + return result; + } + request.attr_end(data); + return result; + } + + int start() { + ALOGV("Executing firmware roam state set, state = %d", roam_state); + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request); + if (result < 0) { + return result; + } + + result = requestResponse(request); + if (result < 0) { + ALOGE("Failed to execute firmware roam state set, result = %d", result); + return result; + } + + ALOGI("Successfully set firmware roam state - %d", roam_state); + return result; + } + + virtual int handleResponse(WifiEvent& reply) { + /* Nothing to do on response! */ + return NL_SKIP; + } +}; + +wifi_error wifi_enable_firmware_roaming(wifi_interface_handle iface, + fw_roaming_state_t state) +{ + /* Set firmware roaming state */ + FirmwareRoamingStateCommand *cmd = new FirmwareRoamingStateCommand(iface, state); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + //release the reference of command as well + cmd->releaseRef(); + return result; +} + +//////////////////////////////////////////////////////////////////////////////// + +class AnqpoConfigureCommand : public WifiCommand +{ + int num_hs; + wifi_passpoint_network *mNetworks; + wifi_passpoint_event_handler mHandler; + wifi_scan_result *mResult; +public: + AnqpoConfigureCommand(wifi_request_id id, wifi_interface_handle iface, + int num, wifi_passpoint_network *hs_list, wifi_passpoint_event_handler handler) + : WifiCommand("AnqpoConfigureCommand", iface, id), num_hs(num), mNetworks(hs_list), + mHandler(handler) + { + mResult = NULL; + } + + int createRequest(WifiRequest& request, int val) { + + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_ANQPO_CONFIG); + result = request.put_u32(GSCAN_ATTRIBUTE_ANQPO_HS_LIST_SIZE, num_hs); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + struct nlattr * attr = request.attr_start(GSCAN_ATTRIBUTE_ANQPO_HS_LIST); + for (int i = 0; i < num_hs; i++) { + nlattr *attr2 = request.attr_start(i); + if (attr2 == NULL) { + return WIFI_ERROR_OUT_OF_MEMORY; + } + result = request.put_u32(GSCAN_ATTRIBUTE_ANQPO_HS_NETWORK_ID, mNetworks[i].id); + if (result < 0) { + return result; + } + result = request.put(GSCAN_ATTRIBUTE_ANQPO_HS_NAI_REALM, mNetworks[i].realm, 256); + if (result < 0) { + return result; + } + result = request.put(GSCAN_ATTRIBUTE_ANQPO_HS_ROAM_CONSORTIUM_ID, + mNetworks[i].roamingConsortiumIds, 128); + if (result < 0) { + return result; + } + result = request.put(GSCAN_ATTRIBUTE_ANQPO_HS_PLMN, mNetworks[i].plmn, 3); + if (result < 0) { + return result; + } + + request.attr_end(attr2); + } + + request.attr_end(attr); + request.attr_end(data); + + return WIFI_SUCCESS; + } + + int start() { + + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request, num_hs); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create request; result = %d", result); + return result; + } + + registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_ANQPO_HOTSPOT_MATCH); + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_ANQPO_HOTSPOT_MATCH); + ALOGE("failed to set ANQPO networks; result = %d", result); + return result; + } + + return result; + } + + virtual int cancel() { + + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request, 0); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create request; result = %d", result); + } else { + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to reset ANQPO networks;result = %d", result); + } + } + + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_ANQPO_HOTSPOT_MATCH); + return WIFI_SUCCESS; + } + + virtual int handleResponse(WifiEvent& reply) { + ALOGD("Request complete!"); + /* Nothing to do on response! */ + return NL_SKIP; + } + + virtual int handleEvent(WifiEvent& event) { + typedef struct { + u16 channel; /* channel of GAS protocol */ + u8 dialog_token; /* GAS dialog token */ + u8 fragment_id; /* fragment id */ + u16 status_code; /* status code on GAS completion */ + u16 data_len; /* length of data to follow */ + u8 data[1]; /* variable length specified by data_len */ + } wifi_anqp_gas_resp; + + ALOGI("ANQPO hotspot matched event!"); + + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + unsigned int len = event.get_vendor_data_len(); + + if (vendor_data == NULL || len < sizeof(wifi_scan_result)) { + ALOGI("No scan results found"); + return NL_SKIP; + } + mResult = (wifi_scan_result *)malloc(sizeof(wifi_scan_result)); + if (!mResult) { + return NL_SKIP; + } + wifi_gscan_full_result_t *drv_res = (wifi_gscan_full_result_t *)event.get_vendor_data(); + wifi_gscan_result_t *fixed = &drv_res->fixed; + convert_to_hal_result(mResult, fixed); + + byte *anqp = (byte *)drv_res + offsetof(wifi_gscan_full_result_t, ie_data) + drv_res->ie_length; + wifi_anqp_gas_resp *gas = (wifi_anqp_gas_resp *)anqp; + int anqp_len = offsetof(wifi_anqp_gas_resp, data) + gas->data_len; + int networkId = *(int *)((byte *)anqp + anqp_len); + + ALOGI("%-32s\t", mResult->ssid); + + ALOGI("%02x:%02x:%02x:%02x:%02x:%02x ", mResult->bssid[0], mResult->bssid[1], + mResult->bssid[2], mResult->bssid[3], mResult->bssid[4], mResult->bssid[5]); + + ALOGI("rssi:%d\t", mResult->rssi); + ALOGI("channel:%d\t", mResult->channel); + ALOGI("ts:0x%jx\t", mResult->ts); + ALOGI("rtt:0x%jx\t", mResult->rtt); + ALOGI("rtt_sd:0x%jx\n", mResult->rtt_sd); + + if(*mHandler.on_passpoint_network_found) + (*mHandler.on_passpoint_network_found)(id(), networkId, mResult, anqp_len, anqp); + free(mResult); + return NL_SKIP; + } +}; + +wifi_error wifi_set_passpoint_list(wifi_request_id id, wifi_interface_handle iface, int num, + wifi_passpoint_network *networks, wifi_passpoint_event_handler handler) +{ + wifi_handle handle = getWifiHandle(iface); + + AnqpoConfigureCommand *cmd = new AnqpoConfigureCommand(id, iface, num, networks, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + +wifi_error wifi_reset_passpoint_list(wifi_request_id id, wifi_interface_handle iface) +{ + return wifi_cancel_cmd(id, iface); +} diff --git a/synadhd/wifi_hal/link_layer_stats.cpp b/synadhd/wifi_hal/link_layer_stats.cpp new file mode 100644 index 0000000..8de7acd --- /dev/null +++ b/synadhd/wifi_hal/link_layer_stats.cpp @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * 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 <stdint.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/errqueue.h> + +#include <linux/pkt_sched.h> +#include <netlink/object-api.h> +#include <netlink/netlink.h> +#include <netlink/socket.h> +#include <netlink/handlers.h> + +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include <utils/Log.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +typedef enum { + ANDR_WIFI_ATTRIBUTE_INVALID = 0, + ANDR_WIFI_ATTRIBUTE_NUM_RADIO = 1, + ANDR_WIFI_ATTRIBUTE_STATS_INFO = 2, + ANDR_WIFI_ATTRIBUTE_STATS_MAX = 3 +} LINK_STAT_ATTRIBUTE; + +/* Internal radio statistics structure in the driver */ +typedef struct { + wifi_radio radio; + uint32_t on_time; + uint32_t tx_time; + uint32_t rx_time; + uint32_t on_time_scan; + uint32_t on_time_nbd; + uint32_t on_time_gscan; + uint32_t on_time_roam_scan; + uint32_t on_time_pno_scan; + uint32_t on_time_hs20; + uint32_t num_channels; + wifi_channel_stat channels[]; +} wifi_radio_stat_internal; + +enum { + LSTATS_SUBCMD_GET_INFO = ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START, +}; + +class GetLinkStatsCommand : public WifiCommand +{ + wifi_stats_result_handler mHandler; +public: + GetLinkStatsCommand(wifi_interface_handle iface, wifi_stats_result_handler handler) + : WifiCommand("GetLinkStatsCommand", iface, 0), mHandler(handler) + { } + + virtual int create() { + // ALOGI("Creating message to get link statistics; iface = %d", mIfaceInfo->id); + + int ret = mMsg.create(GOOGLE_OUI, LSTATS_SUBCMD_GET_INFO); + if (ret < 0) { + ALOGE("Failed to create %x - %d", LSTATS_SUBCMD_GET_INFO, ret); + return ret; + } + + return ret; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + void *data = NULL; + wifi_radio_stat *radio_stat_ptr = NULL; + u8 *iface_stat = NULL; + u8 *radioStatsBuf = NULL, *output = NULL, *data_ptr = NULL; + uint32_t total_size = 0, per_radio_size = 0, data_len = 0, rem_len = 0; + int num_radios = 0, id = 0, subcmd = 0, len = 0; + + // ALOGI("In GetLinkStatsCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + id = reply.get_vendor_id(); + subcmd = reply.get_vendor_subcmd(); + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + len = reply.get_vendor_data_len(); + + ALOGV("Id = %0x, subcmd = %d, len = %d\n", id, subcmd, len); + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in GetLinkStatCommand response; ignoring it"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == ANDR_WIFI_ATTRIBUTE_NUM_RADIO) { + num_radios = it.get_u32(); + } else if (it.get_type() == ANDR_WIFI_ATTRIBUTE_STATS_INFO) { + data = it.get_data(); + data_len = it.get_len(); + } else { + ALOGW("Ignoring invalid attribute type = %d, size = %d\n", + it.get_type(), it.get_len()); + } + } + + if (num_radios) { + rem_len = MAX_CMD_RESP_BUF_LEN; + radioStatsBuf = (u8 *)malloc(MAX_CMD_RESP_BUF_LEN); + if (!radioStatsBuf) { + ALOGE("No memory\n"); + return NL_SKIP; + } + memset(radioStatsBuf, 0, MAX_CMD_RESP_BUF_LEN); + output = radioStatsBuf; + + if (!data || !data_len) { + ALOGE("%s: null data\n", __func__); + return NL_SKIP; + } + + data_ptr = (u8*)data; + for (int i = 0; i < num_radios; i++) { + rem_len -= per_radio_size; + if (rem_len < per_radio_size) { + ALOGE("No data left for radio %d\n", i); + goto exit; + } + data_ptr = (u8*)data + total_size; + if (!data_ptr) { + ALOGE("Invalid data for radio index = %d\n", i); + goto exit; + } + radio_stat_ptr = + convertToExternalRadioStatStructure((wifi_radio_stat*)data_ptr, + &per_radio_size); + if (!radio_stat_ptr || !per_radio_size) { + ALOGE("No data for radio %d\n", i); + continue; + } + memcpy(output, radio_stat_ptr, per_radio_size); + output += per_radio_size; + total_size += per_radio_size; + } + + iface_stat = ((u8*)data + total_size); + if (!iface_stat || data_len < total_size) { + ALOGE("No data for iface stats!!, data_len = %d, total_size = %d\n", + data_len, total_size); + goto exit; + } + (*mHandler.on_link_stats_results)(id, (wifi_iface_stat *)iface_stat, + num_radios, (wifi_radio_stat *)radioStatsBuf); + } else { + /* To be deprecated, adding it to keep it backward compatible */ + // ALOGD("GetLinkStatCommand: zero radio case\n"); + data = reply.get_vendor_data(); + if (!data) { + ALOGE("Invalid vendor data received\n"); + return NL_SKIP; + } + + num_radios = 1; + data = reply.get_vendor_data(); + len = reply.get_vendor_data_len(); + if (!data || !len) { + ALOGE("Invalid vendor data received\n"); + return NL_SKIP; + } + radio_stat_ptr = + convertToExternalRadioStatStructureLegacy((wifi_radio_stat_internal *)data); + if (!radio_stat_ptr) { + ALOGE("Invalid stats pointer received\n"); + return NL_SKIP; + } + wifi_iface_stat *iface_stat = + (wifi_iface_stat *)((char *)&((wifi_radio_stat_internal *)data)->channels + + radio_stat_ptr->num_channels * sizeof(wifi_channel_stat)); + (*mHandler.on_link_stats_results)(id, iface_stat, num_radios, radio_stat_ptr); + } +exit: + if (radio_stat_ptr) { + free(radio_stat_ptr); + radio_stat_ptr = NULL; + } + if (radioStatsBuf) { + free(radioStatsBuf); + radioStatsBuf = NULL; + } + return NL_OK; + } + +private: + wifi_radio_stat *convertToExternalRadioStatStructure(wifi_radio_stat *internal_stat_ptr, + uint32_t *per_radio_size) { + wifi_radio_stat *external_stat_ptr = NULL; + if (!internal_stat_ptr) { + ALOGE("Incoming data is null\n"); + } else { + uint32_t channel_size = internal_stat_ptr->num_channels * sizeof(wifi_channel_stat); + *per_radio_size = offsetof(wifi_radio_stat, channels) + channel_size; + external_stat_ptr = (wifi_radio_stat *)malloc(*per_radio_size); + if (external_stat_ptr) { + external_stat_ptr->radio = internal_stat_ptr->radio; + external_stat_ptr->on_time = internal_stat_ptr->on_time; + external_stat_ptr->tx_time = internal_stat_ptr->tx_time; + external_stat_ptr->num_tx_levels = internal_stat_ptr->num_tx_levels; + external_stat_ptr->tx_time_per_levels = NULL; + external_stat_ptr->rx_time = internal_stat_ptr->rx_time; + external_stat_ptr->on_time_scan = internal_stat_ptr->on_time_scan; + external_stat_ptr->on_time_nbd = internal_stat_ptr->on_time_nbd; + external_stat_ptr->on_time_gscan = internal_stat_ptr->on_time_gscan; + external_stat_ptr->on_time_roam_scan = internal_stat_ptr->on_time_roam_scan; + external_stat_ptr->on_time_pno_scan = internal_stat_ptr->on_time_pno_scan; + external_stat_ptr->on_time_hs20 = internal_stat_ptr->on_time_hs20; + external_stat_ptr->num_channels = internal_stat_ptr->num_channels; + if (internal_stat_ptr->num_channels) { + memcpy(&(external_stat_ptr->channels), &(internal_stat_ptr->channels), + channel_size); + } + } + } + return external_stat_ptr; + } + + wifi_radio_stat *convertToExternalRadioStatStructureLegacy(wifi_radio_stat_internal *internal_stat_ptr) { + wifi_radio_stat *external_stat_ptr = NULL; + if (!internal_stat_ptr) { + ALOGE("Sta_ptr is null\n"); + } else { + uint32_t channel_size = internal_stat_ptr->num_channels * sizeof(wifi_channel_stat); + uint32_t total_size = sizeof(wifi_radio_stat) + channel_size; + external_stat_ptr = (wifi_radio_stat *)malloc(total_size); + if (external_stat_ptr) { + external_stat_ptr->radio = internal_stat_ptr->radio; + external_stat_ptr->on_time = internal_stat_ptr->on_time; + external_stat_ptr->tx_time = internal_stat_ptr->tx_time; + external_stat_ptr->rx_time = internal_stat_ptr->rx_time; + external_stat_ptr->tx_time_per_levels = NULL; + external_stat_ptr->num_tx_levels = 0; + external_stat_ptr->on_time_scan = internal_stat_ptr->on_time_scan; + external_stat_ptr->on_time_nbd = internal_stat_ptr->on_time_nbd; + external_stat_ptr->on_time_gscan = internal_stat_ptr->on_time_gscan; + external_stat_ptr->on_time_roam_scan = internal_stat_ptr->on_time_roam_scan; + external_stat_ptr->on_time_pno_scan = internal_stat_ptr->on_time_pno_scan; + external_stat_ptr->on_time_hs20 = internal_stat_ptr->on_time_hs20; + external_stat_ptr->num_channels = internal_stat_ptr->num_channels; + if (internal_stat_ptr->num_channels) { + memcpy(&(external_stat_ptr->channels), &(internal_stat_ptr->channels), + channel_size); + } + } + } + return external_stat_ptr; + } +}; + +wifi_error wifi_get_link_stats(wifi_request_id id, + wifi_interface_handle iface, wifi_stats_result_handler handler) +{ + GetLinkStatsCommand command(iface, handler); + return (wifi_error) command.requestResponse(); +} + +wifi_error wifi_set_link_stats( + wifi_interface_handle /* iface */, wifi_link_layer_params /* params */) +{ + /* Return success here since bcom HAL does not need set link stats. */ + return WIFI_SUCCESS; +} + +wifi_error wifi_clear_link_stats( + wifi_interface_handle /* iface */, u32 /* stats_clear_req_mask */, + u32 * /* stats_clear_rsp_mask */, u8 /* stop_req */, u8 * /* stop_rsp */) +{ + /* Return success here since bcom HAL does not support clear link stats. */ + return WIFI_SUCCESS; +} diff --git a/synadhd/wifi_hal/nan.cpp b/synadhd/wifi_hal/nan.cpp new file mode 100755 index 0000000..8536564 --- /dev/null +++ b/synadhd/wifi_hal/nan.cpp @@ -0,0 +1,5443 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * 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 <stdint.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/errqueue.h> +#include <ctype.h> +#include <linux/pkt_sched.h> +#include <netlink/object-api.h> +#include <netlink/netlink.h> +#include <netlink/socket.h> + +#include "nl80211_copy.h" + +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include <utils/Log.h> +#include <log/log.h> +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" +#include "netinet/in.h" +#include "arpa/inet.h" +#include <openssl/sha.h> +#include <openssl/evp.h> +#include <sys/ioctl.h> + +/* Changes between incompatible Version of NAN */ +#define NAN_MAJOR_REL_VERSION 1 +/* Changes between Source and Binary compatible Version of NAN */ +#define NAN_MINOR_REL_VERSION 2 +/* Changes between perfectly compatible Version of NAN */ +#define NAN_PATCH_REL_VERSION 3 + +#define SVC_NAME_TO_HASH 1 +#define NAN_SVC_HASH_SIZE 6 +#define C2S(x) case x: return #x; +#define NAN_PUB_RECV_FLAG_MAX 15 +#define NAN_SUB_RECV_FLAG_MAX 7 +#define NAN_DISC_IND_MAX 7 +#define NAN_MAX 255 +#define NAN_MIN 0 +#define INVALID 0xFF +#define NAN_MAX_PERIOD 16 +#define ISGREATER(i, x) (i > x) ? 1 : 0 +#define NAN_MAX_RSSI 90 +#define NAN_SECURITY_SALT_SIZE 14 +#define NAN_MAC_INVALID_TRANSID 0xFFFF + +#define SVCHASH_ISNULL(svc_hash) ((((u8 *)(svc_hash))[0] | \ + ((u8 *)(svc_hash))[1] | \ + ((u8 *)(svc_hash))[2] | \ + ((u8 *)(svc_hash))[3] | \ + ((u8 *)(svc_hash))[4] | \ + ((u8 *)(svc_hash))[5]) == 0) +#define ETHER_ISNULLADDR(ea) ((((u8 *)(ea))[0] | \ + ((u8 *)(ea))[1] | \ + ((u8 *)(ea))[2] | \ + ((u8 *)(ea))[3] | \ + ((u8 *)(ea))[4] | \ + ((u8 *)(ea))[5]) == 0) + +/* NAN structs versioning b/w DHD and HAL + * TODO:add versions for each struct*/ +#define NAN_HAL_VERSION_1 0x2 +struct nan_dbg_cntrs { + u32 dp_req; /* cmd */ + u32 dp_resp; /* cmd */ + u32 dp_req_evt; + u32 dp_confirm_evt; + u32 transmit_req; /* cmd */ + u32 transmit_txs; /* event */ + u32 transmit_recv; /* event */ +}; +nan_dbg_cntrs counters; + +u32 current_dhd_hal_ver = 0; + +/* TODO: Known bug in Android which was discovered too late and then left in for backward compatibility. + * The issue is that the Service Name selected by the framework is invalid - it contains a space. + * Therefore, the underlying implementation partially converts it to lower case and uses the results for PMK generation. + * I.e. the PMK is generated based on the following service name: "Wi-Fi Aware Data Path" + */ +/* SVC Hash generated for svc name string "Wi-Fi Aware Data Path" */ +u8 NAN_OOB_INTEROP_SVC_HASH[NAN_SVC_HASH_SIZE] = {0x05, 0x9e, 0xd4, 0xcf, 0x89, 0x1a}; +#define NAN_OOB_INTEROP_SVC_NAME "Wi-Fi Aware Data Path" + +static const char *NanStatusToString(NanStatusType status) +{ + switch (status) { + C2S(NAN_STATUS_SUCCESS) + C2S(NAN_STATUS_INTERNAL_FAILURE) + C2S(NAN_STATUS_PROTOCOL_FAILURE) + C2S(NAN_STATUS_INVALID_PUBLISH_SUBSCRIBE_ID) + C2S(NAN_STATUS_NO_RESOURCE_AVAILABLE) + C2S(NAN_STATUS_INVALID_PARAM) + C2S(NAN_STATUS_INVALID_REQUESTOR_INSTANCE_ID) + C2S(NAN_STATUS_INVALID_NDP_ID) + C2S(NAN_STATUS_NAN_NOT_ALLOWED) + C2S(NAN_STATUS_NO_OTA_ACK) + C2S(NAN_STATUS_ALREADY_ENABLED) + C2S(NAN_STATUS_FOLLOWUP_QUEUE_FULL) + C2S(NAN_STATUS_UNSUPPORTED_CONCURRENCY_NAN_DISABLED) + + default: + return "NAN_STATUS_INTERNAL_FAILURE"; + } +} + +/* Nan Data Path Security Information */ +typedef struct { + /* + Unique Instance Id identifying the Responder's service. + This is same as publish_id notified on the subscribe side + in a publish/subscribe scenario + */ + u32 requestor_instance_id; /* Value 0 for no publish/subscribe */ + /* + Discovery MAC addr of the publisher/peer + */ + u8 peer_disc_mac_addr[NAN_MAC_ADDR_LEN]; + /* + Unique token Id generated on the initiator/responder + side used for a NDP session between two NAN devices + */ + NanDataPathId ndp_instance_id; +} NanDataPathSecInfoRequest; +/* + * Note: NAN_ATTRIBUTE should match with one that on driver side, wl_cfgnan.h and + * NanAttrToString as well for enum to string. + */ +typedef enum { + NAN_ATTRIBUTE_HEADER = 100, + NAN_ATTRIBUTE_HANDLE = 101, + NAN_ATTRIBUTE_TRANSAC_ID = 102, + + /* NAN Enable request attributes */ + NAN_ATTRIBUTE_2G_SUPPORT = 103, + NAN_ATTRIBUTE_5G_SUPPORT = 104, + NAN_ATTRIBUTE_CLUSTER_LOW = 105, + NAN_ATTRIBUTE_CLUSTER_HIGH = 106, + NAN_ATTRIBUTE_SID_BEACON = 107, + NAN_ATTRIBUTE_SYNC_DISC_2G_BEACON = 108, + NAN_ATTRIBUTE_SYNC_DISC_5G_BEACON = 109, + NAN_ATTRIBUTE_SDF_2G_SUPPORT = 110, + NAN_ATTRIBUTE_SDF_5G_SUPPORT = 111, + NAN_ATTRIBUTE_RSSI_CLOSE = 112, + NAN_ATTRIBUTE_RSSI_MIDDLE = 113, + NAN_ATTRIBUTE_RSSI_PROXIMITY = 114, + NAN_ATTRIBUTE_HOP_COUNT_LIMIT = 115, + NAN_ATTRIBUTE_RANDOM_FACTOR = 116, + NAN_ATTRIBUTE_MASTER_PREF = 117, + NAN_ATTRIBUTE_PERIODIC_SCAN_INTERVAL = 118, + + /* Nan Publish/Subscribe request attributes */ + NAN_ATTRIBUTE_PUBLISH_ID = 119, + NAN_ATTRIBUTE_TTL = 120, + NAN_ATTRIBUTE_PERIOD = 121, + NAN_ATTRIBUTE_REPLIED_EVENT_FLAG = 122, + NAN_ATTRIBUTE_PUBLISH_TYPE = 123, + NAN_ATTRIBUTE_TX_TYPE = 124, + NAN_ATTRIBUTE_PUBLISH_COUNT = 125, + NAN_ATTRIBUTE_SERVICE_NAME_LEN = 126, + NAN_ATTRIBUTE_SERVICE_NAME = 127, + NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN = 128, + NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO = 129, + NAN_ATTRIBUTE_RX_MATCH_FILTER_LEN = 130, + NAN_ATTRIBUTE_RX_MATCH_FILTER = 131, + NAN_ATTRIBUTE_TX_MATCH_FILTER_LEN = 132, + NAN_ATTRIBUTE_TX_MATCH_FILTER = 133, + NAN_ATTRIBUTE_SUBSCRIBE_ID = 134, + NAN_ATTRIBUTE_SUBSCRIBE_TYPE = 135, + NAN_ATTRIBUTE_SERVICERESPONSEFILTER = 136, + NAN_ATTRIBUTE_SERVICERESPONSEINCLUDE = 137, + NAN_ATTRIBUTE_USESERVICERESPONSEFILTER = 138, + NAN_ATTRIBUTE_SSIREQUIREDFORMATCHINDICATION = 139, + NAN_ATTRIBUTE_SUBSCRIBE_MATCH = 140, + NAN_ATTRIBUTE_SUBSCRIBE_COUNT = 141, + NAN_ATTRIBUTE_MAC_ADDR = 142, + NAN_ATTRIBUTE_MAC_ADDR_LIST = 143, + NAN_ATTRIBUTE_MAC_ADDR_LIST_NUM_ENTRIES = 144, + NAN_ATTRIBUTE_PUBLISH_MATCH = 145, + + /* Nan Event attributes */ + NAN_ATTRIBUTE_ENABLE_STATUS = 146, + NAN_ATTRIBUTE_JOIN_STATUS = 147, + NAN_ATTRIBUTE_ROLE = 148, + NAN_ATTRIBUTE_MASTER_RANK = 149, + NAN_ATTRIBUTE_ANCHOR_MASTER_RANK = 150, + NAN_ATTRIBUTE_CNT_PEND_TXFRM = 151, + NAN_ATTRIBUTE_CNT_BCN_TX = 152, + NAN_ATTRIBUTE_CNT_BCN_RX = 153, + NAN_ATTRIBUTE_CNT_SVC_DISC_TX = 154, + NAN_ATTRIBUTE_CNT_SVC_DISC_RX = 155, + NAN_ATTRIBUTE_AMBTT = 156, + NAN_ATTRIBUTE_CLUSTER_ID = 157, + NAN_ATTRIBUTE_INST_ID = 158, + NAN_ATTRIBUTE_OUI = 159, + NAN_ATTRIBUTE_STATUS = 160, + NAN_ATTRIBUTE_DE_EVENT_TYPE = 161, + NAN_ATTRIBUTE_MERGE = 162, + NAN_ATTRIBUTE_IFACE = 163, + NAN_ATTRIBUTE_CHANNEL = 164, + NAN_ATTRIBUTE_PEER_ID = 165, + NAN_ATTRIBUTE_NDP_ID = 167, + NAN_ATTRIBUTE_SECURITY = 168, + NAN_ATTRIBUTE_QOS = 169, + NAN_ATTRIBUTE_RSP_CODE = 170, + NAN_ATTRIBUTE_INST_COUNT = 171, + NAN_ATTRIBUTE_PEER_DISC_MAC_ADDR = 172, + NAN_ATTRIBUTE_PEER_NDI_MAC_ADDR = 173, + NAN_ATTRIBUTE_IF_ADDR = 174, + NAN_ATTRIBUTE_WARMUP_TIME = 175, + NAN_ATTRIBUTE_RECV_IND_CFG = 176, + NAN_ATTRIBUTE_RSSI_CLOSE_5G = 177, + NAN_ATTRIBUTE_RSSI_MIDDLE_5G = 178, + NAN_ATTRIBUTE_RSSI_PROXIMITY_5G = 179, + NAN_ATTRIBUTE_CONNMAP = 180, + NAN_ATTRIBUTE_24G_CHANNEL = 181, + NAN_ATTRIBUTE_5G_CHANNEL = 182, + NAN_ATTRIBUTE_DWELL_TIME = 183, + NAN_ATTRIBUTE_SCAN_PERIOD = 184, + NAN_ATTRIBUTE_RSSI_WINDOW_SIZE = 185, + NAN_ATTRIBUTE_CONF_CLUSTER_VAL = 186, + NAN_ATTRIBUTE_AVAIL_BIT_MAP = 187, + NAN_ATTRIBUTE_ENTRY_CONTROL = 188, + NAN_ATTRIBUTE_CIPHER_SUITE_TYPE = 189, + NAN_ATTRIBUTE_KEY_TYPE = 190, + NAN_ATTRIBUTE_KEY_LEN = 191, + NAN_ATTRIBUTE_SCID = 192, + NAN_ATTRIBUTE_SCID_LEN = 193, + NAN_ATTRIBUTE_SDE_CONTROL_CONFIG_DP = 194, + NAN_ATTRIBUTE_SDE_CONTROL_SECURITY = 195, + NAN_ATTRIBUTE_SDE_CONTROL_DP_TYPE = 196, + NAN_ATTRIBUTE_SDE_CONTROL_RANGE_SUPPORT = 197, + NAN_ATTRIBUTE_NO_CONFIG_AVAIL = 198, + NAN_ATTRIBUTE_2G_AWAKE_DW = 199, + NAN_ATTRIBUTE_5G_AWAKE_DW = 200, + NAN_ATTRIBUTE_RANGING_INTERVAL = 201, + NAN_ATTRIBUTE_RANGING_INDICATION = 202, + NAN_ATTRIBUTE_RANGING_INGRESS_LIMIT = 203, + NAN_ATTRIBUTE_RANGING_EGRESS_LIMIT = 204, + NAN_ATTRIBUTE_RANGING_AUTO_ACCEPT = 205, + NAN_ATTRIBUTE_RANGING_RESULT = 206, + NAN_ATTRIBUTE_DISC_IND_CFG = 207, + NAN_ATTRIBUTE_RSSI_THRESHOLD_FLAG = 208, + NAN_ATTRIBUTE_KEY_DATA = 209, + NAN_ATTRIBUTE_SDEA_SERVICE_SPECIFIC_INFO_LEN = 210, + NAN_ATTRIBUTE_SDEA_SERVICE_SPECIFIC_INFO = 211, + NAN_ATTRIBUTE_REASON = 212, + NAN_ATTRIBUTE_MATCH_OCCURRED_FLAG = 213, + NAN_ATTRIBUTE_OUT_OF_RESOURCE_FLAG = 214, + NAN_ATTRIBUTE_DWELL_TIME_5G = 215, + NAN_ATTRIBUTE_SCAN_PERIOD_5G = 216, + NAN_ATTRIBUTE_SVC_RESPONDER_POLICY = 217, + NAN_ATTRIBUTE_EVENT_MASK = 218, + NAN_ATTRIBUTE_SUB_SID_BEACON = 219, + NAN_ATTRIBUTE_RANDOMIZATION_INTERVAL = 220, + NAN_ATTRIBUTE_CMD_RESP_DATA = 221, + NAN_ATTRIBUTE_CMD_USE_NDPE = 222, + NAN_ATTRIBUTE_ENABLE_MERGE = 223, + NAN_ATTRIBUTE_DISCOVERY_BEACON_INTERVAL = 224, + NAN_ATTRIBUTE_NSS = 225, + NAN_ATTRIBUTE_ENABLE_RANGING = 226, + NAN_ATTRIBUTE_DW_EARLY_TERM = 227, + NAN_ATTRIBUTE_CHANNEL_INFO = 228, + NAN_ATTRIBUTE_NUM_CHANNELS = 229, + NAN_ATTRIBUTE_INSTANT_MODE_ENABLE = 230, + NAN_ATTRIBUTE_INSTANT_COMM_CHAN = 231 +} NAN_ATTRIBUTE; + +typedef enum { + NAN_REQUEST_ENABLE = 0, + NAN_REQUEST_DISABLE = 1, + NAN_REQUEST_PUBLISH = 2, + NAN_REQUEST_PUBLISH_CANCEL = 3, + NAN_REQUEST_TRANSMIT_FOLLOWUP = 4, + NAN_REQUEST_SUBSCRIBE = 5, + NAN_REQUEST_SUBSCRIBE_CANCEL = 6, + NAN_REQUEST_STATS = 7, + NAN_REQUEST_CONFIG = 8, + NAN_REQUEST_TCA = 9, + NAN_REQUEST_EVENT_CHECK = 10, + NAN_REQUEST_GET_CAPABILITIES = 11, + NAN_DATA_PATH_IFACE_CREATE = 12, + NAN_DATA_PATH_IFACE_DELETE = 13, + NAN_DATA_PATH_INIT_REQUEST = 14, + NAN_DATA_PATH_IND_RESPONSE = 15, + NAN_DATA_PATH_END = 16, + NAN_DATA_PATH_IFACE_UP = 17, + NAN_DATA_PATH_SEC_INFO = 18, + NAN_VERSION_INFO = 19, + NAN_REQUEST_LAST = 0xFFFF +} NanRequestType; + +/* + * The enum is based on the BCME Response defs + * used in the firmware and defined at + * path: src/include/bcmeutils.h + */ +enum nan_response_status { + BCME_OK = 0, + BCME_ERROR = -1, + BCME_BADARG = -2, + BCME_BADRATESET = -12, + BCME_BADBAND = -13, + BCME_BUSY = -16, + BCME_BADCHAN = -20, + BCME_UNSUPPORTED = -23, + BCME_BADLEN = -24, + BCME_NOTREADY = -25, + BCME_NOMEM = -27, + BCME_NOTFOUND = -30, + BCME_TXFAIL = -38, + BCME_RXFAIL = -39, + BCME_SCANREJECT = -43, + BCME_USAGE_ERROR = -44, + BCME_IOCTL_ERROR = -45 +}; + +enum nan_de_event_type { + NAN_EVENT_IFACE = 0, + NAN_EVENT_START = 1, + NAN_EVENT_JOIN = 2, + NAN_EVENT_ROLE_CHANGE = 3, + NAN_EVENT_MERGE = 4 +}; + +typedef struct _nan_hal_resp { + u16 instance_id; + u16 subcmd; + int32_t status; + int32_t value; + /* Identifier for the instance of the NDP */ + u16 ndp_instance_id; + /* Publisher NMI */ + u8 pub_nmi[NAN_MAC_ADDR_LEN]; + /* SVC_HASH */ + u8 svc_hash[NAN_SVC_HASH_SIZE]; + char nan_reason[NAN_ERROR_STR_LEN]; /* Describe the NAN reason type */ + char pad[3]; + NanCapabilities capabilities; +} nan_hal_resp_t; + +typedef int (*match_fn)(void *p1, void *data); + +typedef struct _nan_hal_info { + void *nan_handle; + void *nan_mac_control; + void *nan_disc_control; + void *nan_dp_control; +} nan_hal_info_t; + +u8 mNmi[NAN_MAC_ADDR_LEN]; +/* Static functions */ +static int is_de_event(int cmd); +static int is_dp_event(int cmd); +static int is_cmd_response(int cmd); + +static int get_svc_hash(unsigned char *svc_name, u16 svc_name_len, + u8 *svc_hash, u16 svc_hash_len); +NanResponseType get_response_type(WIFI_SUB_COMMAND nan_subcmd); +static NanStatusType nan_map_response_status(int vendor_status); + +/* Function to separate the common events to NAN1.0 events */ +static int is_de_event(int cmd) { + bool is_de_evt = false; + + switch(cmd) { + case NAN_EVENT_SUBSCRIBE_UNMATCH: + case NAN_EVENT_SUBSCRIBE_TERMINATED: + case NAN_EVENT_PUBLISH_TERMINATED: + case NAN_EVENT_SUBSCRIBE_MATCH: + case NAN_EVENT_FOLLOWUP: + case NAN_EVENT_TRANSMIT_FOLLOWUP_IND: + case NAN_EVENT_PUBLISH_REPLIED_IND: + case NAN_EVENT_MATCH_EXPIRY: + is_de_evt = true; + break; + default: + /* Not used */ + break; + } + return is_de_evt; +} + +/* Function to separate NAN2.0 events */ +static int is_dp_event(int cmd) { + bool is_dp_evt = false; + + switch(cmd) { + case NAN_EVENT_DATA_REQUEST: + case NAN_EVENT_DATA_CONFIRMATION: + case NAN_EVENT_DATA_END: + is_dp_evt = true; + break; + default: + /* Not used */ + break; + } + return is_dp_evt; +} + +static int is_cmd_response(int cmd) { + bool is_cmd_resp = false; + + switch(cmd) { + case NAN_ASYNC_RESPONSE_DISABLED: + is_cmd_resp = true; + break; + default: + break; + } + return is_cmd_resp; +} + +static NanStatusType nan_map_response_status (int vendor_status) { + NanStatusType hal_status; + + switch(vendor_status) { + case BCME_OK: + hal_status = NAN_STATUS_SUCCESS; + break; + case BCME_BUSY: + hal_status = NAN_STATUS_NO_RESOURCE_AVAILABLE; + break; + case BCME_NOTREADY: + hal_status = NAN_STATUS_NAN_NOT_ALLOWED; + break; + case BCME_BADLEN: + case BCME_BADBAND: + hal_status = NAN_STATUS_INVALID_PARAM; + break; + case BCME_NOMEM: + hal_status = NAN_STATUS_NO_RESOURCE_AVAILABLE; + break; + case NAN_STATUS_INTERNAL_FAILURE: + case NAN_STATUS_PROTOCOL_FAILURE: + case NAN_STATUS_INVALID_PUBLISH_SUBSCRIBE_ID: + case NAN_STATUS_NO_RESOURCE_AVAILABLE: + case NAN_STATUS_INVALID_PARAM: + case NAN_STATUS_INVALID_REQUESTOR_INSTANCE_ID: + case NAN_STATUS_INVALID_NDP_ID: + case NAN_STATUS_NAN_NOT_ALLOWED: + case NAN_STATUS_NO_OTA_ACK: + case NAN_STATUS_ALREADY_ENABLED: + case NAN_STATUS_FOLLOWUP_QUEUE_FULL: + case NAN_STATUS_UNSUPPORTED_CONCURRENCY_NAN_DISABLED: + hal_status = (NanStatusType)vendor_status; + break; + default: + ALOGE("%s Unknown vendor status, status = %d\n", + __func__, vendor_status); + /* Generic error */ + hal_status = NAN_STATUS_INTERNAL_FAILURE; + } + return hal_status; +} + +static void prhex(const char *msg, u8 *buf, u32 nbytes); +static const char *NanAttrToString(u16 cmd); +static const char *NanCmdToString(int cmd); +static const char *NanRspToString(int cmd); + +#define NAN_DBG_ENTER() {ALOGI("Enter: %s\n", __func__);} +#define NAN_DBG_EXIT() {ALOGI("Exit: %s\n", __func__);} + +static int passphrase_to_pmk(u8 *peer_mac, u32 cipher_type, + u8 *svc_hash, NanSecurityKeyInfo *key_info, u8 *pmk_hex) { + int result = NAN_STATUS_SUCCESS; + u8 salt[NAN_SECURITY_SALT_SIZE]; + + NAN_DBG_ENTER(); + salt[0] = 0; /* salt_version */ + salt[1] = cipher_type; + if (svc_hash && peer_mac) { + memcpy(&salt[2], svc_hash, NAN_SVC_HASH_SIZE); + memcpy(&salt[2 + NAN_SVC_HASH_SIZE], peer_mac, + ETHER_ADDR_LEN); + prhex("Salt", salt, NAN_SECURITY_SALT_SIZE); + } else { + ALOGE("Mandory parameters are not present\n"); + return WIFI_ERROR_INVALID_ARGS; + } + if (key_info->body.passphrase_info.passphrase_len < NAN_SECURITY_MIN_PASSPHRASE_LEN || + key_info->body.passphrase_info.passphrase_len > NAN_SECURITY_MAX_PASSPHRASE_LEN) { + ALOGE("passphrase must be between %d and %d characters long\n", + NAN_SECURITY_MIN_PASSPHRASE_LEN, + NAN_SECURITY_MAX_PASSPHRASE_LEN); + return WIFI_ERROR_INVALID_ARGS; + } + + result = PKCS5_PBKDF2_HMAC((const char *) key_info->body.passphrase_info.passphrase, + key_info->body.passphrase_info.passphrase_len, salt, sizeof(salt), + 4096, ((cipher_type == NAN_CIPHER_SUITE_SHARED_KEY_128_MASK) ? + (const EVP_MD *)EVP_sha256():(const EVP_MD *)EVP_sha384()), NAN_PMK_INFO_LEN, pmk_hex); + prhex("PMK_HEX", pmk_hex, 32); + NAN_DBG_EXIT(); + return result; +} + +typedef void *NanRequest; +nan_hal_info_t info; + +#define SVC_LIST(info) ((info).svc_list) +#define SVC_LIST_SIZE(info) ((info).svc_list.total_items) +#define DP_SVC_LIST(info) ((info).dp_svc_list) +#define DP_SVC_LIST_SIZE(info) ((info).dp_svc_list.total_items) +#define NAN_HANDLE(info) ((info).nan_handle) +#define GET_NAN_HANDLE(info) ((NanHandle *)info.nan_handle) +#define NAN_MAC_CONTROL(info) ((info).nan_mac_control) + +/////////////////////////////////////////////////////////////////////////////// +class NanHandle +{ + public: + NanCallbackHandler mHandlers; + NanHandle(wifi_handle handle, NanCallbackHandler handlers):mHandlers(handlers) + {} + +}; + +void HandleExpiryEvent(nan_hal_info_t info, nlattr *vendor_data) { + ALOGI("Received NAN_EVENT_MATCH_EXPIRY\n"); + u16 attr_type; + NanMatchExpiredInd expired_event; + memset(&expired_event, 0, sizeof(NanMatchExpiredInd)); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + if (attr_type == NAN_ATTRIBUTE_SUBSCRIBE_ID) { + expired_event.publish_subscribe_id = it.get_u16(); + ALOGI("pub_sub id = %u\n", + expired_event.publish_subscribe_id); + } else if (attr_type == NAN_ATTRIBUTE_PUBLISH_ID) { + expired_event.requestor_instance_id = it.get_u32(); + ALOGI("req_inst id = %u\n", expired_event.requestor_instance_id); + } + } + + if (expired_event.requestor_instance_id && expired_event.publish_subscribe_id) { + GET_NAN_HANDLE(info)->mHandlers.EventMatchExpired(&expired_event); + } else { + ALOGE("Invalid values for notifying the expired event, dropping the event\n"); + } +} + +/////////////////////////////////////////////////////////////////////////////// +class NanDiscEnginePrimitive : public WifiCommand +{ + NanRequest mParams; + NanRequestType mType; + u16 mInstId; + u32 mPeerId; + u16 mTxId; + + public: + NanDiscEnginePrimitive(wifi_interface_handle iface, int id, + NanRequest params, NanRequestType cmdType) + : WifiCommand("NanCommand", iface, id), mParams(params), mType(cmdType) + { + mInstId = 0; + mPeerId = 0; + setTransactionId(id); + } + + ~NanDiscEnginePrimitive() { + ALOGE("NanDiscEnginePrimitive destroyed\n"); + } + + void setType(NanRequestType type) { + mType = type; + } + + void setInstId(u16 inst_id) { + mInstId = inst_id; + } + + int getInstanceId() { + return mInstId; + } + + void setTransactionId(u16 tx_id) { + mTxId = tx_id; + } + + int getTransactionId() { + return mTxId; + } + + void setParams(NanRequest params) { + mParams = params; + } + + int createRequest(WifiRequest& request) + { + ALOGI("NAN CMD: %s\n", NanCmdToString(mType)); + if (mType == NAN_REQUEST_SUBSCRIBE) { + return createSubscribeRequest(request, + (NanSubscribeRequest *)mParams); + } else if (mType == NAN_REQUEST_SUBSCRIBE_CANCEL) { + return createSubscribeCancelRequest(request, + (NanSubscribeCancelRequest *)mParams); + } else if (mType == NAN_REQUEST_PUBLISH) { + return createPublishRequest(request, + (NanPublishRequest *)mParams); + } else if (mType == NAN_REQUEST_PUBLISH_CANCEL) { + return createPublishCancelRequest(request, + (NanPublishCancelRequest *)mParams); + } else if (mType == NAN_REQUEST_TRANSMIT_FOLLOWUP) { + return createTransmitFollowupRequest(request, + (NanTransmitFollowupRequest *)mParams); + } else if (mType == NAN_REQUEST_GET_CAPABILITIES) { + return getCapabilitiesRequest(request); + } else { + ALOGE("%s Unknown Nan request\n", __func__); + } + return WIFI_SUCCESS; + } + + int createPublishRequest(WifiRequest& request, NanPublishRequest *mParams) + { + NAN_DBG_ENTER(); + u8 pmk_hex[NAN_PMK_INFO_LEN]; + int result = request.create(GOOGLE_OUI, NAN_SUBCMD_PUBLISH); + if (result < 0) { + ALOGE("%s Failed to create request, result = %d\n", __func__, result); + return result; + } + + /* If handle is 0xFFFF, then update instance_id in response of this request + * otherwise, update not needed + */ + mInstId = mParams->publish_id; + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + result = request.put_u32(NAN_ATTRIBUTE_PUBLISH_ID, mInstId); + if (result < 0) { + ALOGE("%s: Failed to fill pub id, result = %d\n", __func__, result); + return result; + } + + result = request.put_u16(NAN_ATTRIBUTE_TTL, mParams->ttl); + if (result < 0) { + ALOGE("%s: Failed to fill ttl, result = %d\n", __func__, result); + return result; + } + + if (ISGREATER(mParams->period, NAN_MAX_PERIOD)) { + ALOGE("%s:Invalid period value.\n", __FUNCTION__); + return WIFI_ERROR_NOT_SUPPORTED; + } + result = request.put_u16(NAN_ATTRIBUTE_PERIOD, mParams->period); + if (result < 0) { + ALOGE("%s: Failed to fill period, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_PUBLISH_TYPE, mParams->publish_type); + if (result < 0) { + ALOGE("%s: Failed to fill pub type, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_TX_TYPE, mParams->tx_type); + if (result < 0) { + ALOGE("%s: Failed to fill tx type, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_PUBLISH_COUNT, mParams->publish_count); + if (result < 0) { + ALOGE("%s: Failed to fill pub cnt, result = %d\n", __func__, result); + return result; + } + + if (mParams->service_name_len) { + u8 svc_hash[NAN_SVC_HASH_SIZE]; + + result = get_svc_hash(mParams->service_name, mParams->service_name_len, + svc_hash, NAN_SVC_HASH_SIZE); + if (result < 0) { + ALOGE("%s: Failed to get hashed svc name\n", __func__); + return result; + } + + mParams->service_name_len = NAN_SVC_HASH_SIZE; + memcpy(mParams->service_name, svc_hash, mParams->service_name_len); + + result = request.put_u16(NAN_ATTRIBUTE_SERVICE_NAME_LEN, mParams->service_name_len); + if (result < 0) { + ALOGE("%s: Failed to fill svc name len, result = %d\n", __func__, result); + return result; + } + + result = request.put(NAN_ATTRIBUTE_SERVICE_NAME, (void *)mParams->service_name, + mParams->service_name_len); + if (result < 0) { + ALOGE("%s: Failed to fill svc name, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->service_specific_info_len) { + result = request.put_u16(NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN, + mParams->service_specific_info_len); + if (result < 0) { + ALOGE("%s: Failed to fill svc info len, result = %d\n", __func__, result); + return result; + } + + result = request.put(NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO, + (void *)mParams->service_specific_info, mParams->service_specific_info_len); + if (result < 0) { + ALOGE("%s: Failed to fill svc info, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->rx_match_filter_len) { + result = request.put_u16(NAN_ATTRIBUTE_RX_MATCH_FILTER_LEN, + mParams->rx_match_filter_len); + if (result < 0) { + ALOGE("%s: Failed to fill rx match filter len, result = %d\n", + __func__, result); + return result; + } + + prhex(NULL, mParams->rx_match_filter, mParams->rx_match_filter_len); + result = request.put(NAN_ATTRIBUTE_RX_MATCH_FILTER, + (void *)mParams->rx_match_filter, mParams->rx_match_filter_len); + if (result < 0) { + ALOGE("%s: Failed to fill rx match filter, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->tx_match_filter_len) { + result = request.put_u16(NAN_ATTRIBUTE_TX_MATCH_FILTER_LEN, + mParams->tx_match_filter_len); + if (result < 0) { + ALOGE("%s: Failed to fill tx match filter, result = %d\n", __func__, result); + return result; + } + + prhex(NULL, mParams->tx_match_filter, mParams->tx_match_filter_len); + result = request.put(NAN_ATTRIBUTE_TX_MATCH_FILTER, + (void *)mParams->tx_match_filter, mParams->tx_match_filter_len); + if (result < 0) { + ALOGE("%s: Failed to fill tx match filter, result = %d\n", + __func__, result); + return result; + } + } + + result = request.put_u8(NAN_ATTRIBUTE_PUBLISH_MATCH, mParams->publish_match_indicator); + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_PUBLISH_MATCH, result = %d\n", + __func__, result); + return result; + } + + if (ISGREATER(mParams->recv_indication_cfg, NAN_PUB_RECV_FLAG_MAX)) { + ALOGE("%s:Invalid recv_flag value.\n", __FUNCTION__); + return WIFI_ERROR_NOT_SUPPORTED; + } + result = request.put_u8(NAN_ATTRIBUTE_RECV_IND_CFG, + mParams->recv_indication_cfg); + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_RECV_IND_CFG, result = %d\n", + __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_CIPHER_SUITE_TYPE, + mParams->cipher_type); + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_CIPHER_SUITE_TYPE, result = %d\n", + __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_KEY_TYPE, + mParams->key_info.key_type); + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_KEY_TYPE, result = %d\n", + __func__, result); + return result; + } + + if (mParams->key_info.key_type == NAN_SECURITY_KEY_INPUT_PMK) { + if (mParams->key_info.body.pmk_info.pmk_len) { + result = request.put_u32(NAN_ATTRIBUTE_KEY_LEN, + mParams->key_info.body.pmk_info.pmk_len); + if (result < 0) { + ALOGE("%s: Failed to fill pmk len, result = %d\n", __func__, result); + return result; + } + result = request.put(NAN_ATTRIBUTE_KEY_DATA, + (void *)mParams->key_info.body.pmk_info.pmk, + mParams->key_info.body.pmk_info.pmk_len); + if (result < 0) { + ALOGE("%s: Failed to fill pmk, result = %d\n", __func__, result); + return result; + } + } + } + + if (mParams->key_info.key_type == NAN_SECURITY_KEY_INPUT_PASSPHRASE) { + if (mParams->key_info.body.passphrase_info.passphrase_len < NAN_SECURITY_MIN_PASSPHRASE_LEN || + mParams->key_info.body.passphrase_info.passphrase_len > NAN_SECURITY_MAX_PASSPHRASE_LEN) { + ALOGE("passphrase must be between %d and %d characters long\n", + NAN_SECURITY_MIN_PASSPHRASE_LEN, + NAN_SECURITY_MAX_PASSPHRASE_LEN); + return NAN_STATUS_INVALID_PARAM; + } else { + memset(pmk_hex, 0, NAN_PMK_INFO_LEN); + result = passphrase_to_pmk(mNmi, mParams->cipher_type, + mParams->service_name, &mParams->key_info, pmk_hex); + if (result < 0) { + ALOGE("%s: Failed to convert passphrase to key data, result = %d\n", __func__, result); + return result; + } + result = request.put_u32(NAN_ATTRIBUTE_KEY_LEN, NAN_PMK_INFO_LEN); + if (result < 0) { + ALOGE("%s: Failed to fill passphrase len, result = %d\n", __func__, result); + return result; + } + result = request.put(NAN_ATTRIBUTE_KEY_DATA, pmk_hex, NAN_PMK_INFO_LEN); + if (result < 0) { + ALOGE("%s: Failed to fill passphrase, result = %d\n", __func__, result); + return result; + } + } + } + + if (mParams->scid_len) { + if ((mParams->scid_len > NAN_MAX_SCID_BUF_LEN) || + (mParams->scid_len % NAN_SCID_INFO_LEN)) { + ALOGE("%s: Invalid scid len, = %d\n", __func__, mParams->scid_len); + return NAN_STATUS_INVALID_PARAM; + } + result = request.put_u32(NAN_ATTRIBUTE_SCID_LEN, + mParams->scid_len); + if (result < 0) { + ALOGE("%s: Failed to fill scid len, result = %d\n", __func__, result); + return result; + } + + prhex(NULL, mParams->scid, mParams->scid_len); + result = request.put(NAN_ATTRIBUTE_SCID, + (void *)mParams->scid, mParams->scid_len); + if (result < 0) { + ALOGE("%s: Failed to fill scid, result = %d\n", __func__, result); + return result; + } + } + + result = request.put_u8(NAN_ATTRIBUTE_SDE_CONTROL_CONFIG_DP, + mParams->sdea_params.config_nan_data_path); + + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_SDE_CONTROL_CONFIG_DP, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_SDE_CONTROL_SECURITY, + mParams->sdea_params.security_cfg); + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_SDE_CONTROL_SECURITY, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_SDE_CONTROL_DP_TYPE, + mParams->sdea_params.ndp_type); + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_SDE_CONTROL_DP_TYPE, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_SDE_CONTROL_RANGE_SUPPORT, + mParams->sdea_params.ranging_state); + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_SDE_CONTROL_RANGE_SUPPORT, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_RSSI_THRESHOLD_FLAG, + mParams->rssi_threshold_flag); + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_RSSI_THRESHOLD_FLAG, result = %d\n", + __func__, result); + return result; + } + + if (mParams->sdea_service_specific_info_len) { + result = request.put_u16(NAN_ATTRIBUTE_SDEA_SERVICE_SPECIFIC_INFO_LEN, + mParams->sdea_service_specific_info_len); + if (result < 0) { + ALOGE("%s: Failed to fill sdea svc info len, result = %d\n", __func__, result); + return result; + } + + prhex(NULL, mParams->sdea_service_specific_info, mParams->sdea_service_specific_info_len); + result = request.put(NAN_ATTRIBUTE_SDEA_SERVICE_SPECIFIC_INFO, + (void *)mParams->sdea_service_specific_info, mParams->sdea_service_specific_info_len); + if (result < 0) { + ALOGE("%s: Failed to fill sdea svc info, result = %d\n", __func__, result); + return result; + } + } + + result = request.put_u8(NAN_ATTRIBUTE_SVC_RESPONDER_POLICY, + mParams->service_responder_policy); + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_SVC_RESPONDER_POLICY, result = %d\n", + __func__, result); + return result; + } + + request.attr_end(data); + + ALOGI("Returning successfully\n"); + NAN_DBG_EXIT(); + return result; + } + + int createPublishCancelRequest(WifiRequest& request, NanPublishCancelRequest *mParams) + { + int result = request.create(GOOGLE_OUI, NAN_SUBCMD_PUBLISH_CANCEL); + if (result < 0) { + ALOGE("%s: Failed to create request, result = %d\n", __func__, result); + return result; + } + + NAN_DBG_ENTER(); + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + if (ISGREATER(mInstId, NAN_MAX)) { + ALOGE("%s:Invalid publish count value.\n", __FUNCTION__); + return WIFI_ERROR_NOT_SUPPORTED; + } + ALOGI("%s: pub id = %d, inst_id = %d\n", __func__, mParams->publish_id, mInstId); + + result = request.put_u32(NAN_ATTRIBUTE_PUBLISH_ID, mInstId); + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_PUBLISH_ID, result = %d\n", + __func__, result); + return result; + } + request.attr_end(data); + NAN_DBG_EXIT(); + return WIFI_SUCCESS; + } + + int createSubscribeRequest(WifiRequest& request, NanSubscribeRequest *mParams) + { + int result = request.create(GOOGLE_OUI, NAN_SUBCMD_SUBSCRIBE); + if (result < 0) { + ALOGE("%s Failed to create request\n", __func__); + return result; + } + + NAN_DBG_ENTER(); + + /* If handle is 0xFFFF, then update instance_id in response of this request + * otherwise, update not needed + */ + mInstId = mParams->subscribe_id; + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + result = request.put_u16(NAN_ATTRIBUTE_SUBSCRIBE_ID, mInstId); + if (result < 0) { + ALOGE("%s: Failed to fill sub id, result = %d\n", __func__, result); + return result; + } + + result = request.put_u16(NAN_ATTRIBUTE_TTL, mParams->ttl); + if (result < 0) { + ALOGE("%s: Failed to fill ttl, result = %d\n", __func__, result); + return result; + } + + if (ISGREATER(mParams->period, NAN_MAX_PERIOD)) { + ALOGE("%s:Invalid period value.\n", __FUNCTION__); + return WIFI_ERROR_NOT_SUPPORTED; + } + result = request.put_u16(NAN_ATTRIBUTE_PERIOD, mParams->period); + if (result < 0) { + ALOGE("%s: Failed to fill period, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_SUBSCRIBE_TYPE, mParams->subscribe_type); + if (result < 0) { + ALOGE("%s: Failed to fill sub type, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_SERVICERESPONSEFILTER, + mParams->serviceResponseFilter); + if (result < 0) { + ALOGE("%s: Failed to fill svc resp filter, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_SERVICERESPONSEINCLUDE, + mParams->serviceResponseInclude); + if (result < 0) { + ALOGE("%s: Failed to fill svc resp include, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_USESERVICERESPONSEFILTER, + mParams->useServiceResponseFilter); + if (result < 0) { + ALOGE("%s: Failed to fill use svc resp filter, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_SSIREQUIREDFORMATCHINDICATION, + mParams->ssiRequiredForMatchIndication); + if (result < 0) { + ALOGE("%s: Failed to fill ssi req match ind, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_SUBSCRIBE_MATCH, + mParams->subscribe_match_indicator); + if (result < 0) { + ALOGE("%s: Failed to fill sub match, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_SUBSCRIBE_COUNT, mParams->subscribe_count); + if (result < 0) { + ALOGE("%s: Failed to fill sub cnt, result = %d\n", __func__, result); + return result; + } + + if (mParams->service_name_len) { + u8 svc_hash[NAN_SVC_HASH_SIZE]; + + result = get_svc_hash(mParams->service_name, mParams->service_name_len, + svc_hash, NAN_SVC_HASH_SIZE); + if (result < 0) { + ALOGE("%s: Failed to get hashed svc name\n", __func__); + return result; + } + + mParams->service_name_len = NAN_SVC_HASH_SIZE; + memcpy(mParams->service_name, svc_hash, mParams->service_name_len); + + result = request.put_u16(NAN_ATTRIBUTE_SERVICE_NAME_LEN, mParams->service_name_len); + if (result < 0) { + ALOGE("%s: Failed to fill svc hash len, result = %d\n", + __func__, result); + return result; + } + + result = request.put(NAN_ATTRIBUTE_SERVICE_NAME, (void *)mParams->service_name, + mParams->service_name_len); + if (result < 0) { + ALOGE("%s: Failed to fill hashed svc name, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->service_specific_info_len) { + result = request.put_u16(NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN, + mParams->service_specific_info_len); + if (result < 0) { + ALOGE("%s: Failed to fill svc info len, result = %d\n", __func__, result); + return result; + } + + result = request.put(NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO, + (void *)mParams->service_specific_info, mParams->service_specific_info_len); + if (result < 0) { + ALOGE("%s: Failed to fill svc info, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->rx_match_filter_len) { + result = request.put_u16(NAN_ATTRIBUTE_RX_MATCH_FILTER_LEN, + mParams->rx_match_filter_len); + if (result < 0) { + ALOGE("%s: Failed to fill rx match filter len, result = %d\n", __func__, result); + return result; + } + + prhex(NULL, mParams->rx_match_filter, mParams->rx_match_filter_len); + result = request.put(NAN_ATTRIBUTE_RX_MATCH_FILTER, + (void *)mParams->rx_match_filter, mParams->rx_match_filter_len); + if (result < 0) { + ALOGE("%s: Failed to fill rx match filter, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->tx_match_filter_len) { + result = request.put_u16(NAN_ATTRIBUTE_TX_MATCH_FILTER_LEN, + mParams->tx_match_filter_len); + if (result < 0) { + ALOGE("%s: Failed to fill tx match filter len, result = %d\n", __func__, result); + return result; + } + + prhex(NULL, mParams->tx_match_filter, mParams->tx_match_filter_len); + result = request.put(NAN_ATTRIBUTE_TX_MATCH_FILTER, + (void *)mParams->tx_match_filter, mParams->tx_match_filter_len); + if (result < 0) { + ALOGE("%s: Failed to fill tx match filter, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->num_intf_addr_present > NAN_MAX_SUBSCRIBE_MAX_ADDRESS) { + ALOGE("%s: Number of mac addrs: %d have crossed the threshold, fail to subscribe\n", + __func__, mParams->num_intf_addr_present); + return WIFI_ERROR_NOT_SUPPORTED; + } else if (mParams->num_intf_addr_present) { + result = request.put_u16(NAN_ATTRIBUTE_MAC_ADDR_LIST_NUM_ENTRIES, + mParams->num_intf_addr_present); + if (result < 0) { + ALOGE("%s: Failed to fill mac addr list no, result = %d\n", + __func__, result); + return result; + } + + prhex(NULL, (u8 *)mParams->intf_addr, + (mParams->num_intf_addr_present * NAN_MAC_ADDR_LEN)); + result = request.put(NAN_ATTRIBUTE_MAC_ADDR_LIST, (void *)mParams->intf_addr, + (mParams->num_intf_addr_present * NAN_MAC_ADDR_LEN)); + if (result < 0) { + ALOGE("%s: Failed to fill mac addr list, result = %d\n", __func__, result); + return result; + } + } + + if (ISGREATER(mParams->recv_indication_cfg, NAN_SUB_RECV_FLAG_MAX)) { + ALOGE("%s:Invalid recv_flag value.\n", __FUNCTION__); + return WIFI_ERROR_NOT_SUPPORTED; + } + result = request.put_u8(NAN_ATTRIBUTE_RECV_IND_CFG, + mParams->recv_indication_cfg); + if (result < 0) { + ALOGE("%s: Failed to fill recv_indication_cfg, result = %d\n", + __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_CIPHER_SUITE_TYPE, + mParams->cipher_type); + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_CIPHER_SUITE_TYPE, result = %d\n", + __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_KEY_TYPE, + mParams->key_info.key_type); + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_KEY_TYPE, result = %d\n", + __func__, result); + return result; + } + + if (mParams->key_info.key_type == NAN_SECURITY_KEY_INPUT_PMK) { + if (mParams->key_info.body.pmk_info.pmk_len) { + result = request.put_u32(NAN_ATTRIBUTE_KEY_LEN, + mParams->key_info.body.pmk_info.pmk_len); + if (result < 0) { + ALOGE("%s: Failed to fill pmk len, result = %d\n", __func__, result); + return result; + } + result = request.put(NAN_ATTRIBUTE_KEY_DATA, + (void *)mParams->key_info.body.pmk_info.pmk, + mParams->key_info.body.pmk_info.pmk_len); + if (result < 0) { + ALOGE("%s: Failed to fill pmk, result = %d\n", __func__, result); + return result; + } + } + } + + if (mParams->scid_len) { + if (mParams->scid_len != NAN_SCID_INFO_LEN) { + ALOGE("%s: Invalid scid len, = %d\n", __func__, mParams->scid_len); + return NAN_STATUS_INVALID_PARAM; + } + result = request.put_u32(NAN_ATTRIBUTE_SCID_LEN, + mParams->scid_len); + if (result < 0) { + ALOGE("%s: Failed to fill scid len, result = %d\n", __func__, result); + return result; + } + + result = request.put(NAN_ATTRIBUTE_SCID, + (void *)mParams->scid, mParams->scid_len); + if (result < 0) { + ALOGE("%s: Failed to fill scid, result = %d\n", __func__, result); + return result; + } + } + + result = request.put_u8(NAN_ATTRIBUTE_SDE_CONTROL_CONFIG_DP, + mParams->sdea_params.config_nan_data_path); + if (result < 0) { + ALOGE("%s: Failed to fill config_nan_data_path, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_SDE_CONTROL_SECURITY, + mParams->sdea_params.security_cfg); + if (result < 0) { + ALOGE("%s: Failed to fill security_cfg, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_SDE_CONTROL_DP_TYPE, + mParams->sdea_params.ndp_type); + if (result < 0) { + ALOGE("%s: Failed to fill ndp_type, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_SDE_CONTROL_RANGE_SUPPORT, + mParams->sdea_params.ranging_state); + if (result < 0) { + ALOGE("%s: Failed to fill ranging state, result = %d\n", __func__, result); + return result; + } + + if (mParams->sdea_params.ranging_state == NAN_RANGING_ENABLE) { + result = request.put_u32(NAN_ATTRIBUTE_RANGING_INTERVAL, + mParams->ranging_cfg.ranging_interval_msec); + if (result < 0) { + ALOGE("%s: Failed to fill ranging_interval_msec, result = %d\n", __func__, result); + return result; + } + + result = request.put_u32(NAN_ATTRIBUTE_RANGING_EGRESS_LIMIT, + mParams->ranging_cfg.distance_egress_mm); + if (result < 0) { + ALOGE("%s: Failed to fill distance_egress_mm, result = %d\n", __func__, result); + return result; + } + + result = request.put_u32(NAN_ATTRIBUTE_RANGING_INDICATION, + mParams->ranging_cfg.config_ranging_indications); + if (result < 0) { + ALOGE("%s: Failed to fill config_ranging_indications, result = %d\n", __func__, result); + return result; + } + + result = request.put_u32(NAN_ATTRIBUTE_RANGING_INGRESS_LIMIT, + mParams->ranging_cfg.distance_ingress_mm); + if (result < 0) { + ALOGE("%s: Failed to fill distance_ingress_mm, result = %d\n", __func__, result); + return result; + } + } + + ALOGI("%s:RSSI threshold flag %d", __func__, mParams->rssi_threshold_flag); + result = request.put_u8(NAN_ATTRIBUTE_RSSI_THRESHOLD_FLAG, + mParams->rssi_threshold_flag); + if (result < 0) { + ALOGE("%s: Failed to fill rssi_threshold_flag, result = %d\n", + __func__, result); + return result; + } + + if (mParams->sdea_service_specific_info_len) { + result = request.put_u16(NAN_ATTRIBUTE_SDEA_SERVICE_SPECIFIC_INFO_LEN, + mParams->sdea_service_specific_info_len); + if (result < 0) { + ALOGE("%s: Failed to fill sdea svc info len, result = %d\n", __func__, result); + return result; + } + + prhex(NULL, mParams->sdea_service_specific_info, mParams->sdea_service_specific_info_len); + result = request.put(NAN_ATTRIBUTE_SDEA_SERVICE_SPECIFIC_INFO, + (void *)mParams->sdea_service_specific_info, mParams->sdea_service_specific_info_len); + if (result < 0) { + ALOGE("%s: Failed to fill sdea svc info, result = %d\n", __func__, result); + return result; + } + } + + request.attr_end(data); + NAN_DBG_EXIT(); + return WIFI_SUCCESS; + } + + int createSubscribeCancelRequest(WifiRequest& request, + NanSubscribeCancelRequest *mParams) { + int result = request.create(GOOGLE_OUI, NAN_SUBCMD_SUBSCRIBE_CANCEL); + if (result < 0) { + ALOGE("%s Failed to create request \n", __func__); + return result; + } + + NAN_DBG_ENTER(); + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + if (ISGREATER(mInstId, NAN_MAX)) { + ALOGE("%s:Invalid subscribe id value.\n", __FUNCTION__); + return WIFI_ERROR_NOT_SUPPORTED; + } + ALOGI("%s: sub id = %u\n", __func__, mInstId); + + result = request.put_u16(NAN_ATTRIBUTE_SUBSCRIBE_ID, mInstId); + if (result < 0) { + ALOGE("%s: Failed to fill sub id, result = %d\n", __func__, result); + return result; + } + + request.attr_end(data); + NAN_DBG_EXIT(); + return WIFI_SUCCESS; + } + + int createTransmitFollowupRequest(WifiRequest& request, + NanTransmitFollowupRequest *mParams) + { + int result = request.create(GOOGLE_OUI, NAN_SUBCMD_TRANSMIT_FOLLOWUP); + if (result < 0) { + ALOGE("%s Failed to create request \n", __func__); + return result; + } + + NAN_DBG_ENTER(); + + /* If handle is 0xFFFF, then update instance_id in response of this request + * otherwise, update not needed + */ + mInstId = mParams->publish_subscribe_id; + mPeerId = mParams->requestor_instance_id; + mTxId = getTransactionId(); + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + result = request.put_u32(NAN_ATTRIBUTE_PEER_ID, mPeerId); + if (result < 0) { + ALOGE("%s: Failed to fill peer id, result = %d\n", __func__, result); + return result; + } + + result = request.put_u16(NAN_ATTRIBUTE_INST_ID, mInstId); + if (result < 0) { + ALOGE("%s Failed to fill inst id = %d \n", __func__, mInstId); + return result; + } + + result = request.put_addr(NAN_ATTRIBUTE_MAC_ADDR, mParams->addr); + if (result < 0) { + ALOGE("%s: Failed to fill mac addr\n", __func__); + return result; + } + + if (mParams->service_specific_info_len > 0) { + result = request.put_u16(NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN, + mParams->service_specific_info_len); + if (result < 0) { + ALOGE("%s: Failed to fill svc info len \n", __func__); + return result; + } + + prhex(NULL, mParams->service_specific_info, mParams->service_specific_info_len); + result = request.put(NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO, + (void *)mParams->service_specific_info, mParams->service_specific_info_len); + if (result < 0) { + ALOGE("%s: Failed to put svc info, result = %d", __func__, result); + return result; + } + mParams->service_specific_info[mParams->service_specific_info_len] = '\0'; + ALOGI("Transmit service info string is %s\n", mParams->service_specific_info); + } + + if (ISGREATER(mParams->recv_indication_cfg, NAN_PUB_RECV_FLAG_MAX)) { + ALOGE("%s:Invalid recv_flag value.\n", __FUNCTION__); + return WIFI_ERROR_NOT_SUPPORTED; + } + + result = request.put_u8(NAN_ATTRIBUTE_RECV_IND_CFG, + mParams->recv_indication_cfg); + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_RECV_IND_CFG, result = %d\n", + __func__, result); + return result; + } + result = request.put_u16(NAN_ATTRIBUTE_TRANSAC_ID, mTxId); + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_TRANSAC_ID, result = %d\n", + __func__, result); + return result; + } + + if (mParams->sdea_service_specific_info_len) { + result = request.put_u16(NAN_ATTRIBUTE_SDEA_SERVICE_SPECIFIC_INFO_LEN, + mParams->sdea_service_specific_info_len); + if (result < 0) { + ALOGE("%s: Failed to fill sdea svc info len, result = %d\n", __func__, result); + return result; + } + + prhex(NULL, mParams->sdea_service_specific_info, mParams->sdea_service_specific_info_len); + result = request.put(NAN_ATTRIBUTE_SDEA_SERVICE_SPECIFIC_INFO, + (void *)mParams->sdea_service_specific_info, mParams->sdea_service_specific_info_len); + if (result < 0) { + ALOGE("%s: Failed to fill sdea svc info, result = %d\n", __func__, result); + return result; + } + } + + request.attr_end(data); + NAN_DBG_EXIT(); + return WIFI_SUCCESS; + } + + int getCapabilitiesRequest(WifiRequest& request) { + int result = 0; + NAN_DBG_ENTER(); + + result = request.create(GOOGLE_OUI, NAN_SUBCMD_GET_CAPABILITIES); + if (result < 0) { + ALOGE("%s Failed to create request \n", __func__); + return result; + } + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + request.attr_end(data); + + NAN_DBG_EXIT(); + return WIFI_SUCCESS; + } + + int start() + { + int result = 0; + WifiRequest request(familyId(), ifaceId()); + result = createRequest(request); + if (result != WIFI_SUCCESS) { + ALOGE("%s: Failed to create setup request; result = %d\n", __func__, result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("%s: Failed to configure setup; result = %d\n", __func__, result); + return result; + } + + request.destroy(); + return WIFI_SUCCESS; + } + + virtual bool valid_disc_response_type(int response_type) { + bool valid = false; + switch(response_type) { + case NAN_RESPONSE_PUBLISH: + case NAN_RESPONSE_SUBSCRIBE: + case NAN_GET_CAPABILITIES: + case NAN_RESPONSE_PUBLISH_CANCEL: + case NAN_RESPONSE_SUBSCRIBE_CANCEL: + case NAN_RESPONSE_TRANSMIT_FOLLOWUP: + valid = true; + break; + default: + ALOGE("NanDiscEnginePrmitive:Unknown cmd Response: %d\n", response_type); + break; + } + return valid; + } + + int handleResponse(WifiEvent& reply) + { + nan_hal_resp_t *rsp_vndr_data = NULL; + NanResponseMsg rsp_data; + u32 len; + if (reply.get_cmd() != NL80211_CMD_VENDOR || reply.get_vendor_data() == NULL) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + rsp_vndr_data = (nan_hal_resp_t *)reply.get_vendor_data(); + len = reply.get_vendor_data_len(); + ALOGI("NanDiscEnginePrmitive::handle response\n"); + memset(&rsp_data, 0, sizeof(NanResponseMsg)); + rsp_data.response_type = get_response_type((WIFI_SUB_COMMAND)rsp_vndr_data->subcmd); + if (!valid_disc_response_type(rsp_data.response_type)) + return NL_SKIP; + + rsp_data.status = nan_map_response_status(rsp_vndr_data->status); + ALOGE("Mapped hal status = %d\n", rsp_data.status); + if (rsp_vndr_data->nan_reason[0] == '\0') { + memcpy(rsp_data.nan_error, NanStatusToString(rsp_data.status), + strlen(NanStatusToString(rsp_data.status))); + rsp_data.nan_error[strlen(NanStatusToString(rsp_data.status))] = '\0'; + } + rsp_data.nan_error[NAN_ERROR_STR_LEN - 1] = '\0'; + ALOGI("\n Received nan_error string %s\n", (u8*)rsp_data.nan_error); + + if (mInstId == 0 && + (rsp_data.response_type == NAN_RESPONSE_PUBLISH || + rsp_data.response_type == NAN_RESPONSE_SUBSCRIBE)) { + ALOGI("Received service instance_id %d\n", rsp_vndr_data->instance_id); + mInstId = rsp_vndr_data->instance_id; + } + + if (rsp_data.response_type == NAN_RESPONSE_PUBLISH) { + rsp_data.body.publish_response.publish_id = mInstId; + } else if (rsp_data.response_type == NAN_RESPONSE_SUBSCRIBE) { + rsp_data.body.subscribe_response.subscribe_id = mInstId; + } else if (rsp_data.response_type == NAN_GET_CAPABILITIES) { + memcpy((void *)&rsp_data.body.nan_capabilities, (void *)&rsp_vndr_data->capabilities, + min(len, sizeof(rsp_data.body.nan_capabilities))); + } + + GET_NAN_HANDLE(info)->mHandlers.NotifyResponse(id(), &rsp_data); + ALOGI("NanDiscEnginePrmitive:Received response for cmd [%s], ret %d\n", + NanRspToString(rsp_data.response_type), rsp_data.status); + + return NL_SKIP; + } + + int handleEvent(WifiEvent& event) { + int cmd = event.get_vendor_subcmd(); + u16 attr_type; + + ALOGI("Received NanDiscEnginePrimitive event: %d\n", event.get_cmd()); + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + + switch(cmd) { + case NAN_EVENT_PUBLISH_TERMINATED: + NanPublishTerminatedInd pub_term_event; + + memset(&pub_term_event, 0, sizeof(NanPublishTerminatedInd)); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (attr_type == NAN_ATTRIBUTE_PUBLISH_ID) { + pub_term_event.publish_id = it.get_u32(); + ALOGI("pub id = %u", pub_term_event.publish_id); + } else if (attr_type == NAN_ATTRIBUTE_STATUS) { + pub_term_event.reason = (NanStatusType)it.get_u8(); + ALOGI("pub termination status %u", pub_term_event.reason); + } else if (attr_type == NAN_ATTRIBUTE_REASON) { + u8 len = min(it.get_len(), sizeof(pub_term_event.nan_reason)); + memcpy(pub_term_event.nan_reason, it.get_data(), len); + ALOGI("pub termination reason: %s, len = %d\n", + pub_term_event.nan_reason, len); + } else { + ALOGE("Unknown attr: %u\n", attr_type); + } + } + + GET_NAN_HANDLE(info)->mHandlers.EventPublishTerminated(&pub_term_event); + break; + + case NAN_EVENT_SUBSCRIBE_MATCH: + NanMatchInd subscribe_event; + + memset(&subscribe_event, 0, sizeof(NanMatchInd)); + + /* By default FW is unable to cache this match */ + subscribe_event.out_of_resource_flag = true; + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (attr_type == NAN_ATTRIBUTE_SUBSCRIBE_ID) { + ALOGI("sub id: %u", it.get_u16()); + subscribe_event.publish_subscribe_id = it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_PUBLISH_ID) { + ALOGI("pub id: %u", it.get_u32()); + subscribe_event.requestor_instance_id = it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_MAC_ADDR) { + memcpy(subscribe_event.addr, it.get_data(), NAN_MAC_ADDR_LEN); + ALOGI("Publisher mac: " MACSTR, MAC2STR(subscribe_event.addr)); + } else if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN) { + ALOGI("svc length %d", it.get_u16()); + subscribe_event.service_specific_info_len = it.get_u16(); + } else if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO) { + memcpy(subscribe_event.service_specific_info, it.get_data(), + subscribe_event.service_specific_info_len); + subscribe_event.service_specific_info + [subscribe_event.service_specific_info_len] = '\0'; + ALOGI("service info: %s", subscribe_event.service_specific_info); + } else if (attr_type == NAN_ATTRIBUTE_TX_MATCH_FILTER_LEN) { + ALOGI("sdf match filter length: %d", subscribe_event.sdf_match_filter_len); + subscribe_event.sdf_match_filter_len = it.get_u16(); + } else if (attr_type == NAN_ATTRIBUTE_TX_MATCH_FILTER) { + memcpy(subscribe_event.sdf_match_filter, it.get_data(), + subscribe_event.sdf_match_filter_len); + subscribe_event.sdf_match_filter + [subscribe_event.sdf_match_filter_len] = '\0'; + ALOGI("sdf match filter: %s", subscribe_event.sdf_match_filter); + } else if (attr_type == NAN_ATTRIBUTE_CIPHER_SUITE_TYPE) { + ALOGI("Peer Cipher suite type: %u", it.get_u8()); + subscribe_event.peer_cipher_type = it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_SCID_LEN) { + ALOGI("scid length %d", it.get_u32()); + subscribe_event.scid_len= it.get_u32(); + } else if (attr_type == NAN_ATTRIBUTE_SCID) { + memcpy(subscribe_event.scid, it.get_data(), + subscribe_event.scid_len); + subscribe_event.scid + [subscribe_event.scid_len] = '\0'; + ALOGI("scid: %s", subscribe_event.scid); + } else if (attr_type == NAN_ATTRIBUTE_RANGING_INDICATION) { + subscribe_event.range_info.ranging_event_type = it.get_u32(); + ALOGI("ranging indication %d", it.get_u32()); + } else if (attr_type == NAN_ATTRIBUTE_RANGING_RESULT) { + subscribe_event.range_info.range_measurement_mm = it.get_u32(); + ALOGI("ranging result %d", it.get_u32()); + } else if (attr_type == NAN_ATTRIBUTE_RSSI_PROXIMITY) { + subscribe_event.rssi_value = it.get_u8(); + ALOGI("rssi value : %u", it.get_u8()); + } else if (attr_type == NAN_ATTRIBUTE_SDEA_SERVICE_SPECIFIC_INFO_LEN) { + ALOGI("sdea svc length %d", it.get_u16()); + subscribe_event.sdea_service_specific_info_len = it.get_u16(); + } else if (attr_type == NAN_ATTRIBUTE_SDEA_SERVICE_SPECIFIC_INFO) { + memcpy(subscribe_event.sdea_service_specific_info, it.get_data(), + subscribe_event.sdea_service_specific_info_len); + subscribe_event.sdea_service_specific_info + [subscribe_event.sdea_service_specific_info_len] = '\0'; + ALOGI("sdea service info: %s", subscribe_event.sdea_service_specific_info); + } else if (attr_type == NAN_ATTRIBUTE_MATCH_OCCURRED_FLAG) { + ALOGI("match occurred flag: %u", it.get_u8()); + subscribe_event.match_occured_flag = it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_OUT_OF_RESOURCE_FLAG) { + ALOGI("Out of resource flag: %u", it.get_u8()); + subscribe_event.out_of_resource_flag = it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_SDE_CONTROL_CONFIG_DP) { + ALOGI("Peer config for data path needed: %u", it.get_u8()); + subscribe_event.peer_sdea_params.config_nan_data_path = it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_SDE_CONTROL_DP_TYPE) { + ALOGI("Data Path type: %u", it.get_u8()); + subscribe_event.peer_sdea_params.ndp_type = (NdpType)it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_SDE_CONTROL_SECURITY) { + ALOGI("Security configuration: %u", it.get_u8()); + subscribe_event.peer_sdea_params.security_cfg = + (NanDataPathSecurityCfgStatus)it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_SDE_CONTROL_RANGE_SUPPORT) { + ALOGI("Ranging report state: %u", it.get_u8()); + subscribe_event.peer_sdea_params.range_report = (NanRangeReport)it.get_u8(); + } + } + + GET_NAN_HANDLE(info)->mHandlers.EventMatch(&subscribe_event); + break; + + case NAN_EVENT_SUBSCRIBE_UNMATCH: + ALOGE("%s: Not applicable yet\n", __func__); + break; + + case NAN_EVENT_SUBSCRIBE_TERMINATED: + NanSubscribeTerminatedInd sub_term_event; + memset(&sub_term_event, 0, sizeof(NanSubscribeTerminatedInd)); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (attr_type == NAN_ATTRIBUTE_SUBSCRIBE_ID) { + sub_term_event.subscribe_id = it.get_u16(); + ALOGI("sub id = %u", sub_term_event.subscribe_id); + } else if (attr_type == NAN_ATTRIBUTE_STATUS) { + sub_term_event.reason = (NanStatusType)it.get_u16(); + ALOGI("sub termination status %u", sub_term_event.reason); + } else if (attr_type == NAN_ATTRIBUTE_REASON) { + u8 len = min(it.get_len(), sizeof(sub_term_event.nan_reason)); + memcpy(sub_term_event.nan_reason, it.get_data(), len); + ALOGI("sub termination nan reason: %s, len = %d\n", + sub_term_event.nan_reason, len); + } else { + ALOGI("Unknown attr: %d\n", attr_type); + } + } + + GET_NAN_HANDLE(info)->mHandlers.EventSubscribeTerminated(&sub_term_event); + break; + case NAN_EVENT_MATCH_EXPIRY: + HandleExpiryEvent(info, vendor_data); + break; + case NAN_EVENT_FOLLOWUP: + NanFollowupInd followup_event; + memset(&followup_event, 0, sizeof(NanFollowupInd)); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (attr_type == NAN_ATTRIBUTE_MAC_ADDR) { + memcpy(followup_event.addr, it.get_data(), NAN_MAC_ADDR_LEN); + } else if (attr_type == NAN_ATTRIBUTE_PEER_ID) { + followup_event.publish_subscribe_id = it.get_u16(); + } else if (attr_type == NAN_ATTRIBUTE_INST_ID) { + followup_event.requestor_instance_id = it.get_u32(); + } else if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN) { + followup_event.service_specific_info_len = it.get_u16(); + } else if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO) { + memcpy(followup_event.service_specific_info, it.get_data(), + followup_event.service_specific_info_len); + } else if (attr_type == NAN_ATTRIBUTE_SDEA_SERVICE_SPECIFIC_INFO) { + memcpy(followup_event.sdea_service_specific_info, it.get_data(), + followup_event.sdea_service_specific_info_len); + } + } + counters.transmit_recv++; + GET_NAN_HANDLE(info)->mHandlers.EventFollowup(&followup_event); + break; + + case NAN_EVENT_TRANSMIT_FOLLOWUP_IND: + NanTransmitFollowupInd followup_ind; + counters.transmit_txs++; + memset(&followup_ind, 0, sizeof(NanTransmitFollowupInd)); + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + if (attr_type == NAN_ATTRIBUTE_TRANSAC_ID) { + followup_ind.id = it.get_u16(); + } else if (attr_type == NAN_ATTRIBUTE_STATUS) { + followup_ind.reason = (NanStatusType)it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_REASON) { + u8 len = min(it.get_len(), sizeof(followup_ind.nan_reason)); + memcpy(followup_ind.nan_reason, it.get_data(), len); + ALOGI("nan transmit followup ind: reason: %s, len = %d\n", + followup_ind.nan_reason, len); + } + } + GET_NAN_HANDLE(info)->mHandlers.EventTransmitFollowup(&followup_ind); + break; +#ifdef NOT_YET + case NAN_EVENT_PUBLISH_REPLIED_IND: + NanPublishRepliedInd pub_reply_event; + memset(&pub_reply_event, 0, sizeof(pub_reply_event)); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (attr_type == NAN_ATTRIBUTE_SUBSCRIBE_ID) { + ALOGI("sub id: %u", it.get_u16()); + pub_reply_event.requestor_instance_id = it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_MAC_ADDR) { + memcpy(pub_reply_event.addr, it.get_data(), NAN_MAC_ADDR_LEN); + ALOGI("Subscriber mac: " MACSTR, MAC2STR(pub_reply_event.addr)); + } else if (attr_type == NAN_ATTRIBUTE_RSSI_PROXIMITY) { + pub_reply_event.rssi_value = it.get_u8(); + ALOGI("Received rssi value : %u", it.get_u8()); + } + } + GET_NAN_HANDLE(info)->mHandlers.EventPublishReplied(&pub_reply_event); + break; +#endif /* NOT_YET */ + } // end-of-switch-case + return NL_SKIP; + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +class NanDataPathPrimitive : public WifiCommand +{ + NanRequest reqContext; + u32 mNdpId; + NanRequestType mType; + u8 count; + + public: + NanDataPathPrimitive(wifi_interface_handle iface, int id, + NanRequest params, NanRequestType cmdType) + : WifiCommand("NanCommand", iface, id), reqContext(params), mType(cmdType) + { + mNdpId = 0; + count = 0; + } + ~NanDataPathPrimitive() { + ALOGE("NanDataPathPrimitive destroyed\n"); + } + u8 mSvcHash[NAN_SVC_HASH_SIZE]; + u8 mPubNmi[NAN_MAC_ADDR_LEN]; + + void setType(NanRequestType type ) { + mType = type; + } + + int getNdpId() { + return mNdpId; + } + + int createRequest(WifiRequest& request) + { + ALOGI("NAN CMD: %s\n", NanCmdToString(mType)); + if (mType == NAN_DATA_PATH_IFACE_CREATE) { + return createDataPathIfaceRequest(request, (char *)reqContext); + } else if (mType == NAN_DATA_PATH_IFACE_DELETE) { + return deleteDataPathIfaceRequest(request, (char *)reqContext); + } else if (mType == NAN_DATA_PATH_INIT_REQUEST) { + return createDataPathInitRequest(request, + (NanDataPathInitiatorRequest *)reqContext); + } else if (mType == NAN_DATA_PATH_IND_RESPONSE) { + return createDataPathIndResponse(request, + (NanDataPathIndicationResponse *)reqContext); + } else if (mType == NAN_DATA_PATH_END) { + return createDataPathEndRequest(request, + (NanDataPathEndRequest *)reqContext); + } else if (mType == NAN_DATA_PATH_SEC_INFO) { + return createDataPathSecInfoRequest(request, + (NanDataPathSecInfoRequest *)reqContext); + } else { + ALOGE("%s: Unknown NDP request: %d\n", __func__, mType); + } + + return WIFI_SUCCESS; + } + + int createDataPathIfaceRequest(WifiRequest& request, char *iface_name) + { + int result = request.create(GOOGLE_OUI, NAN_SUBCMD_DATA_PATH_IFACE_CREATE); + if (result < 0) { + ALOGE("%s Failed to create request\n", __func__); + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_string(NAN_ATTRIBUTE_IFACE, (char *)iface_name); + if (result < 0) { + ALOGE("%s: Failed to fill iface, result = %d\n", __func__, result); + return result; + } + + request.attr_end(data); + return WIFI_SUCCESS; + } + + int deleteDataPathIfaceRequest(WifiRequest& request, char *iface_name) + { + int result = request.create(GOOGLE_OUI, NAN_SUBCMD_DATA_PATH_IFACE_DELETE); + if (result < 0) { + ALOGE("%s: Failed to create request, result = %d\n", __func__, result); + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + result = request.put_string(NAN_ATTRIBUTE_IFACE, (char *)iface_name); + if (result < 0) { + ALOGE("%s: Failed to fill iface, result = %d\n", __func__, result); + return result; + } + + request.attr_end(data); + return WIFI_SUCCESS; + } + + int createDataPathSecInfoRequest(WifiRequest& request, NanDataPathSecInfoRequest *mParams) + { + int result = request.create(GOOGLE_OUI, NAN_SUBCMD_DATA_PATH_SEC_INFO); + if (result < 0) { + ALOGE("%s Failed to create request\n", __func__); + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + result = request.put_u32(NAN_ATTRIBUTE_PUBLISH_ID, mParams->requestor_instance_id); + if (result < 0) { + ALOGE("%s: Failed to fill instance id = %d, result = %d\n", + __func__, mParams->requestor_instance_id, result); + return result; + } + + result = request.put_addr(NAN_ATTRIBUTE_MAC_ADDR, mParams->peer_disc_mac_addr); + if (result < 0) { + ALOGE("%s: Failed to fill mac addr, result = %d\n", __func__, result); + return result; + } + + result = request.put_u32(NAN_ATTRIBUTE_NDP_ID, mParams->ndp_instance_id); + if (result < 0) { + ALOGE("%s: Failed to fill ndp_instance_id = %d, result = %d\n", + __func__, mParams->ndp_instance_id, result); + return result; + } + + request.attr_end(data); + return WIFI_SUCCESS; + } + + int createDataPathInitRequest(WifiRequest& request, NanDataPathInitiatorRequest *mParams) + { + int result = request.create(GOOGLE_OUI, NAN_SUBCMD_DATA_PATH_REQUEST); + u8 pmk_hex[NAN_PMK_INFO_LEN]; + if (result < 0) { + ALOGE("%s: Failed to create request, result = %d\n", __func__, result); + return result; + } + + mNdpId = mParams->requestor_instance_id; + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + result = request.put_u32(NAN_ATTRIBUTE_PUBLISH_ID, mParams->requestor_instance_id); + if (result < 0) { + ALOGE("%s: Failed to fill pub id = %d, result = %d\n", + __func__, mParams->requestor_instance_id, result); + return result; + } + + result = request.put_u32(NAN_ATTRIBUTE_CHANNEL, (u32)mParams->channel); + if (result < 0) { + ALOGE("%s: Failed to fill channel = %d, result = %d\n", + __func__, mParams->channel, result); + return result; + } + + result = request.put_addr(NAN_ATTRIBUTE_MAC_ADDR, mParams->peer_disc_mac_addr); + if (result < 0) { + ALOGE("%s: Failed to fill mac addr, result = %d\n", __func__, result); + return result; + } + + result = request.put_string(NAN_ATTRIBUTE_IFACE, mParams->ndp_iface); + if (result < 0) { + ALOGE("%s: Failed to fill ndp_iface, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_SECURITY, + (NanDataPathSecurityCfgStatus)mParams->ndp_cfg.security_cfg); + if (result < 0) { + ALOGE("%s: Failed to fill security, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_QOS, + (NanDataPathQosCfg) mParams->ndp_cfg.qos_cfg); + if (result < 0) { + ALOGE("%s: Failed to fill QoS, result = %d\n", __func__, result); + return result; + } + + if (mParams->app_info.ndp_app_info_len) { + result = request.put_u16(NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN, + mParams->app_info.ndp_app_info_len); + if (result < 0) { + ALOGE("%s: Failed to fill svc info len = %d, result = %d\n", + __func__, mParams->app_info.ndp_app_info_len, result); + return result; + } + + result = request.put(NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO, + (void *)mParams->app_info.ndp_app_info, mParams->app_info.ndp_app_info_len); + if (result < 0) { + ALOGE("%s: Failed to fill svc info, result = %d\n", __func__, result); + return result; + } + } + + result = request.put_u8(NAN_ATTRIBUTE_CIPHER_SUITE_TYPE, + mParams->cipher_type); + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_CIPHER_SUITE_TYPE, result = %d\n", + __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_KEY_TYPE, + mParams->key_info.key_type); + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_KEY_TYPE, result = %d\n", + __func__, result); + return result; + } + + + if (mParams->service_name_len) { + result = request.put_u16(NAN_ATTRIBUTE_SERVICE_NAME_LEN, mParams->service_name_len); + if (result < 0) { + ALOGE("%s: Failed to fill svc name len, result = %d\n", __func__, result); + return result; + } + + prhex(NULL, mParams->service_name, mParams->service_name_len); + result = request.put(NAN_ATTRIBUTE_SERVICE_NAME, (void *)mParams->service_name, + mParams->service_name_len); + if (result < 0) { + ALOGE("%s: Failed to fill svc name, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->key_info.key_type == NAN_SECURITY_KEY_INPUT_PMK) { + if (mParams->key_info.body.pmk_info.pmk_len) { + result = request.put_u32(NAN_ATTRIBUTE_KEY_LEN, + mParams->key_info.body.pmk_info.pmk_len); + if (result < 0) { + ALOGE("%s: Failed to fill pmk len, result = %d\n", __func__, result); + return result; + } + result = request.put(NAN_ATTRIBUTE_KEY_DATA, + (void *)mParams->key_info.body.pmk_info.pmk, + mParams->key_info.body.pmk_info.pmk_len); + if (result < 0) { + ALOGE("%s: Failed to fill pmk, result = %d\n", __func__, result); + return result; + } + } + } + + if (mParams->key_info.key_type == NAN_SECURITY_KEY_INPUT_PASSPHRASE) { + if (mParams->key_info.body.passphrase_info.passphrase_len < NAN_SECURITY_MIN_PASSPHRASE_LEN || + mParams->key_info.body.passphrase_info.passphrase_len > NAN_SECURITY_MAX_PASSPHRASE_LEN) { + ALOGE("passphrase must be between %d and %d characters long\n", + NAN_SECURITY_MIN_PASSPHRASE_LEN, + NAN_SECURITY_MAX_PASSPHRASE_LEN); + return NAN_STATUS_INVALID_PARAM; + } else { + memset(pmk_hex, 0, NAN_PMK_INFO_LEN); + result = passphrase_to_pmk(mParams->peer_disc_mac_addr, mParams->cipher_type, + mParams->service_name, &mParams->key_info, pmk_hex); + if (result < 0) { + ALOGE("%s: Failed to convert passphrase to key data, result = %d\n", __func__, result); + return result; + } + result = request.put_u32(NAN_ATTRIBUTE_KEY_LEN, NAN_PMK_INFO_LEN); + if (result < 0) { + ALOGE("%s: Failed to fill passphrase len, result = %d\n", __func__, result); + return result; + } + result = request.put(NAN_ATTRIBUTE_KEY_DATA, pmk_hex, NAN_PMK_INFO_LEN); + if (result < 0) { + ALOGE("%s: Failed to fill passphrase, result = %d\n", __func__, result); + return result; + } + prhex("PMK", pmk_hex, NAN_PMK_INFO_LEN); + } + } + + if (mParams->scid_len) { + if (mParams->scid_len != NAN_SCID_INFO_LEN) { + ALOGE("%s: Invalid scid len, = %d\n", __func__, mParams->scid_len); + return NAN_STATUS_INVALID_PARAM; + } + result = request.put_u32(NAN_ATTRIBUTE_SCID_LEN, + mParams->scid_len); + if (result < 0) { + ALOGE("%s: Failed to fill scid len, result = %d\n", __func__, result); + return result; + } + + result = request.put(NAN_ATTRIBUTE_SCID, + (void *)mParams->scid, mParams->scid_len); + if (result < 0) { + ALOGE("%s: Failed to fill scid, result = %d\n", __func__, result); + return result; + } + } + + request.attr_end(data); + return WIFI_SUCCESS; + } + + int createDataPathIndResponse(WifiRequest& request, + NanDataPathIndicationResponse *mParams) + { + int result = request.create(GOOGLE_OUI, NAN_SUBCMD_DATA_PATH_RESPONSE); + u8 pmk_hex[NAN_PMK_INFO_LEN]; + if (result < 0) { + ALOGE("%s: Failed to create request, result = %d\n", __func__, result); + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + result = request.put_u32(NAN_ATTRIBUTE_NDP_ID, mParams->ndp_instance_id); + if (result < 0) { + ALOGE("%s: Failed to fill ndp_instance_id = %d, result = %d\n", + __func__, mParams->ndp_instance_id, result); + return result; + } + + result = request.put_string(NAN_ATTRIBUTE_IFACE, mParams->ndp_iface); + if (result < 0) { + ALOGE("%s: Failed to fill ndp_iface, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_SECURITY, + (NanDataPathSecurityCfgStatus)mParams->ndp_cfg.security_cfg); + if (result < 0) { + ALOGE("%s: Failed to fill security_cfg, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_QOS, + (NanDataPathQosCfg)mParams->ndp_cfg.qos_cfg); + if (result < 0) { + ALOGE("%s: Failed to fill qos_cfg, result = %d\n", __func__, result); + return result; + } + + if (mParams->app_info.ndp_app_info_len) { + result = request.put_u16(NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN, + mParams->app_info.ndp_app_info_len); + if (result < 0) { + ALOGE("%s: Failed to fill svc info len = %d, result = %d\n", + __func__, mParams->app_info.ndp_app_info_len, result); + return result; + } + + result = request.put(NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO, + (void *)mParams->app_info.ndp_app_info, mParams->app_info.ndp_app_info_len); + if (result < 0) { + ALOGE("%s: Failed to fill svc info, result = %d\n", __func__, result); + return result; + } + } + + result = request.put_u8(NAN_ATTRIBUTE_RSP_CODE, mParams->rsp_code); + if (result < 0) { + ALOGE("%s: Failed to fill resp code = %d, result = %d\n", + __func__, mParams->rsp_code, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_CIPHER_SUITE_TYPE, + mParams->cipher_type); + if (result < 0) { + ALOGE("%s: Failed to fill cipher_type, result = %d\n", + __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_KEY_TYPE, + mParams->key_info.key_type); + if (result < 0) { + ALOGE("%s: Failed to fill key type, result = %d\n", + __func__, result); + return result; + } + + if (mParams->service_name_len) { + result = request.put_u16(NAN_ATTRIBUTE_SERVICE_NAME_LEN, mParams->service_name_len); + if (result < 0) { + ALOGE("%s: Failed to fill svc name len, result = %d\n", __func__, result); + return result; + } + + prhex(NULL, mParams->service_name, mParams->service_name_len); + result = request.put(NAN_ATTRIBUTE_SERVICE_NAME, (void *)mParams->service_name, + mParams->service_name_len); + if (result < 0) { + ALOGE("%s: Failed to fill svc name, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->key_info.key_type == NAN_SECURITY_KEY_INPUT_PMK) { + if (mParams->key_info.body.pmk_info.pmk_len) { + result = request.put_u32(NAN_ATTRIBUTE_KEY_LEN, + mParams->key_info.body.pmk_info.pmk_len); + if (result < 0) { + ALOGE("%s: Failed to fill pmk len, result = %d\n", __func__, result); + return result; + } + result = request.put(NAN_ATTRIBUTE_KEY_DATA, + (void *)mParams->key_info.body.pmk_info.pmk, + mParams->key_info.body.pmk_info.pmk_len); + if (result < 0) { + ALOGE("%s: Failed to fill pmk, result = %d\n", __func__, result); + return result; + } + } + } + + if (mParams->key_info.key_type == NAN_SECURITY_KEY_INPUT_PASSPHRASE) { + if (mParams->key_info.body.passphrase_info.passphrase_len < NAN_SECURITY_MIN_PASSPHRASE_LEN || + mParams->key_info.body.passphrase_info.passphrase_len > NAN_SECURITY_MAX_PASSPHRASE_LEN) { + ALOGE("passphrase must be between %d and %d characters long\n", + NAN_SECURITY_MIN_PASSPHRASE_LEN, + NAN_SECURITY_MAX_PASSPHRASE_LEN); + return NAN_STATUS_INVALID_PARAM; + } else { + memset(pmk_hex, 0, NAN_PMK_INFO_LEN); + result = passphrase_to_pmk(mPubNmi, mParams->cipher_type, + mParams->service_name, &mParams->key_info, pmk_hex); + if (result < 0) { + ALOGE("%s: Failed to convert passphrase to key data, result = %d\n", __func__, result); + return result; + } + result = request.put_u32(NAN_ATTRIBUTE_KEY_LEN, NAN_PMK_INFO_LEN); + if (result < 0) { + ALOGE("%s: Failed to fill passphrase len, result = %d\n", __func__, result); + return result; + } + result = request.put(NAN_ATTRIBUTE_KEY_DATA, pmk_hex, NAN_PMK_INFO_LEN); + if (result < 0) { + ALOGE("%s: Failed to fill passphrase, result = %d\n", __func__, result); + return result; + } + } + } + + if (mParams->scid_len) { + if (mParams->scid_len != NAN_SCID_INFO_LEN) { + ALOGE("%s: Invalid scid len, = %d\n", __func__, mParams->scid_len); + return NAN_STATUS_INVALID_PARAM; + } + result = request.put_u32(NAN_ATTRIBUTE_SCID_LEN, + mParams->scid_len); + if (result < 0) { + ALOGE("%s: Failed to fill scid len, result = %d\n", __func__, result); + return result; + } + + prhex(NULL, mParams->scid, mParams->scid_len); + result = request.put(NAN_ATTRIBUTE_SCID, + (void *)mParams->scid, mParams->scid_len); + if (result < 0) { + ALOGE("%s: Failed to fill scid, result = %d\n", __func__, result); + return result; + } + } + + request.attr_end(data); + return WIFI_SUCCESS; + } + + int createDataPathEndRequest(WifiRequest& request, NanDataPathEndRequest *mParams) + { + int result = request.create(GOOGLE_OUI, NAN_SUBCMD_DATA_PATH_END); + if (result < 0) { + ALOGE("%s: Failed to create request, result = %d\n", __func__, result); + return result; + } + + count = mParams->num_ndp_instances; + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + result = request.put_u8(NAN_ATTRIBUTE_INST_COUNT, mParams->num_ndp_instances); + if (result < 0) { + ALOGE("%s: Failed to fill num_ndp_instances = %d, result = %d\n", + __func__, mParams->num_ndp_instances, result); + return result; + } + + while (count) { + result = request.put_u32(NAN_ATTRIBUTE_NDP_ID, mParams->ndp_instance_id[count-1]); + if (result < 0) { + ALOGE("%s: Failed to fill ndp id = %d, result = %d\n", + __func__, mParams->ndp_instance_id[count-1], result); + return result; + } + ALOGE("%s:NDP ID = %d\n", __func__, mParams->ndp_instance_id[count-1]); + count -= 1; + } + + request.attr_end(data); + return WIFI_SUCCESS; + } + + int open() + { + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request); + if (result != WIFI_SUCCESS) { + ALOGE("%s: failed to create setup request; result = %d", __func__, result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("%s: failed to configure setup; result = %d", __func__, result); + return result; + } + + request.destroy(); + return WIFI_SUCCESS; + } + + virtual bool valid_dp_response_type(int response_type) { + bool valid = false; + switch(response_type) { + case NAN_DP_INTERFACE_CREATE: + case NAN_DP_INTERFACE_DELETE: + case NAN_DP_INITIATOR_RESPONSE: + case NAN_DP_RESPONDER_RESPONSE: + case NAN_DP_END: + valid = true; + break; + default: + ALOGE("NanDataPathPrmitive::Unknown cmd Response: %d\n", response_type); + break; + } + return valid; + } + + int handleResponse(WifiEvent& reply) + { + nan_hal_resp_t *rsp_vndr_data = NULL; + + if (reply.get_cmd() != NL80211_CMD_VENDOR || reply.get_vendor_data() == NULL) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + rsp_vndr_data = (nan_hal_resp_t *)reply.get_vendor_data(); + ALOGI("NanDataPathPrmitive::handle response\n"); + int32_t result = rsp_vndr_data->value; + NanResponseMsg rsp_data; + + memset(&rsp_data, 0, sizeof(NanResponseMsg)); + rsp_data.response_type = get_response_type((WIFI_SUB_COMMAND)rsp_vndr_data->subcmd); + + if ((WIFI_SUB_COMMAND)rsp_vndr_data->subcmd == NAN_SUBCMD_DATA_PATH_SEC_INFO) { + /* Follow through */ + } else if (!valid_dp_response_type(rsp_data.response_type)) { + return NL_SKIP; + } + rsp_data.status = nan_map_response_status(rsp_vndr_data->status); + ALOGE("Mapped hal status = %d\n", rsp_data.status); + + if (rsp_vndr_data->nan_reason[0] == '\0') { + memcpy(rsp_data.nan_error, NanStatusToString(rsp_data.status), + strlen(NanStatusToString(rsp_data.status))); + rsp_data.nan_error[strlen(NanStatusToString(rsp_data.status))] = '\0'; + } + rsp_data.nan_error[NAN_ERROR_STR_LEN - 1] = '\0'; + ALOGI("\n Received nan_error string %s\n", (u8*)rsp_data.nan_error); + + if (rsp_data.response_type == NAN_DP_INITIATOR_RESPONSE) { + ALOGI("received ndp instance_id %d and ret = %d\n", rsp_vndr_data->ndp_instance_id, result); + rsp_data.body.data_request_response.ndp_instance_id = rsp_vndr_data->ndp_instance_id; + mNdpId = rsp_vndr_data->ndp_instance_id; + } else if ((WIFI_SUB_COMMAND)rsp_vndr_data->subcmd == NAN_SUBCMD_DATA_PATH_SEC_INFO) { + memcpy(mPubNmi, rsp_vndr_data->pub_nmi, NAN_MAC_ADDR_LEN); + memcpy(mSvcHash, rsp_vndr_data->svc_hash, NAN_SVC_HASH_SIZE); + return NL_SKIP; + } + + ALOGI("NanDataPathPrmitive:Received response for cmd [%s], ret %d\n", + NanRspToString(rsp_data.response_type), rsp_data.status); + GET_NAN_HANDLE(info)->mHandlers.NotifyResponse(id(), &rsp_data); + return NL_SKIP; + } + + int handleEvent(WifiEvent& event) + { + int cmd = event.get_vendor_subcmd(); + u16 attr_type; + + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + + switch(cmd) { + case NAN_EVENT_DATA_REQUEST: { + NanDataPathRequestInd ndp_request_event; + memset(&ndp_request_event, 0, sizeof(NanDataPathRequestInd)); + u16 ndp_ind_app_info_len = 0; + counters.dp_req_evt++; + ALOGI("Received NAN_EVENT_DATA_REQUEST_INDICATION\n"); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (attr_type == NAN_ATTRIBUTE_PUBLISH_ID) { + ALOGI("publish_id: %u\n", it.get_u32()); + ndp_request_event.service_instance_id = it.get_u32(); + + } else if (attr_type == NAN_ATTRIBUTE_MAC_ADDR) { + memcpy(ndp_request_event.peer_disc_mac_addr, + it.get_data(), NAN_MAC_ADDR_LEN); + ALOGI("Discovery MAC addr of the peer/initiator: " MACSTR "\n", + MAC2STR(ndp_request_event.peer_disc_mac_addr)); + + } else if (attr_type == NAN_ATTRIBUTE_NDP_ID) { + ALOGI("ndp id: %u\n", it.get_u32()); + ndp_request_event.ndp_instance_id = it.get_u32(); + + } else if (attr_type == NAN_ATTRIBUTE_SECURITY) { + ALOGI("security: %u\n", + (NanDataPathSecurityCfgStatus)it.get_u8()); + ndp_request_event.ndp_cfg.security_cfg = + (NanDataPathSecurityCfgStatus)it.get_u8(); + + } else if (attr_type == NAN_ATTRIBUTE_QOS) { + ALOGI("QoS: %u\n", (NanDataPathQosCfg)it.get_u8()); + ndp_request_event.ndp_cfg.qos_cfg = (NanDataPathQosCfg)it.get_u8(); + + } else if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN) { + ndp_request_event.app_info.ndp_app_info_len = it.get_u16(); + ndp_ind_app_info_len = ndp_request_event.app_info.ndp_app_info_len; + + } else if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO) { + memcpy(ndp_request_event.app_info.ndp_app_info, it.get_data(), + ndp_ind_app_info_len); + ndp_request_event.app_info.ndp_app_info + [ndp_ind_app_info_len] = '\0'; + ALOGI("service info: %s\n", ndp_request_event.app_info.ndp_app_info); + + } else if (attr_type == NAN_ATTRIBUTE_SCID_LEN) { + ALOGI("scid len: %u\n", it.get_u32()); + ndp_request_event.scid_len = it.get_u32(); + + } else if (attr_type == NAN_ATTRIBUTE_SCID) { + memcpy(ndp_request_event.scid, it.get_data(), + ndp_request_event.scid_len); + ndp_request_event.scid[ndp_request_event.scid_len] = '\0'; + ALOGI("scid : %s\n", ndp_request_event.scid); + + } + } + + GET_NAN_HANDLE(info)->mHandlers.EventDataRequest(&ndp_request_event); + break; + } + case NAN_EVENT_DATA_CONFIRMATION: { + NanDataPathConfirmInd ndp_create_confirmation_event; + memset(&ndp_create_confirmation_event, 0, sizeof(NanDataPathConfirmInd)); + u16 ndp_conf_app_info_len = 0; + u8 chan_idx = 0; + counters.dp_confirm_evt++; + ALOGI("Received NAN_EVENT_DATA_CONFIRMATION\n"); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (attr_type == NAN_ATTRIBUTE_NDP_ID) { + ALOGI("ndp id: %u", it.get_u32()); + ndp_create_confirmation_event.ndp_instance_id = it.get_u32(); + + } else if (attr_type == NAN_ATTRIBUTE_PEER_NDI_MAC_ADDR) { + memcpy(ndp_create_confirmation_event.peer_ndi_mac_addr, it.get_data(), + NAN_MAC_ADDR_LEN); + ALOGI("NDI mac address of the peer: " MACSTR "\n", + MAC2STR(ndp_create_confirmation_event.peer_ndi_mac_addr)); + + } else if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN) { + ALOGI("service info len: %d", it.get_u16()); + ndp_create_confirmation_event.app_info.ndp_app_info_len = it.get_u16(); + ndp_conf_app_info_len = ndp_create_confirmation_event.app_info.ndp_app_info_len; + } else if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO) { + memcpy(ndp_create_confirmation_event.app_info.ndp_app_info, + it.get_data(), ndp_conf_app_info_len); + ndp_create_confirmation_event.app_info.ndp_app_info[ndp_conf_app_info_len] + = '\0'; + ALOGI("service info: %s", + ndp_create_confirmation_event.app_info.ndp_app_info); + + } else if (attr_type == NAN_ATTRIBUTE_RSP_CODE) { + ALOGI("response code: %u", (NanDataPathResponseCode)it.get_u8()); + ndp_create_confirmation_event.rsp_code = + (NanDataPathResponseCode)it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_STATUS) { + ALOGI("reason code %u", (NanDataPathResponseCode)it.get_u8()); + ndp_create_confirmation_event.rsp_code = + (NanDataPathResponseCode)it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_NUM_CHANNELS) { + ALOGI("num channels %u", it.get_u32()); + if (it.get_u32() <= NAN_MAX_CHANNEL_INFO_SUPPORTED) { + ndp_create_confirmation_event.num_channels = it.get_u32(); + } else { + ndp_create_confirmation_event.num_channels = + NAN_MAX_CHANNEL_INFO_SUPPORTED; + ALOGE("num channels reset to max allowed %u", + ndp_create_confirmation_event.num_channels); + } + } else if (attr_type == NAN_ATTRIBUTE_CHANNEL_INFO) { + ALOGI("Channel info \n"); + memcpy((u8 *)ndp_create_confirmation_event.channel_info, it.get_data(), + ndp_create_confirmation_event.num_channels * sizeof(NanChannelInfo)); + while (chan_idx < ndp_create_confirmation_event.num_channels) { + ALOGI("channel: %u, Bandwidth: %u, nss: %u\n", + ndp_create_confirmation_event.channel_info[chan_idx].channel, + ndp_create_confirmation_event.channel_info[chan_idx].bandwidth, + ndp_create_confirmation_event.channel_info[chan_idx].nss); + chan_idx++; + } + } + } + GET_NAN_HANDLE(info)->mHandlers.EventDataConfirm(&ndp_create_confirmation_event); + break; + } + case NAN_EVENT_DATA_END: { + NanDataPathEndInd ndp_end_event; + memset(&ndp_end_event, 0, sizeof(NanDataPathEndInd)); + u16 attr_type; + ALOGI("Received NAN_EVENT_DATA_END\n"); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (attr_type == NAN_ATTRIBUTE_INST_COUNT) { + ALOGI("ndp count: %u\n", it.get_u8()); + ndp_end_event.num_ndp_instances = it.get_u8(); + count = it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_NDP_ID) { + while (count) { + ndp_end_event.ndp_instance_id[count-1] = it.get_u32(); + ALOGI("NDP Id from the Event = %u\n", ndp_end_event.ndp_instance_id[count-1]); + count -= 1; + } + } else { + ALOGI("Unknown attr_type: %s\n", NanAttrToString(attr_type)); + } + } + + GET_NAN_HANDLE(info)->mHandlers.EventDataEnd(&ndp_end_event); + break; + } + } // end-of-switch + return NL_SKIP; + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +class NanMacControl : public WifiCommand +{ + NanRequest mParams; + transaction_id mId = NAN_MAC_INVALID_TRANSID; + wifi_interface_handle mIface; + NanRequestType mType; + u32 mVersion; + + public: + NanMacControl(wifi_interface_handle iface, int id, + NanRequest params, NanRequestType cmdType) + : WifiCommand("NanCommand", iface, id), mParams(params), mType(cmdType) + { + mVersion = 0; + setIface(iface); + setId(id); + } + ~NanMacControl() { + ALOGE("NanMacControl destroyed\n"); + } + + void setIface(wifi_interface_handle iface ) { + mIface = iface; + } + + void setId(transaction_id id) { + if (id != NAN_MAC_INVALID_TRANSID) { + mId = id; + } + } + + transaction_id getId() { + return mId; + } + + void setType(NanRequestType type) { + mType = type; + } + u32 getVersion() { + return mVersion; + } + + void setMsg(NanRequest params) { + mParams = params; + } + + int createRequest(WifiRequest& request) { + ALOGI("NAN CMD: %s\n", NanCmdToString(mType)); + if (mType == NAN_REQUEST_ENABLE) { + return createEnableRequest(request, (NanEnableRequest *)mParams); + } else if (mType == NAN_REQUEST_DISABLE) { + return createDisableRequest(request); + } else if (mType == NAN_REQUEST_CONFIG) { + return createConfigRequest(request, (NanConfigRequest*)mParams); + } else if (mType == NAN_REQUEST_STATS) { + /* TODO: Not yet implemented */ + } else if (mType == NAN_REQUEST_TCA) { + /* TODO: Not yet implemented */ + } else if (mType == NAN_VERSION_INFO) { + return createVersionRequest(request); + } else { + ALOGE("Unknown Nan request\n"); + } + + return WIFI_SUCCESS; + } + + int createVersionRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, NAN_SUBCMD_VERSION_INFO); + if (result < 0) { + ALOGE("%s: Fail to create request\n", __func__); + return result; + } + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + request.attr_end(data); + NAN_DBG_EXIT(); + return WIFI_SUCCESS; + } + + int createEnableRequest(WifiRequest& request, NanEnableRequest *mParams) { + int result = request.create(GOOGLE_OUI, NAN_SUBCMD_ENABLE); + s8 rssi; + if (result < 0) { + ALOGE("%s: Fail to create request\n", __func__); + return result; + } + + NAN_DBG_ENTER(); + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + if (mParams->config_2dot4g_support) { + result = request.put_u8(NAN_ATTRIBUTE_2G_SUPPORT, mParams->support_2dot4g_val); + if (result < 0) { + ALOGE("%s: Failing in 2g support, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_support_5g) { + result = request.put_u8(NAN_ATTRIBUTE_5G_SUPPORT, mParams->support_5g_val); + if (result < 0) { + ALOGE("%s: Failing in 5g support, result = %d\n", __func__, result); + return result; + } + } + + result = request.put_u16(NAN_ATTRIBUTE_CLUSTER_LOW, mParams->cluster_low); + if (result < 0) { + ALOGE("%s: Failing in cluster low, result = %d\n", __func__, result); + return result; + } + + result = request.put_u16(NAN_ATTRIBUTE_CLUSTER_HIGH, mParams->cluster_high); + if (result < 0) { + ALOGE("%s: Failing in cluster high, result = %d\n", __func__, result); + return result; + } + + if (mParams->config_sid_beacon) { + result = request.put_u8(NAN_ATTRIBUTE_SID_BEACON, mParams->sid_beacon_val); + if (result < 0) { + ALOGE("%s: Failing in sid beacon, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_subscribe_sid_beacon) { + result = request.put_u8(NAN_ATTRIBUTE_SUB_SID_BEACON, mParams->subscribe_sid_beacon_val); + if (result < 0) { + ALOGE("%s: Failing in sub sid beacon, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_2dot4g_beacons) { + result = request.put_u8(NAN_ATTRIBUTE_SYNC_DISC_2G_BEACON, mParams->beacon_2dot4g_val); + if (result < 0) { + ALOGE("%s: Failing in beacon_2dot4g_val, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_5g_beacons) { + result = request.put_u8(NAN_ATTRIBUTE_SYNC_DISC_5G_BEACON, mParams->beacon_5g_val); + if (result < 0) { + ALOGE("%s: Failing in 5g beacon, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_2dot4g_sdf) { + result = request.put_u8(NAN_ATTRIBUTE_SDF_2G_SUPPORT, mParams->sdf_2dot4g_val); + if (result < 0) { + ALOGE("%s: Failing in 2dot4g sdf, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_5g_sdf) { + result = request.put_u8(NAN_ATTRIBUTE_SDF_5G_SUPPORT, mParams->sdf_5g_val); + if (result < 0) { + ALOGE("%s: Failing in 5g sdf, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_2dot4g_rssi_close) { + if (ISGREATER(mParams->rssi_close_2dot4g_val, NAN_MAX_RSSI)) { + ALOGI("%s: Invalid rssi param \n", __func__); + return WIFI_ERROR_INVALID_ARGS; + } + rssi = -mParams->rssi_close_2dot4g_val; + result = request.put_s8(NAN_ATTRIBUTE_RSSI_CLOSE, rssi); + if (result < 0) { + ALOGE("%s: Failing in 2g rssi close, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_2dot4g_rssi_middle) { + if (ISGREATER(mParams->rssi_middle_2dot4g_val, NAN_MAX_RSSI)) { + ALOGI("%s: Invalid rssi param \n", __func__); + return WIFI_ERROR_INVALID_ARGS; + } + rssi = -mParams->rssi_middle_2dot4g_val; + result = request.put_s8(NAN_ATTRIBUTE_RSSI_MIDDLE, rssi); + if (result < 0) { + ALOGE("%s: Failing in 2g rssi middle, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_2dot4g_rssi_proximity) { + if (ISGREATER(mParams->rssi_proximity_2dot4g_val, NAN_MAX_RSSI)) { + ALOGI("%s: Invalid rssi param \n", __func__); + return WIFI_ERROR_INVALID_ARGS; + } + rssi = -mParams->rssi_proximity_2dot4g_val; + result = request.put_s8(NAN_ATTRIBUTE_RSSI_PROXIMITY, rssi); + if (result < 0) { + ALOGE("%s: Failing in 2g rssi proximity, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_5g_rssi_close) { + if (ISGREATER(mParams->rssi_close_5g_val, NAN_MAX_RSSI)) { + ALOGI("%s: Invalid rssi param \n", __func__); + return WIFI_ERROR_INVALID_ARGS; + } + rssi = -mParams->rssi_close_5g_val; + result = request.put_s8(NAN_ATTRIBUTE_RSSI_CLOSE_5G, rssi); + if (result < 0) { + ALOGE("%s: Failing in 5g rssi close, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_5g_rssi_middle) { + if (ISGREATER(mParams->rssi_middle_5g_val, NAN_MAX_RSSI)) { + ALOGI("%s: Invalid rssi param \n", __func__); + return WIFI_ERROR_INVALID_ARGS; + } + rssi = -mParams->rssi_middle_5g_val; + result = request.put_s8(NAN_ATTRIBUTE_RSSI_MIDDLE_5G, rssi); + if (result < 0) { + ALOGE("%s: Failing in 5g rssi middle, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_5g_rssi_close_proximity) { + if (ISGREATER(mParams->rssi_close_proximity_5g_val, NAN_MAX_RSSI)) { + ALOGI("%s: Invalid rssi param \n", __func__); + return WIFI_ERROR_INVALID_ARGS; + } + rssi = -mParams->rssi_close_proximity_5g_val; + result = request.put_s8(NAN_ATTRIBUTE_RSSI_PROXIMITY_5G, rssi); + if (result < 0) { + ALOGE("%s: Failing in rssi_close_proximity_5g_val, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_cluster_attribute_val) { + result = request.put_u8(NAN_ATTRIBUTE_CONF_CLUSTER_VAL, mParams->config_cluster_attribute_val); + if (result < 0) { + ALOGE("%s: Failing in config_cluster_attribute_val, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_hop_count_limit) { + result = request.put_u8(NAN_ATTRIBUTE_HOP_COUNT_LIMIT, + mParams->hop_count_limit_val); + if (result < 0) { + ALOGE("%s: Failing in hop cnt limit, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_oui) { + ALOGI("%s: oui = 0x%04x\n", __func__, mParams->oui_val); + result = request.put_u32(NAN_ATTRIBUTE_OUI, mParams->oui_val); + if (result < 0) { + ALOGE("%s: Failing in oui, result = %d\n", __func__, result); + return result; + } + } + + result = request.put_u8(NAN_ATTRIBUTE_MASTER_PREF, mParams->master_pref); + if (result < 0) { + ALOGE("%s: Failing in master pref, result = %d\n", __func__, result); + return result; + } + if (mParams->config_random_factor_force) { + result = request.put_u8(NAN_ATTRIBUTE_RANDOM_FACTOR, mParams->random_factor_force_val); + if (result < 0) { + ALOGE("%s: Failing in random factor, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_24g_channel) { + result = request.put_u32(NAN_ATTRIBUTE_24G_CHANNEL, mParams->channel_24g_val); + if (result < 0) { + ALOGE("%s: Failing in 2.4g channel, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_5g_channel) { + result = request.put_u32(NAN_ATTRIBUTE_5G_CHANNEL, mParams->channel_5g_val); + if (result < 0) { + ALOGE("%s: Failing in 5g channel, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_intf_addr) { + result = request.put_addr(NAN_ATTRIBUTE_IF_ADDR, mParams->intf_addr_val); + if (result < 0) { + ALOGE("%s: Failing in intf addr val, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_dw.config_2dot4g_dw_band) { + result = request.put_u32(NAN_ATTRIBUTE_2G_AWAKE_DW, mParams->config_dw.dw_2dot4g_interval_val); + if (result < 0) { + ALOGE("%s: Failing in 2dot4g awake dw, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_dw.config_5g_dw_band) { + result = request.put_u32(NAN_ATTRIBUTE_5G_AWAKE_DW, mParams->config_dw.dw_5g_interval_val); + if (result < 0) { + ALOGE("%s: Failing in 5g awake dw, result = %d\n", __func__, result); + return result; + } + } + + if (ISGREATER(mParams->discovery_indication_cfg, NAN_DISC_IND_MAX)) { + ALOGE("%s:Invalid disc_ind_cfg value.\n", __FUNCTION__); + return WIFI_ERROR_INVALID_ARGS; + } + + result = request.put_u8(NAN_ATTRIBUTE_DISC_IND_CFG, + mParams->discovery_indication_cfg); + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_DISC_IND_CFG, result = %d\n", + __func__, result); + return result; + } + + if (mParams->config_rssi_window_size) { + result = request.put_u8(NAN_ATTRIBUTE_RSSI_WINDOW_SIZE, + mParams->rssi_window_size_val); + if (result < 0) { + ALOGE("%s: Failing in rssi_window_size_val, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_scan_params) { + result = request.put_u8(NAN_ATTRIBUTE_DWELL_TIME, + mParams->scan_params_val.dwell_time[0]); + if (result < 0) { + ALOGE("%s: Failing in dwell time, result = %d\n", __func__, result); + return result; + } + result = request.put_u8(NAN_ATTRIBUTE_DWELL_TIME_5G, + mParams->scan_params_val.dwell_time[1]); + if (result < 0) { + ALOGE("%s: Failing in 5g dwell time, result = %d\n", __func__, result); + return result; + } + result = request.put_u16(NAN_ATTRIBUTE_SCAN_PERIOD, + mParams->scan_params_val.scan_period[0]); + if (result < 0) { + ALOGE("%s: Failing in scan_period, result = %d\n", __func__, result); + return result; + } + result = request.put_u16(NAN_ATTRIBUTE_SCAN_PERIOD_5G, + mParams->scan_params_val.scan_period[1]); + if (result < 0) { + ALOGE("%s: Failing in 5g scan_period, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_disc_mac_addr_randomization) { + result = request.put_u32(NAN_ATTRIBUTE_RANDOMIZATION_INTERVAL, + mParams->disc_mac_addr_rand_interval_sec); + if (result < 0) { + ALOGE("%s: Failing to fill rand mac address interval, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_discovery_beacon_int) { + result = request.put_u32(NAN_ATTRIBUTE_DISCOVERY_BEACON_INTERVAL, + mParams->discovery_beacon_interval); + if (result < 0) { + ALOGE("%s: Failing to fill disc beacon interval, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_nss) { + result = request.put_u32(NAN_ATTRIBUTE_NSS, mParams->nss); + if (result < 0) { + ALOGE("%s: Failing to fill nss, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_enable_ranging) { + result = request.put_u32(NAN_ATTRIBUTE_ENABLE_RANGING, mParams->enable_ranging); + if (result < 0) { + ALOGE("%s: Failing to fill enable ranging value, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_dw_early_termination) { + result = request.put_u32(NAN_ATTRIBUTE_DW_EARLY_TERM, mParams->enable_dw_termination); + if (result < 0) { + ALOGE("%s: Failing to fill enable dw termination value, result = %d\n", + __func__, result); + return result; + } + } + + if (mParams->config_ndpe_attr) { + result = request.put_u32(NAN_ATTRIBUTE_CMD_USE_NDPE, + mParams->use_ndpe_attr); + if (result < 0) { + ALOGE("%s: Failing to fill use_ndpe, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_enable_instant_mode) { + result = request.put_u32(NAN_ATTRIBUTE_INSTANT_MODE_ENABLE, + mParams->enable_instant_mode); + if (result < 0) { + ALOGE("%s: Failing to fill enable instant mode, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->enable_instant_mode && mParams->config_instant_mode_channel + && mParams->instant_mode_channel) { + result = request.put_u32(NAN_ATTRIBUTE_INSTANT_COMM_CHAN, + mParams->instant_mode_channel); + if (result < 0) { + ALOGE("%s: Failing in config instant channel, result = %d\n", __func__, result); + return result; + } + ALOGI("%s: instant mode channel = %d\n", __func__, mParams->instant_mode_channel); + } + + request.attr_end(data); + NAN_DBG_EXIT(); + return WIFI_SUCCESS; + } + + int createDisableRequest(WifiRequest& request) { + NAN_DBG_ENTER(); + + int result = request.create(GOOGLE_OUI, NAN_SUBCMD_DISABLE); + if (result < 0) { + ALOGE("%s: Fail to create request, result = %d\n", __func__, result); + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + request.attr_end(data); + + NAN_DBG_EXIT(); + return result; + } + + int createConfigRequest(WifiRequest& request, NanConfigRequest *mParams) { + + int result = request.create(GOOGLE_OUI, NAN_SUBCMD_CONFIG); + s8 rssi; + if (result < 0) { + ALOGE("%s: Fail to create config request\n", __func__); + return result; + } + + NAN_DBG_ENTER(); + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + if (mParams->config_sid_beacon) { + result = request.put_u8(NAN_ATTRIBUTE_SID_BEACON, mParams->sid_beacon); + if (result < 0) { + ALOGE("%s: Failing in sid beacon, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_subscribe_sid_beacon) { + result = request.put_u8(NAN_ATTRIBUTE_SUB_SID_BEACON, mParams->subscribe_sid_beacon_val); + if (result < 0) { + ALOGE("%s: Failing in sub sid beacon, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_rssi_proximity) { + if (ISGREATER(mParams->rssi_proximity, NAN_MAX_RSSI)) { + ALOGI("%s: Invalid rssi param \n", __func__); + return WIFI_ERROR_INVALID_ARGS; + } + rssi = -mParams->rssi_proximity; + result = request.put_s8(NAN_ATTRIBUTE_RSSI_PROXIMITY, rssi); + if (result < 0) { + ALOGE("%s: Failing in rssi_proximity, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_master_pref) { + ALOGI("%s: master pref = %u\n", __func__, mParams->master_pref); + result = request.put_u8(NAN_ATTRIBUTE_MASTER_PREF, mParams->master_pref); + if (result < 0) { + ALOGE("%s: Failing in master pref, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_5g_rssi_close_proximity) { + if (ISGREATER(mParams->rssi_close_proximity_5g_val, NAN_MAX_RSSI)) { + ALOGI("%s: Invalid rssi param \n", __func__); + return WIFI_ERROR_INVALID_ARGS; + } + rssi = -mParams->rssi_close_proximity_5g_val; + result = request.put_s8(NAN_ATTRIBUTE_RSSI_PROXIMITY_5G, rssi); + if (result < 0) { + ALOGE("%s: Failing in rssi_close_proximity_5g_val, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_rssi_window_size) { + result = request.put_u8(NAN_ATTRIBUTE_RSSI_WINDOW_SIZE, + mParams->rssi_window_size_val); + if (result < 0) { + ALOGE("%s: Failing in rssi_window_size_val, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_scan_params) { + result = request.put_u8(NAN_ATTRIBUTE_DWELL_TIME, + mParams->scan_params_val.dwell_time[0]); + if (result < 0) { + ALOGE("%s: Failing in dwell time, result = %d\n", __func__, result); + return result; + } + + result = request.put_u8(NAN_ATTRIBUTE_DWELL_TIME_5G, + mParams->scan_params_val.dwell_time[1]); + if (result < 0) { + ALOGE("%s: Failing in 5g dwell time, result = %d\n", __func__, result); + return result; + } + result = request.put_u16(NAN_ATTRIBUTE_SCAN_PERIOD, + mParams->scan_params_val.scan_period[0]); + if (result < 0) { + ALOGE("%s: Failing in scan_period, result = %d\n", __func__, result); + return result; + } + + result = request.put_u16(NAN_ATTRIBUTE_SCAN_PERIOD_5G, + mParams->scan_params_val.scan_period[1]); + if (result < 0) { + ALOGE("%s: Failing in 5g scan_period, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_random_factor_force) { + result = request.put_u8(NAN_ATTRIBUTE_RANDOM_FACTOR, mParams->random_factor_force_val); + if (result < 0) { + ALOGE("%s: Failing in random factor, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_hop_count_force) { + result = request.put_u8(NAN_ATTRIBUTE_HOP_COUNT_LIMIT, + mParams->hop_count_force_val); + if (result < 0) { + ALOGE("%s: Failing in hop cnt limit, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_cluster_attribute_val) { + result = request.put_u8(NAN_ATTRIBUTE_CONF_CLUSTER_VAL, mParams->config_cluster_attribute_val); + if (result < 0) { + ALOGE("%s: Failing in config_cluster_attribute_val, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_fam) { + while (mParams->fam_val.numchans) { + result = request.put_u8(NAN_ATTRIBUTE_ENTRY_CONTROL, + mParams->fam_val.famchan[mParams->fam_val.numchans].entry_control); + if (result < 0) { + ALOGE("%s: Failing in entry control, result = %d\n", __func__, result); + return result; + } + + result = request.put_u32(NAN_ATTRIBUTE_CHANNEL, + (u32)mParams->fam_val.famchan[mParams->fam_val.numchans].channel); + if (result < 0) { + ALOGE("%s: Failed to fill channel = %d, result = %d\n", __func__, + mParams->fam_val.famchan[mParams->fam_val.numchans].channel, result); + return result; + } + + result = request.put_u32(NAN_ATTRIBUTE_AVAIL_BIT_MAP, + (u32)mParams->fam_val.famchan[mParams->fam_val.numchans].avail_interval_bitmap); + if (result < 0) { + ALOGE("%s: Failed to fill avail interval bitmap = %d, result = %d\n", __func__, + mParams->fam_val.famchan[mParams->fam_val.numchans].avail_interval_bitmap, result); + return result; + } + mParams->fam_val.numchans -= 1; + } + + } + + if (mParams->config_dw.config_2dot4g_dw_band) { + result = request.put_u32(NAN_ATTRIBUTE_2G_AWAKE_DW, mParams->config_dw.dw_2dot4g_interval_val); + if (result < 0) { + ALOGE("%s: Failing in 2dot4g awake dw, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_dw.config_5g_dw_band) { + result = request.put_u32(NAN_ATTRIBUTE_5G_AWAKE_DW, mParams->config_dw.dw_5g_interval_val); + if (result < 0) { + ALOGE("%s: Failing in 5g awake dw, result = %d\n", __func__, result); + return result; + } + } + if (ISGREATER(mParams->discovery_indication_cfg, NAN_DISC_IND_MAX)) { + ALOGE("%s:Invalid disc_ind_cfg value.\n", __FUNCTION__); + return WIFI_ERROR_INVALID_ARGS; + } + result = request.put_u8(NAN_ATTRIBUTE_DISC_IND_CFG, + mParams->discovery_indication_cfg); + if (result < 0) { + ALOGE("%s: Failed to fill NAN_ATTRIBUTE_DISC_IND_CFG, result = %d\n", + __func__, result); + return result; + } + if (mParams->config_disc_mac_addr_randomization) { + result = request.put_u32(NAN_ATTRIBUTE_RANDOMIZATION_INTERVAL, + mParams->disc_mac_addr_rand_interval_sec); + if (result < 0) { + ALOGE("%s: Failing in 5g scan_period, result = %d\n", __func__, result); + return result; + } + } + if (mParams->config_ndpe_attr) { + result = request.put_u32(NAN_ATTRIBUTE_CMD_USE_NDPE, + mParams->use_ndpe_attr); + if (result < 0) { + ALOGE("%s: Failing to fill use_ndpe, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_disc_mac_addr_randomization) { + result = request.put_u32(NAN_ATTRIBUTE_RANDOMIZATION_INTERVAL, + mParams->disc_mac_addr_rand_interval_sec); + if (result < 0) { + ALOGE("%s: Failing to fill rand mac interval, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_discovery_beacon_int) { + result = request.put_u32(NAN_ATTRIBUTE_DISCOVERY_BEACON_INTERVAL, + mParams->discovery_beacon_interval); + if (result < 0) { + ALOGE("%s: Failing to fill disc beacon interval, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_nss) { + result = request.put_u32(NAN_ATTRIBUTE_NSS, mParams->nss); + if (result < 0) { + ALOGE("%s: Failing to fill nss, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_enable_ranging) { + result = request.put_u32(NAN_ATTRIBUTE_ENABLE_RANGING, mParams->enable_ranging); + if (result < 0) { + ALOGE("%s: Failing to fill enable ranging value, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->config_dw_early_termination) { + result = request.put_u32(NAN_ATTRIBUTE_DW_EARLY_TERM, mParams->enable_dw_termination); + if (result < 0) { + ALOGE("%s: Failing to fill enable dw termination value, result = %d\n", + __func__, result); + return result; + } + } + + if (mParams->config_enable_instant_mode) { + result = request.put_u32(NAN_ATTRIBUTE_INSTANT_MODE_ENABLE, + mParams->enable_instant_mode); + if (result < 0) { + ALOGE("%s: Failing to fill enable instant mode, result = %d\n", __func__, result); + return result; + } + } + + if (mParams->enable_instant_mode && mParams->config_instant_mode_channel + && mParams->instant_mode_channel) { + result = request.put_u32(NAN_ATTRIBUTE_INSTANT_COMM_CHAN, + mParams->instant_mode_channel); + if (result < 0) { + ALOGE("%s: Failing in config instant channel, result = %d\n", __func__, result); + return result; + } + ALOGI("%s: instant mode channel = %d\n", __func__, mParams->instant_mode_channel); + } + + request.attr_end(data); + NAN_DBG_EXIT(); + return WIFI_SUCCESS; + } + + int start() + { + NAN_DBG_ENTER(); + + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request); + if (result != WIFI_SUCCESS) { + ALOGE("%s: Failed to create setup request; result = %d", __func__, result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("%s: Failed to configure setup; result = %d", __func__, result); + return result; + } + + request.destroy(); + NAN_DBG_EXIT(); + return WIFI_SUCCESS; + } + + int cancel() + { + NAN_DBG_ENTER(); + + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request); + if (result != WIFI_SUCCESS) { + ALOGE("%s: Failed to create setup request; result = %d", __func__, result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("%s: Failed to configure setup; result = %d", __func__, result); + return result; + } + + request.destroy(); + NAN_DBG_EXIT(); + return WIFI_SUCCESS; + } + + int handleResponse(WifiEvent& reply) { + nan_hal_resp_t *rsp_vndr_data = NULL; + + if (reply.get_cmd() != NL80211_CMD_VENDOR || reply.get_vendor_data() == NULL) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + rsp_vndr_data = (nan_hal_resp_t *)reply.get_vendor_data(); + ALOGI("NanMacControl::handleResponse\n"); + if (mType == NAN_VERSION_INFO) { + mVersion = *((u32*)reply.get_vendor_data()); + ALOGI("Response not required for version cmd %d\n", mVersion); + return NL_SKIP; + } + if (rsp_vndr_data->subcmd == NAN_SUBCMD_CONFIG) { + NanResponseMsg rsp_data; + memset(&rsp_data, 0, sizeof(NanResponseMsg)); + rsp_data.response_type = get_response_type((WIFI_SUB_COMMAND)rsp_vndr_data->subcmd); + rsp_data.status = nan_map_response_status(rsp_vndr_data->status); + + ALOGI("NanMacControl:Received response for cmd [%s], TxID %d ret %d\n", + NanRspToString(rsp_data.response_type), id(), rsp_data.status); + + GET_NAN_HANDLE(info)->mHandlers.NotifyResponse(id(), &rsp_data); + } + if (rsp_vndr_data->subcmd == NAN_SUBCMD_ENABLE) { + NanResponseMsg rsp_data; + memset(&rsp_data, 0, sizeof(NanResponseMsg)); + rsp_data.response_type = get_response_type((WIFI_SUB_COMMAND)rsp_vndr_data->subcmd); + rsp_data.status = nan_map_response_status(rsp_vndr_data->status); + + ALOGI("NanMacControl:Received response for cmd [%s], TxID %d ret %d\n", + NanRspToString(rsp_data.response_type), mId, rsp_data.status); + + if( rsp_data.status != NAN_STATUS_SUCCESS) { + GET_NAN_HANDLE(info)->mHandlers.NotifyResponse(mId, &rsp_data); + } + } + return NL_SKIP; + } + + int handleAsyncResponse(nan_hal_resp_t *rsp_vndr_data) { + NanResponseMsg rsp_data; + ALOGI("NanMacControl::handleAsyncResponse\n"); + /* Enable response will be provided to framework in event path */ + if (rsp_vndr_data->subcmd == NAN_SUBCMD_ENABLE) { + return NL_SKIP; + } + memset(&rsp_data, 0, sizeof(NanResponseMsg)); + rsp_data.response_type = get_response_type((WIFI_SUB_COMMAND)rsp_vndr_data->subcmd); + rsp_data.status = nan_map_response_status(rsp_vndr_data->status); + ALOGE("Mapped hal status = %d\n", rsp_data.status); + + /* populate error string if not coming from DHD */ + if (rsp_vndr_data->nan_reason[0] == '\0') { + memcpy(rsp_data.nan_error, NanStatusToString(rsp_data.status), + strlen(NanStatusToString(rsp_data.status))); + rsp_data.nan_error[strlen(NanStatusToString(rsp_data.status))] = '\0'; + } + rsp_data.nan_error[NAN_ERROR_STR_LEN - 1] = '\0'; + ALOGI("\n Received nan_error string %s\n", (u8*)rsp_data.nan_error); + ALOGI("Retrieved ID = %d\n", mId); + + if ((rsp_vndr_data->subcmd == NAN_SUBCMD_DISABLE) && + (mId != NAN_MAC_INVALID_TRANSID)) { + GET_NAN_HANDLE(info)->mHandlers.NotifyResponse(mId, &rsp_data); + mId = NAN_MAC_INVALID_TRANSID; + } + return NL_SKIP; + } + + int handleEvent(WifiEvent& event) { + u32 ndp_instance_id = 0; + int event_id = event.get_vendor_subcmd(); + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = event.get_vendor_data_len(); + u16 attr_type; + nan_hal_resp_t *rsp_vndr_data = NULL; + + ALOGI("%s: Received NanMacControl event = %d (len=%d)\n", + __func__, event.get_cmd(), len); + if (!vendor_data || len == 0) { + ALOGE("No event data found"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (it.get_type() == NAN_ATTRIBUTE_HANDLE) { + } else if (it.get_type() == NAN_ATTRIBUTE_NDP_ID) { + ndp_instance_id = it.get_u32(); + ALOGI("handleEvent: ndp_instance_id = [%d]\n", ndp_instance_id); + } else if (attr_type == NAN_ATTRIBUTE_CMD_RESP_DATA) { + ALOGI("sizeof cmd response data: %ld, it.get_len() = %d\n", + sizeof(nan_hal_resp_t), it.get_len()); + if (it.get_len() == sizeof(nan_hal_resp_t)) { + rsp_vndr_data = (nan_hal_resp_t*)it.get_data(); + } else { + ALOGE("Wrong cmd response data received\n"); + return NL_SKIP; + } + } + } + + ALOGI("Received vendor sub cmd %d\n", event_id); + if (is_de_event(event_id)) { + + NanDiscEnginePrimitive *de_prim = + (NanDiscEnginePrimitive *)(info.nan_disc_control); + if (de_prim != NULL) { + de_prim->handleEvent(event); + } else { + ALOGE("%s: de_primitive is no more available\n", __func__); + } + return NL_SKIP; + + } else if (is_dp_event(event_id)) { + + NanDataPathPrimitive *dp_prim = + (NanDataPathPrimitive *)(info.nan_dp_control); + ALOGI("ndp_instance_id = [%d]\n", ndp_instance_id); + if (dp_prim != NULL) { + dp_prim->handleEvent(event); + } else { + ALOGE("%s: dp_primitive is no more available\n", __func__); + } + return NL_SKIP; + } else { + if (is_cmd_response(event_id)) { + ALOGE("Handling cmd response asynchronously\n"); + if (rsp_vndr_data != NULL) { + handleAsyncResponse(rsp_vndr_data); + } else { + ALOGE("Wrong response data, rsp_vndr_data is NULL\n"); + return NL_SKIP; + } + } + } + + switch(event_id) { + case NAN_EVENT_DE_EVENT: + NanDiscEngEventInd de_event; + memset(&de_event, 0, sizeof(de_event)); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (attr_type == NAN_ATTRIBUTE_CLUSTER_ID) { + memcpy(&de_event.data.cluster.addr, it.get_data(), NAN_MAC_ADDR_LEN); + ALOGI("cluster id = " MACSTR "\n", MAC2STR(de_event.data.cluster.addr)); + } else if (attr_type == NAN_ATTRIBUTE_ENABLE_STATUS) { + ALOGI("nan enable status = %u\n", it.get_u16()); + } else if (attr_type == NAN_ATTRIBUTE_JOIN_STATUS) { + ALOGI("nan joined status = %u\n", it.get_u16()); + } else if (attr_type == NAN_ATTRIBUTE_DE_EVENT_TYPE) { + u8 de_type = it.get_u8(); + ALOGI("nan de event type = %u\n", de_type); + if (de_type == NAN_EVENT_IFACE) { + de_event.event_type = NAN_EVENT_ID_DISC_MAC_ADDR; + ALOGI("received NAN_EVENT_ID_DISC_MAC_ADDR event\n"); + } else if (de_type == NAN_EVENT_START) { + de_event.event_type = NAN_EVENT_ID_STARTED_CLUSTER; + ALOGI("received NAN cluster started event\n"); + } else if (de_type == NAN_EVENT_JOIN) { + /* To be deprecated */ + de_event.event_type = NAN_EVENT_ID_JOINED_CLUSTER; + ALOGI("received join event\n"); + } else if (de_type == NAN_EVENT_ROLE_CHANGE) { + ALOGI("received device role change event\n"); + } else if (de_type == NAN_EVENT_MERGE) { + ALOGI("received merge event\n"); + } else { + ALOGI("received unknown DE event, [%d]\n", de_type); + } + } else if (attr_type == NAN_ATTRIBUTE_MAC_ADDR) { + memcpy(&de_event.data.mac_addr.addr, it.get_data(), NAN_MAC_ADDR_LEN); + memcpy(mNmi, it.get_data(), NAN_MAC_ADDR_LEN); + ALOGI("Primary discovery mac address = " MACSTR "\n", + MAC2STR(mNmi)); + } + } + GET_NAN_HANDLE(info)->mHandlers.EventDiscEngEvent(&de_event); + /* XXX: WAR for sending intf addr to generate Identity + * change callback in framework + * Also WAR for enable response + */ + if (de_event.event_type == NAN_EVENT_ID_STARTED_CLUSTER) { + NanResponseMsg rsp_data; + memcpy(&de_event.data.mac_addr.addr, mNmi, NAN_MAC_ADDR_LEN); + de_event.event_type = NAN_EVENT_ID_DISC_MAC_ADDR; + GET_NAN_HANDLE(info)->mHandlers.EventDiscEngEvent(&de_event); + rsp_data.response_type = NAN_RESPONSE_ENABLED; + rsp_data.status = NAN_STATUS_SUCCESS; + memcpy(rsp_data.nan_error, NanStatusToString(rsp_data.status), + strlen(NanStatusToString(rsp_data.status))); + GET_NAN_HANDLE(info)->mHandlers.NotifyResponse(mId, &rsp_data); + /* clean up mId to distinguish duplciated disable command */ + mId = NAN_MAC_INVALID_TRANSID; + } + break; + + case NAN_EVENT_DISABLED: + ALOGI("Received NAN_EVENT_DISABLED\n"); + NanDisabledInd disabled_ind; + memset(&disabled_ind, 0, sizeof(NanDisabledInd)); + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + if (attr_type == NAN_ATTRIBUTE_STATUS) { + disabled_ind.reason = (NanStatusType)it.get_u8(); + ALOGI("Nan Disable:status %u", disabled_ind.reason); + } else if (attr_type == NAN_ATTRIBUTE_REASON) { + u8 len = min(it.get_len(), sizeof(disabled_ind.nan_reason)); + memcpy(disabled_ind.nan_reason, it.get_data(), len); + ALOGI("Disabled nan reason: %s, len = %d\n", + disabled_ind.nan_reason, len); + } + } + + GET_NAN_HANDLE(info)->mHandlers.EventDisabled(&disabled_ind); + /* unregister Nan vendor events */ + unRegisterNanVendorEvents(); + break; + + case NAN_EVENT_SDF: + ALOGI("Received NAN_EVENT_SDF:\n"); + NanBeaconSdfPayloadInd sdfInd; + memset(&sdfInd, 0, sizeof(sdfInd)); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN) { + sdfInd.data.frame_len = it.get_u16(); + if (sdfInd.data.frame_len > NAN_MAX_FRAME_DATA_LEN) { + sdfInd.data.frame_len = NAN_MAX_FRAME_DATA_LEN; + } + ALOGI("Received NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN: 0x%x(%d)\n", + sdfInd.data.frame_len, sdfInd.data.frame_len); + + } else if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO) { + ALOGI("Received NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO\n"); + memcpy(&sdfInd.data.frame_data, it.get_data(), sdfInd.data.frame_len); + prhex("sdfInd.data.frame_data: ", (u8*)sdfInd.data.frame_data, + sdfInd.data.frame_len); + } + } + GET_NAN_HANDLE(info)->mHandlers.EventBeaconSdfPayload(&sdfInd); + break; + + case NAN_EVENT_TCA: + ALOGI("Received NAN_EVENT_TCA\n"); + break; + + case NAN_EVENT_UNKNOWN: + ALOGI("Received NAN_EVENT_UNKNOWN\n"); + break; + } // end-of-switch + + return NL_SKIP; + } + void unRegisterNanVendorEvents() + { + int i = 0; + for (i = NAN_EVENT_ENABLED; i <= NAN_EVENT_DATA_END; i++) { + unregisterVendorHandler(GOOGLE_OUI, i); + } + unregisterVendorHandler(GOOGLE_OUI, NAN_ASYNC_RESPONSE_DISABLED); + unregisterVendorHandler(GOOGLE_OUI, NAN_EVENT_MATCH_EXPIRY); + } + void registerNanVendorEvents() + { + int i = 0; + for (i = NAN_EVENT_ENABLED; i <= NAN_EVENT_DATA_END; i++) { + registerVendorHandler(GOOGLE_OUI, i); + } + registerVendorHandler(GOOGLE_OUI, NAN_ASYNC_RESPONSE_DISABLED); + registerVendorHandler(GOOGLE_OUI, NAN_EVENT_MATCH_EXPIRY); + } +}; + +/* pretty hex print a contiguous buffer */ +static void prhex(const char *msg, u8 *buf, u32 nbytes) +{ + char line[128]; + char *p; + int len = sizeof(line); + int nchar; + u32 i; + + if (msg && (msg[0] != '\0')) { + printf("%s:\n", msg); + } + + p = line; + for (i = 0; i < nbytes; i++) { + if (i % 16 == 0) { + nchar = snprintf(p, len, " %04d: ", i); /* line prefix */ + p += nchar; + len -= nchar; + } + + if (len > 0) { + nchar = snprintf(p, len, "%02x ", buf[i]); + p += nchar; + len -= nchar; + } + + if (i % 16 == 15) { + ALOGE("%s\n", line); /* flush line */ + p = line; + len = sizeof(line); + } + } + + /* flush last partial line */ + if (p != line) { + ALOGE("%s\n", line); + } +} + + +static const char *NanRspToString(int cmd_resp) +{ + switch (cmd_resp) { + C2S(NAN_RESPONSE_ENABLED) + C2S(NAN_RESPONSE_DISABLED) + C2S(NAN_RESPONSE_PUBLISH) + C2S(NAN_RESPONSE_SUBSCRIBE) + C2S(NAN_RESPONSE_PUBLISH_CANCEL) + C2S(NAN_RESPONSE_SUBSCRIBE_CANCEL) + C2S(NAN_RESPONSE_TRANSMIT_FOLLOWUP) + C2S(NAN_RESPONSE_CONFIG) + C2S(NAN_RESPONSE_TCA) + C2S(NAN_RESPONSE_STATS) + C2S(NAN_DP_INTERFACE_CREATE) + C2S(NAN_DP_INTERFACE_DELETE) + C2S(NAN_DP_INITIATOR_RESPONSE) + C2S(NAN_DP_RESPONDER_RESPONSE) + C2S(NAN_DP_END) + C2S(NAN_GET_CAPABILITIES) + + default: + return "UNKNOWN_NAN_CMD_RESPONSE"; + } +} + +static const char *NanCmdToString(int cmd) +{ + switch (cmd) { + C2S(NAN_REQUEST_ENABLE) + C2S(NAN_REQUEST_DISABLE) + C2S(NAN_REQUEST_PUBLISH) + C2S(NAN_REQUEST_PUBLISH_CANCEL) + C2S(NAN_REQUEST_TRANSMIT_FOLLOWUP) + C2S(NAN_REQUEST_SUBSCRIBE) + C2S(NAN_REQUEST_SUBSCRIBE_CANCEL) + C2S(NAN_REQUEST_STATS) + C2S(NAN_REQUEST_CONFIG) + C2S(NAN_REQUEST_TCA) + C2S(NAN_REQUEST_EVENT_CHECK) + C2S(NAN_REQUEST_GET_CAPABILITIES) + C2S(NAN_DATA_PATH_IFACE_CREATE) + C2S(NAN_DATA_PATH_IFACE_DELETE) + C2S(NAN_DATA_PATH_INIT_REQUEST) + C2S(NAN_DATA_PATH_IND_RESPONSE) + C2S(NAN_DATA_PATH_END) + C2S(NAN_DATA_PATH_IFACE_UP) + C2S(NAN_DATA_PATH_SEC_INFO) + C2S(NAN_VERSION_INFO) + default: + return "UNKNOWN_NAN_CMD"; + } +} + +static const char *NanAttrToString(u16 cmd) +{ + switch (cmd) { + C2S(NAN_ATTRIBUTE_HEADER) + C2S(NAN_ATTRIBUTE_HANDLE) + C2S(NAN_ATTRIBUTE_TRANSAC_ID) + C2S(NAN_ATTRIBUTE_5G_SUPPORT) + C2S(NAN_ATTRIBUTE_CLUSTER_LOW) + C2S(NAN_ATTRIBUTE_CLUSTER_HIGH) + C2S(NAN_ATTRIBUTE_SID_BEACON) + C2S(NAN_ATTRIBUTE_SYNC_DISC_5G_BEACON) + C2S(NAN_ATTRIBUTE_RSSI_CLOSE) + C2S(NAN_ATTRIBUTE_RSSI_MIDDLE) + C2S(NAN_ATTRIBUTE_RSSI_PROXIMITY) + C2S(NAN_ATTRIBUTE_HOP_COUNT_LIMIT) + C2S(NAN_ATTRIBUTE_RANDOM_FACTOR) + C2S(NAN_ATTRIBUTE_MASTER_PREF) + C2S(NAN_ATTRIBUTE_PERIODIC_SCAN_INTERVAL) + C2S(NAN_ATTRIBUTE_PUBLISH_ID) + C2S(NAN_ATTRIBUTE_TTL) + C2S(NAN_ATTRIBUTE_PERIOD) + C2S(NAN_ATTRIBUTE_REPLIED_EVENT_FLAG) + C2S(NAN_ATTRIBUTE_PUBLISH_TYPE) + C2S(NAN_ATTRIBUTE_TX_TYPE) + C2S(NAN_ATTRIBUTE_PUBLISH_COUNT) + C2S(NAN_ATTRIBUTE_SERVICE_NAME_LEN) + C2S(NAN_ATTRIBUTE_SERVICE_NAME) + C2S(NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN) + C2S(NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO) + C2S(NAN_ATTRIBUTE_RX_MATCH_FILTER_LEN) + C2S(NAN_ATTRIBUTE_RX_MATCH_FILTER) + C2S(NAN_ATTRIBUTE_TX_MATCH_FILTER_LEN) + C2S(NAN_ATTRIBUTE_TX_MATCH_FILTER) + C2S(NAN_ATTRIBUTE_SUBSCRIBE_ID) + C2S(NAN_ATTRIBUTE_SUBSCRIBE_TYPE) + C2S(NAN_ATTRIBUTE_SERVICERESPONSEFILTER) + C2S(NAN_ATTRIBUTE_SERVICERESPONSEINCLUDE) + C2S(NAN_ATTRIBUTE_USESERVICERESPONSEFILTER) + C2S(NAN_ATTRIBUTE_SSIREQUIREDFORMATCHINDICATION) + C2S(NAN_ATTRIBUTE_SUBSCRIBE_MATCH) + C2S(NAN_ATTRIBUTE_SUBSCRIBE_COUNT) + C2S(NAN_ATTRIBUTE_MAC_ADDR) + C2S(NAN_ATTRIBUTE_MAC_ADDR_LIST) + C2S(NAN_ATTRIBUTE_MAC_ADDR_LIST_NUM_ENTRIES) + C2S(NAN_ATTRIBUTE_PUBLISH_MATCH) + C2S(NAN_ATTRIBUTE_ENABLE_STATUS) + C2S(NAN_ATTRIBUTE_JOIN_STATUS) + C2S(NAN_ATTRIBUTE_ROLE) + C2S(NAN_ATTRIBUTE_MASTER_RANK) + C2S(NAN_ATTRIBUTE_ANCHOR_MASTER_RANK) + C2S(NAN_ATTRIBUTE_CNT_PEND_TXFRM) + C2S(NAN_ATTRIBUTE_CNT_BCN_TX) + C2S(NAN_ATTRIBUTE_CNT_BCN_RX) + C2S(NAN_ATTRIBUTE_CNT_SVC_DISC_TX) + C2S(NAN_ATTRIBUTE_CNT_SVC_DISC_RX) + C2S(NAN_ATTRIBUTE_AMBTT) + C2S(NAN_ATTRIBUTE_CLUSTER_ID) + C2S(NAN_ATTRIBUTE_INST_ID) + C2S(NAN_ATTRIBUTE_OUI) + C2S(NAN_ATTRIBUTE_STATUS) + C2S(NAN_ATTRIBUTE_DE_EVENT_TYPE) + C2S(NAN_ATTRIBUTE_MERGE) + C2S(NAN_ATTRIBUTE_IFACE) + C2S(NAN_ATTRIBUTE_CHANNEL) + C2S(NAN_ATTRIBUTE_PEER_ID) + C2S(NAN_ATTRIBUTE_NDP_ID) + C2S(NAN_ATTRIBUTE_SECURITY) + C2S(NAN_ATTRIBUTE_QOS) + C2S(NAN_ATTRIBUTE_RSP_CODE) + C2S(NAN_ATTRIBUTE_INST_COUNT) + C2S(NAN_ATTRIBUTE_PEER_DISC_MAC_ADDR) + C2S(NAN_ATTRIBUTE_PEER_NDI_MAC_ADDR) + C2S(NAN_ATTRIBUTE_IF_ADDR) + C2S(NAN_ATTRIBUTE_WARMUP_TIME) + C2S(NAN_ATTRIBUTE_RANGING_RESULT) + C2S(NAN_ATTRIBUTE_RANGING_INDICATION) + C2S(NAN_ATTRIBUTE_SDEA_SERVICE_SPECIFIC_INFO_LEN) + C2S(NAN_ATTRIBUTE_SDEA_SERVICE_SPECIFIC_INFO) + C2S(NAN_ATTRIBUTE_RANDOMIZATION_INTERVAL) + C2S(NAN_ATTRIBUTE_ENABLE_MERGE) + + default: + return "NAN_ATTRIBUTE_UNKNOWN"; + } +} + +NanResponseType get_response_type(WIFI_SUB_COMMAND nan_subcmd) +{ + NanResponseType response_type; + + switch(nan_subcmd) { + case NAN_SUBCMD_ENABLE: + response_type = NAN_RESPONSE_ENABLED; + break; + case NAN_SUBCMD_DISABLE: + response_type = NAN_RESPONSE_DISABLED; + break; + case NAN_SUBCMD_PUBLISH: + response_type = NAN_RESPONSE_PUBLISH; + break; + case NAN_SUBCMD_SUBSCRIBE: + response_type = NAN_RESPONSE_SUBSCRIBE; + break; + case NAN_SUBCMD_PUBLISH_CANCEL: + response_type = NAN_RESPONSE_PUBLISH_CANCEL; + break; + case NAN_SUBCMD_SUBSCRIBE_CANCEL: + response_type = NAN_RESPONSE_SUBSCRIBE_CANCEL; + break; + case NAN_SUBCMD_TRANSMIT_FOLLOWUP: + response_type = NAN_RESPONSE_TRANSMIT_FOLLOWUP; + break; + case NAN_SUBCMD_CONFIG: + response_type = NAN_RESPONSE_CONFIG; + break; + case NAN_SUBCMD_TCA: + response_type = NAN_RESPONSE_TCA; + break; + case NAN_SUBCMD_STATS: + response_type = NAN_RESPONSE_STATS; + break; + case NAN_SUBCMD_DATA_PATH_IFACE_CREATE: + response_type = NAN_DP_INTERFACE_CREATE; + break; + case NAN_SUBCMD_DATA_PATH_IFACE_DELETE: + response_type = NAN_DP_INTERFACE_DELETE; + break; + case NAN_SUBCMD_DATA_PATH_REQUEST: + response_type = NAN_DP_INITIATOR_RESPONSE; + break; + case NAN_SUBCMD_DATA_PATH_RESPONSE: + response_type = NAN_DP_RESPONDER_RESPONSE; + break; + case NAN_SUBCMD_DATA_PATH_END: + response_type = NAN_DP_END; + break; + case NAN_SUBCMD_GET_CAPABILITIES: + response_type = NAN_GET_CAPABILITIES; + break; + default: + /* unknown response for a command */ + response_type = NAN_RESPONSE_ERROR; + break; + } + + return response_type; +} + +static int get_svc_hash(unsigned char *svc_name, + u16 svc_name_len, u8 *svc_hash, u16 svc_hash_len) +{ + SHA256_CTX sha_ctx; + u8 sha_hash[SHA256_DIGEST_LENGTH]; + unsigned char *p; + int len = svc_name_len; + + if (!svc_name || !svc_hash) { + ALOGE("Bad arguments!!\n"); + return WIFI_ERROR_UNKNOWN; + } + + if (svc_hash_len < NAN_SVC_HASH_SIZE) { + ALOGE("Bad len!!\n"); + return WIFI_ERROR_UNKNOWN; + } + for (p = svc_name; *p; p++) + { + *p = tolower((int)*p); + } + SHA256_Init(&sha_ctx); + SHA256_Update(&sha_ctx, svc_name, len); + SHA256_Final(sha_hash, &sha_ctx); + + memcpy(svc_hash, sha_hash, NAN_SVC_HASH_SIZE); + ALOGI("svc_name: %s\n", svc_name); + prhex("svc_hash:", svc_hash, NAN_SVC_HASH_SIZE); + + return WIFI_SUCCESS; +} + +static int dump_NanEnableRequest(NanEnableRequest* msg) +{ + ALOGI("%s: Dump NanEnableRequest msg:\n", __func__); + + if (msg == NULL) { + ALOGE("Invalid msg\n"); + return WIFI_ERROR_UNKNOWN; + } + + ALOGI("master_pref=%u\n", msg->master_pref); + ALOGI("cluster_low=%u\n", msg->cluster_low); + ALOGI("cluster_high=%u\n", msg->cluster_high); + ALOGI("config_support_5g=%u\n", msg->config_support_5g); + ALOGI("support_5g_val=%u\n", msg->support_5g_val); + ALOGI("config_sid_beacon=%u\n", msg->config_sid_beacon); + ALOGI("sid beacon=%u\n", msg->sid_beacon_val); + ALOGI("config_sub_sid_beacon=%u\n", msg->config_subscribe_sid_beacon); + ALOGI("sub sid beacon=%u\n", msg->subscribe_sid_beacon_val); + ALOGI("config_2dot4g_rssi_close=%u\n", msg->config_2dot4g_rssi_close); + ALOGI("rssi_close_2dot4g_val=%u\n", msg->rssi_close_2dot4g_val); + ALOGI("config_2dot4g_rssi_middle=%u\n", msg->config_2dot4g_rssi_middle); + ALOGI("rssi_middle_2dot4g_val=%u\n", msg->rssi_middle_2dot4g_val); + ALOGI("config_2dot4g_rssi_proximity=%u\n", msg->config_2dot4g_rssi_proximity); + ALOGI("rssi_proximity_2dot4g_val=%u\n", msg->rssi_proximity_2dot4g_val); + ALOGI("config_hop_count_limit=%u\n", msg->config_hop_count_limit); + ALOGI("hop_count_limit_val=%u\n", msg->hop_count_limit_val); + ALOGI("config_2dot4g_support=%u\n", msg->config_2dot4g_support); + ALOGI("support_2dot4g_val=%u\n", msg->support_2dot4g_val); + ALOGI("config_2dot4g_beacons=%u\n", msg->config_2dot4g_beacons); + ALOGI("beacon_2dot4g_val=%u\n", msg->beacon_2dot4g_val); + ALOGI("config_2dot4g_sdf=%u\n", msg->config_2dot4g_sdf); + ALOGI("sdf_2dot4g_val=%u\n", msg->sdf_2dot4g_val); + ALOGI("config_5g_beacons=%u\n", msg->config_5g_beacons); + ALOGI("beacon_5g_val=%u\n", msg->beacon_5g_val); + ALOGI("config_5g_sdf=%u\n", msg->config_5g_sdf); + ALOGI("config_5g_rssi_close=%u\n", msg->config_5g_rssi_close); + ALOGI("rssi_close_5g_val=%u\n", msg->rssi_close_5g_val); + ALOGI("config_5g_rssi_middle=%u\n", msg->config_5g_rssi_middle); + ALOGI("rssi_middle_5g_val=%u\n", msg->rssi_middle_5g_val); + ALOGI("config_5g_rssi_close_proximity=%u\n", msg->config_5g_rssi_close_proximity); + ALOGI("rssi_close_proximity_5g_val=%u\n", msg->rssi_close_proximity_5g_val); + ALOGI("config_rssi_window_size=%u\n", msg->config_rssi_window_size); + ALOGI("rssi_window_size_val=%u\n", msg->rssi_window_size_val); + ALOGI("config_oui=%u\n", msg->config_oui); + ALOGI("oui_val=%u\n", msg->oui_val); + ALOGI("config_intf_addr=%u\n", msg->config_intf_addr); + ALOGI("intf_addr_val=" MACSTR "\n", MAC2STR(msg->intf_addr_val)); + ALOGI("config_cluster_attribute_val=%u\n", msg->config_cluster_attribute_val); + ALOGI("config_scan_params=%u\n", msg->config_scan_params); + if (msg->config_scan_params) { + ALOGI("dwell_time=%u\n", msg->scan_params_val.dwell_time[0]); + ALOGI("scan_period=%u\n", msg->scan_params_val.scan_period[0]); + } + ALOGI("config_random_factor_force=%u\n", msg->config_random_factor_force); + ALOGI("random_factor_force_val=%u\n", msg->random_factor_force_val); + ALOGI("config_hop_count_force=%u\n", msg->config_hop_count_force); + ALOGI("config_24g_channel=%u\n", msg->config_24g_channel); + ALOGI("channel_24g_val=%u\n", msg->channel_24g_val); + ALOGI("config_5g_channel=%u\n", msg->config_5g_channel); + ALOGI("channel_5g_val=%u\n", msg->channel_5g_val); + ALOGI("config_dw.config_2dot4g_dw_band=%u\n", msg->config_dw.config_2dot4g_dw_band); + if (msg->config_dw.config_2dot4g_dw_band) { + ALOGI("dw_2dot4g_interval_val=%u\n", msg->config_dw.dw_2dot4g_interval_val); + } + ALOGI("config_dw.config_5g_dw_band=%u\n", msg->config_dw.config_5g_dw_band); + if (msg->config_dw.config_5g_dw_band) { + ALOGI("dw_5g_interval_val=%u\n", msg->config_dw.dw_5g_interval_val); + } + ALOGI("discovery_indication_cfg=%u\n", msg->discovery_indication_cfg); + ALOGI("config_ndpe_attr=%u\n", msg->config_ndpe_attr); + if (msg->config_ndpe_attr) { + ALOGI("use_ndpe_attr=%u\n", msg->use_ndpe_attr); + } + ALOGI("config_discovery_beacon_int=%u\n", msg->config_discovery_beacon_int); + if (msg->config_discovery_beacon_int) { + ALOGI("discovery beacon interval =%u\n", msg->discovery_beacon_interval); + } + ALOGI("config_nss=%u\n", msg->config_nss); + if (msg->config_nss) { + ALOGI("nss =%u\n", msg->nss); + } + ALOGI("config_enable_ranging =%u\n", msg->config_enable_ranging); + if (msg->config_enable_ranging) { + ALOGI("enable_ranging =%u\n", msg->enable_ranging); + } + ALOGI("config_dw_early_termination =%u\n", msg->config_dw_early_termination); + if (msg->config_dw_early_termination) { + ALOGI("enable_dw_termination =%u\n", msg->enable_dw_termination); + } + ALOGI("config_disc_mac_addr_randomization=%u\n", msg->config_disc_mac_addr_randomization); + if (msg->config_disc_mac_addr_randomization) { + ALOGI("disc_mac_addr_rand_interval_sec =%u\n", msg->disc_mac_addr_rand_interval_sec); + } + ALOGI("config_enable_instant_mode =%u\n", msg->config_enable_instant_mode); + if (msg->config_enable_instant_mode) { + ALOGI("enable_instant_mode =%u\n", msg->enable_instant_mode); + } + ALOGI("config_instant_mode_channel=%u\n", msg->config_instant_mode_channel); + if (msg->config_instant_mode_channel) { + ALOGI("instant_mode_channel=%u\n", msg->instant_mode_channel); + } + + return WIFI_SUCCESS; +} + +#ifdef CONFIG_BRCM +static int dump_NanConfigRequestRequest(NanConfigRequest* msg) +{ + ALOGI("%s: Dump NanConfigRequest msg:\n", __func__); + + if (msg == NULL) { + ALOGE("Invalid msg\n"); + return WIFI_ERROR_UNKNOWN; + } + + ALOGI("master_pref=%u\n", msg->master_pref); + ALOGI("sid beacon=%u\n", msg->sid_beacon); + ALOGI("config_sub_sid_beacon=%u\n", msg->config_subscribe_sid_beacon); + ALOGI("sub sid beacon=%u\n", msg->subscribe_sid_beacon_val); + ALOGI("rssi_proximity=%u\n", msg->rssi_proximity); + ALOGI("rssi_close_proximity_5g_val=%u\n", msg->rssi_close_proximity_5g_val); + ALOGI("rssi_window_size_val=%u\n", msg->rssi_window_size_val); + ALOGI("scan_params_val.dwell_time[0]=%u\n", msg->scan_params_val.dwell_time[0]); + ALOGI("scan_params_val.scan_period[0]=%u\n", msg->scan_params_val.scan_period[0]); + ALOGI("config_scan_params=%u\n", msg->config_scan_params); + ALOGI("random_factor_force_val=%u\n", msg->random_factor_force_val); + ALOGI("hop_count_force_val=%u\n", msg->hop_count_force_val); + ALOGI("fam_val.numchans=%u\n", msg->fam_val.numchans); + ALOGI("fam_val.famchan[0].entry_control=%u\n", msg->fam_val.famchan[0].entry_control); + ALOGI("fam_val.famchan[0].class_val=%u\n", msg->fam_val.famchan[0].class_val); + ALOGI("fam_val.famchan[0].channel=%u\n", msg->fam_val.famchan[0].channel); + ALOGI("fam_val.famchan[0].mapid=%u\n", msg->fam_val.famchan[0].mapid); + ALOGI("fam_val.famchan[0].avail_interval_bitmap=%u\n", msg->fam_val.famchan[0].avail_interval_bitmap); + ALOGI("config_dw.config_2dot4g_dw_band=%u\n", msg->config_dw.config_2dot4g_dw_band); + if (msg->config_dw.config_2dot4g_dw_band) { + ALOGI("dw_2dot4g_interval_val=%u\n", msg->config_dw.dw_2dot4g_interval_val); + } + ALOGI("config_dw.config_5g_dw_band=%u\n", msg->config_dw.config_5g_dw_band); + if (msg->config_dw.config_5g_dw_band) { + ALOGI("dw_5g_interval_val=%u\n", msg->config_dw.dw_5g_interval_val); + } + ALOGI("discovery_indication_cfg=%u\n", msg->discovery_indication_cfg); + ALOGI("config_ndpe_attr=%u\n", msg->config_ndpe_attr); + if (msg->config_ndpe_attr) { + ALOGI("use_ndpe_attr=%u\n", msg->use_ndpe_attr); + } + ALOGI("config_discovery_beacon_int=%u\n", msg->config_discovery_beacon_int); + if (msg->config_discovery_beacon_int) { + ALOGI("discovery beacon interval =%u\n", msg->discovery_beacon_interval); + } + ALOGI("config_nss=%u\n", msg->config_nss); + if (msg->config_nss) { + ALOGI("nss =%u\n", msg->nss); + } + ALOGI("config_enable_ranging =%u\n", msg->config_enable_ranging); + if (msg->config_enable_ranging) { + ALOGI("enable_ranging =%u\n", msg->enable_ranging); + } + ALOGI("config_dw_early_termination =%u\n", msg->config_dw_early_termination); + if (msg->config_dw_early_termination) { + ALOGI("enable_dw_termination =%u\n", msg->enable_dw_termination); + } + + ALOGI("config_disc_mac_addr_randomization=%u\n", msg->config_disc_mac_addr_randomization); + if (msg->config_disc_mac_addr_randomization) { + ALOGI("disc_mac_addr_rand_interval_sec =%u\n", msg->disc_mac_addr_rand_interval_sec); + } + ALOGI("config_enable_instant_mode =%u\n", msg->config_enable_instant_mode); + if (msg->config_enable_instant_mode) { + ALOGI("enable_instant_mode =%u\n", msg->enable_instant_mode); + } + ALOGI("config_instant_mode_channel=%u\n", msg->config_instant_mode_channel); + if (msg->config_instant_mode_channel) { + ALOGI("instant_mode_channel=%u\n", msg->instant_mode_channel); + } + + return WIFI_SUCCESS; +} + +static int dump_NanPublishRequest(NanPublishRequest* msg) +{ + ALOGI("%s: Dump NanPublishRequest msg:\n", __func__); + if (msg == NULL) { + ALOGE("Invalid msg\n"); + return WIFI_ERROR_UNKNOWN; + } + ALOGI("publish_id=%u\n", msg->publish_id); + ALOGI("ttl=%u\n", msg->ttl); + ALOGI("period=%u\n", msg->period); + ALOGI("publish_type=%u\n", msg->publish_type); + ALOGI("tx_type=%u\n", msg->tx_type); + ALOGI("publish_count=%u\n", msg->publish_count); + ALOGI("publish_match_indicator=%u\n", msg->publish_match_indicator); + ALOGI("service_responder_policy=%u\n", msg->service_responder_policy); + ALOGI("service_name_len=%u\n", msg->service_name_len); + if (msg->service_name_len) { + ALOGI("service_name=%s\n", msg->service_name); + } + ALOGI("service_specific_info_len=%u\n", msg->service_specific_info_len); + if (msg->service_specific_info_len) { + ALOGI("service_specific_info=%s\n", msg->service_specific_info); + } + ALOGI("rx_match_filter_len=%u\n", msg->rx_match_filter_len); + if (msg->rx_match_filter_len) { + prhex("rx_match_filter", msg->rx_match_filter, msg->rx_match_filter_len); + } + ALOGI("tx_match_filter_len=%u\n", msg->tx_match_filter_len); + if (msg->tx_match_filter_len) { + prhex("tx_match_filter", msg->tx_match_filter, msg->tx_match_filter_len); + } + ALOGI("rssi_threshold_flag=%u\n", msg->rssi_threshold_flag); + ALOGI("connmap=%u\n", msg->connmap); + ALOGI("recv_indication_cfg=%u\n", msg->recv_indication_cfg); + ALOGI("cipher_type=%u\n", msg->cipher_type); + ALOGI("key_info: key_type =%u\n", msg->key_info.key_type); + ALOGI("key_info: pmk info=%s\n", msg->key_info.body.pmk_info.pmk); + ALOGI("key_info: passphrase_info=%s\n", msg->key_info.body.passphrase_info.passphrase); + ALOGI("scid_len=%u\n", msg->scid_len); + if (msg->scid_len) { + ALOGI("scid=%s\n", msg->scid); + } + ALOGI("NanSdeaCtrlParams NdpType=%u\n", msg->sdea_params.ndp_type); + ALOGI("NanSdeaCtrlParams security_cfg=%u\n", msg->sdea_params.security_cfg); + ALOGI("NanSdeaCtrlParams ranging_state=%u\n", msg->sdea_params.ranging_state); + ALOGI("NanSdeaCtrlParams range_report=%u\n", msg->sdea_params.range_report); + ALOGI("NanRangingCfg ranging_interval_msec=%u\n", msg->ranging_cfg.ranging_interval_msec); + ALOGI("NanRangingCfg config_ranging_indications=%u\n", msg->ranging_cfg.config_ranging_indications); + ALOGI("NanRangingCfg distance_ingress_mm=%u\n", msg->ranging_cfg.distance_ingress_mm); + ALOGI("NanRangingCfg distance_egress_mm=%u\n", msg->ranging_cfg.distance_egress_mm); + ALOGI("NanRangingAutoResponse = %u\n", msg->ranging_auto_response); + ALOGI("range_response_cfg=%u\n", msg->range_response_cfg.ranging_response); + + ALOGI("sdea_service_specific_info_len=%u\n", msg->sdea_service_specific_info_len); + if (msg->sdea_service_specific_info_len) { + ALOGI("sdea_service_specific_info=%s\n", msg->sdea_service_specific_info); + } + + return WIFI_SUCCESS; +} + +static int dump_NanSubscribeRequest(NanSubscribeRequest* msg) +{ + ALOGI("%s: Dump NanSubscribeRequest msg:\n", __func__); + u8 i = 0; + if (msg == NULL) { + ALOGE("Invalid msg\n"); + return WIFI_ERROR_UNKNOWN; + } + ALOGI("subscribe_id=%u\n", msg->subscribe_id); + ALOGI("ttl=%u\n", msg->ttl); + ALOGI("period=%u\n", msg->period); + ALOGI("subscribe_type=%u\n", msg->subscribe_type); + ALOGI("serviceResponseFilter=%u\n", msg->serviceResponseFilter); + ALOGI("serviceResponseInclude=%u\n", msg->serviceResponseInclude); + ALOGI("useServiceResponseFilter=%u\n", msg->useServiceResponseFilter); + ALOGI("ssiRequiredForMatchIndication=%u\n", msg->ssiRequiredForMatchIndication); + ALOGI("subscribe_count=%u\n", msg->subscribe_count); + ALOGI("subscribe_match_indicator=%u\n", msg->subscribe_match_indicator); + ALOGI("service_name_len=%u\n", msg->service_name_len); + if (msg->service_name_len) + ALOGI("service_name=%s\n", msg->service_name); + ALOGI("service_specific_info_len=%u\n", msg->service_specific_info_len); + if (msg->service_specific_info_len) + ALOGI("service_specific_info=%s\n", msg->service_specific_info); + ALOGI("rx_match_filter_len=%u\n", msg->rx_match_filter_len); + if (msg->rx_match_filter_len) + prhex("rx_match_filter", msg->rx_match_filter, msg->rx_match_filter_len); + ALOGI("tx_match_filter_len=%u\n", msg->tx_match_filter_len); + if (msg->tx_match_filter_len) + prhex("tx_match_filter", msg->tx_match_filter, msg->tx_match_filter_len); + ALOGI("rssi_threshold_flag=%u\n", msg->rssi_threshold_flag); + ALOGI("connmap=%u\n", msg->connmap); + ALOGI("num_intf_addr_present=%u\n", msg->num_intf_addr_present); + if (msg->num_intf_addr_present) { + for (i = 0; i < NAN_MAX_SUBSCRIBE_MAX_ADDRESS; i++) { + ALOGI("peer_disc_mac_addr=" MACSTR "\n", MAC2STR(msg->intf_addr[i])); + } + } + ALOGI("recv_indication_cfg=%u\n", msg->recv_indication_cfg); + ALOGI("cipher_type=%u\n", msg->cipher_type); + ALOGI("key_info: key_type =%u\n", msg->key_info.key_type); + ALOGI("key_info: pmk info=%s\n", msg->key_info.body.pmk_info.pmk); + ALOGI("key_info: passphrase_info=%s\n", msg->key_info.body.passphrase_info.passphrase); + ALOGI("scid_len=%u\n", msg->scid_len); + if (msg->scid_len) { + ALOGI("scid=%s\n", msg->scid); + } + ALOGI("NanSdeaCtrlParams NdpType=%u\n", msg->sdea_params.ndp_type); + ALOGI("NanSdeaCtrlParams security_cfg=%u\n", msg->sdea_params.security_cfg); + ALOGI("NanSdeaCtrlParams ranging_state=%u\n", msg->sdea_params.ranging_state); + ALOGI("NanSdeaCtrlParams range_report=%u\n", msg->sdea_params.range_report); + ALOGI("NanRangingCfg ranging_interval_msec=%u\n", msg->ranging_cfg.ranging_interval_msec); + ALOGI("NanRangingCfg config_ranging_indications=%u\n", msg->ranging_cfg.config_ranging_indications); + ALOGI("NanRangingCfg distance_ingress_mm=%u\n", msg->ranging_cfg.distance_ingress_mm); + ALOGI("NanRangingCfg distance_egress_mm=%u\n", msg->ranging_cfg.distance_egress_mm); + ALOGI("NanRangingAutoResponse = %u\n", msg->ranging_auto_response); + ALOGI("range_response = %u\n", msg->range_response_cfg.ranging_response); + + ALOGI("sdea_service_specific_info_len=%u\n", msg->sdea_service_specific_info_len); + if (msg->sdea_service_specific_info_len) + ALOGI("sdea_service_specific_info=%s\n", msg->sdea_service_specific_info); + + return WIFI_SUCCESS; +} + +static int dump_NanTransmitFollowupRequest(NanTransmitFollowupRequest* msg) +{ + ALOGI("%s: Dump NanTransmitFollowupRequest msg:\n", __func__); + if (msg == NULL) { + ALOGE("Invalid msg\n"); + return WIFI_ERROR_UNKNOWN; + } + ALOGI("publish_subscribe_id=%u\n", msg->publish_subscribe_id); + ALOGI("requestor_instance_id=%u\n", msg->requestor_instance_id); + ALOGI("addr=" MACSTR "\n", MAC2STR(msg->addr)); + ALOGI("priority=%u\n", msg->priority); + ALOGI("dw_or_faw=%u\n", msg->dw_or_faw); + ALOGI("service_specific_info_len=%u\n", msg->service_specific_info_len); + if (msg->service_specific_info_len) + ALOGI("service_specific_info=%s\n", msg->service_specific_info); + ALOGI("recv_indication_cfg=%u\n", msg->recv_indication_cfg); + ALOGI("sdea_service_specific_info_len=%u\n", msg->sdea_service_specific_info_len); + if (msg->sdea_service_specific_info_len) + ALOGI("sdea_service_specific_info=%s\n", msg->sdea_service_specific_info); + + return WIFI_SUCCESS; +} + +static int dump_NanDataPathInitiatorRequest(NanDataPathInitiatorRequest* msg) +{ + ALOGI("%s: Dump NanDataPathInitiatorRequest msg:\n", __func__); + + if (msg == NULL) { + ALOGE("Invalid msg\n"); + return WIFI_ERROR_UNKNOWN; + } + + ALOGI("requestor_instance_id=%d\n", msg->requestor_instance_id); + ALOGI("channel_request_type=%d\n", msg->channel_request_type); + ALOGI("channel=%u\n", msg->channel); + ALOGI("peer_disc_mac_addr=" MACSTR "\n", MAC2STR(msg->peer_disc_mac_addr)); + ALOGI("ndp_iface=%s\n", msg->ndp_iface); + ALOGI("ndp_cfg: security_cfg =%u\n", msg->ndp_cfg.security_cfg); + ALOGI("ndp_cfg: qos_cfg=%u\n", msg->ndp_cfg.qos_cfg); + ALOGI("dp app info len=%u\n", msg->app_info.ndp_app_info_len); + if (msg->app_info.ndp_app_info_len) { + prhex("dp app info=: ", (u8*)msg->app_info.ndp_app_info, + msg->app_info.ndp_app_info_len); + } + ALOGI("cipher_type=%u\n", msg->cipher_type); + ALOGI("key_info: key_type =%u\n", msg->key_info.key_type); + ALOGI("key_info: pmk info=%s\n", msg->key_info.body.pmk_info.pmk); + ALOGI("key_info: passphrase_info=%s\n", msg->key_info.body.passphrase_info.passphrase); + ALOGI("scid_len=%u\n", msg->scid_len); + if (msg->scid_len) { + ALOGI("scid=%s\n", msg->scid); + } + if (msg->service_name_len) { + ALOGI("service_name=%s\n", msg->service_name); + } + return WIFI_SUCCESS; +} + +static int dump_NanDataPathIndicationResponse(NanDataPathIndicationResponse* msg) +{ + ALOGI("%s: Dump NanDataPathIndicationResponse msg:\n", __func__); + + if (msg == NULL) { + ALOGE("Invalid msg\n"); + return WIFI_ERROR_UNKNOWN; + } + + ALOGI("ndp_instance_id=%d\n", msg->ndp_instance_id); + ALOGI("ndp_iface=%s\n", msg->ndp_iface); + ALOGI("ndp_cfg: security_cfg =%u\n", msg->ndp_cfg.security_cfg); + ALOGI("response code =%u\n", msg->rsp_code); + ALOGI("ndp_cfg: qos_cfg=%u\n", msg->ndp_cfg.qos_cfg); + ALOGI("dp app info len=%u\n", msg->app_info.ndp_app_info_len); + if (msg->app_info.ndp_app_info_len) { + prhex("dp app info=: ", (u8*)msg->app_info.ndp_app_info, + msg->app_info.ndp_app_info_len); + } + ALOGI("cipher_type=%u\n", msg->cipher_type); + ALOGI("key_info: key_type =%u\n", msg->key_info.key_type); + ALOGI("key_info: pmk info=%s\n", msg->key_info.body.pmk_info.pmk); + ALOGI("key_info: passphrase_info=%s\n", msg->key_info.body.passphrase_info.passphrase); + ALOGI("service_name_len=%u\n", msg->service_name_len); + ALOGI("scid_len=%u\n", msg->scid_len); + if (msg->scid_len) { + ALOGI("scid=%s\n", msg->scid); + } + if (msg->service_name_len) { + ALOGI("service_name=%s\n", msg->service_name); + } + return WIFI_SUCCESS; +} +#endif /* CONFIG_BRCM */ + +void nan_reset_dbg_counters() +{ + memset(&counters, 0, sizeof(counters)); +} + +/////////////////////////////////////////////////////////////////////////////// +wifi_error nan_enable_request(transaction_id id, + wifi_interface_handle iface, NanEnableRequest* msg) +{ + wifi_error ret = WIFI_SUCCESS; + wifi_handle handle = getWifiHandle(iface); + NanRequestType cmdType = NAN_REQUEST_ENABLE; + + ALOGI("Enabling Nan, Handle = %p\n", handle); + +#ifdef CONFIG_BRCM + // check up nan enable params from Nan manager level + dump_NanEnableRequest(msg); +#endif /* CONFIG_BRCM */ + nan_reset_dbg_counters(); + /* XXX: WAR posting async enable response */ + //NanMacControl *cmd = new NanMacControl(iface, id, (void *)msg, cmdType); + NanMacControl *cmd = (NanMacControl*)(info.nan_mac_control); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + cmd->setType(cmdType); + cmd->setId(id); + cmd->setMsg((void *)msg); + ret = (wifi_error)cmd->start(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in start, error = %d\n", __func__, ret); + } + //cmd->releaseRef(); + return ret; +} + +void nan_dump_dbg_counters() +{ + ALOGI("Num Data Path Requests %d\n", counters.dp_req); + ALOGI("Num Data Path Responses %d\n", counters.dp_resp); + ALOGI("Num Data Path Confirms %d\n", counters.dp_confirm_evt); + ALOGI("Num Data Path Request Events %d\n", counters.dp_req_evt); + ALOGI("Num Transmit Requests %d\n", counters.transmit_req); + ALOGI("Num Followup Transmits Recvd %d\n", counters.transmit_recv); + ALOGI("Num Transmit Success %d\n", counters.transmit_txs); +} + +wifi_error nan_disable_request(transaction_id id, + wifi_interface_handle iface) +{ + wifi_handle handle = getWifiHandle(iface); + NanRequestType cmdType = NAN_REQUEST_DISABLE; + wifi_error ret = WIFI_SUCCESS; + + ALOGI("Disabling Nan, Handle = %p\n", handle); + NanMacControl *cmd = new NanMacControl(iface, id, NULL, cmdType); + NanMacControl *mac_prim = (NanMacControl*)(info.nan_mac_control); + + if (id != NAN_MAC_INVALID_TRANSID) { + ALOGE("Disable NAN MAC transId= %d\n", id); + mac_prim->setId(id); + } else { + ALOGE("Invalid transId= %d cur= %d\n", id, mac_prim->getId()); + } + + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + + nan_dump_dbg_counters(); + + ret = (wifi_error)cmd->cancel(); + if (ret != WIFI_SUCCESS) { + ALOGE("cancel failed, error = %d\n", ret); + } else { + ALOGE("Deinitializing Nan Mac Control = %p\n", cmd); + } + cmd->releaseRef(); + return ret; +} + +wifi_error nan_publish_request(transaction_id id, + wifi_interface_handle iface, NanPublishRequest* msg) +{ + wifi_error ret = WIFI_SUCCESS; + wifi_handle handle = getWifiHandle(iface); + + ALOGI("Publish Nan, halHandle = %p\n", handle); +#ifdef CONFIG_BRCM + dump_NanPublishRequest(msg); +#endif /* CONFIG_BRCM */ + + NanRequestType cmdType = NAN_REQUEST_PUBLISH; + NanDiscEnginePrimitive *cmd = new NanDiscEnginePrimitive(iface, id, (void *)msg, cmdType); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + ret = (wifi_error)cmd->start(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in start, error = %d\n", __func__, ret); + } + cmd->releaseRef(); + return ret; +} + +/* Function to send NAN request to the wifi driver */ +wifi_error nan_publish_cancel_request(transaction_id id, + wifi_interface_handle iface, NanPublishCancelRequest* msg) +{ + wifi_error ret = WIFI_SUCCESS; + NanDiscEnginePrimitive *cmd; + NanRequestType cmdType = NAN_REQUEST_PUBLISH_CANCEL; + + ALOGE("Cancellling publish request %d\n", msg->publish_id); + cmd = new NanDiscEnginePrimitive(iface, id, (void *)msg, cmdType); + cmd->setInstId(msg->publish_id); + cmd->setType(cmdType); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + + ret = (wifi_error)cmd->start(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in start, error = %d\n", __func__, ret); + } + cmd->releaseRef(); + return ret; +} + +/* Function to send NAN request to the wifi driver */ +wifi_error nan_subscribe_request(transaction_id id, + wifi_interface_handle iface, NanSubscribeRequest* msg) +{ + wifi_error ret = WIFI_SUCCESS; + wifi_handle handle = getWifiHandle(iface); + ALOGI("Subscribe Nan, halHandle = %p handle[%d]\n", handle, msg->subscribe_id); + NanDiscEnginePrimitive *cmd; +#ifdef CONFIG_BRCM + dump_NanSubscribeRequest(msg); +#endif /* CONFIG_BRCM */ + + NanRequestType cmdType = NAN_REQUEST_SUBSCRIBE; + cmd = new NanDiscEnginePrimitive(iface, id, (void *)msg, cmdType); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + ret = (wifi_error)cmd->start(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in start, error = %d\n", __func__, ret); + } + cmd->releaseRef(); + return ret; + +} + +/* Function to send NAN request to the wifi driver.*/ +wifi_error nan_subscribe_cancel_request(transaction_id id, + wifi_interface_handle iface, NanSubscribeCancelRequest* msg) +{ + wifi_error ret = WIFI_SUCCESS; + NanDiscEnginePrimitive *cmd; + NanRequestType cmdType = NAN_REQUEST_SUBSCRIBE_CANCEL; + + ALOGE("creating new instance + %d\n", msg->subscribe_id); + cmd = new NanDiscEnginePrimitive(iface, id, (void *)msg, cmdType); + cmd->setInstId(msg->subscribe_id); + cmd->setType(cmdType); + ret = (wifi_error)cmd->start(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in start, error = %d\n", __func__, ret); + } + cmd->releaseRef(); + + return ret; +} + +/* Function to send nan transmit followup Request to the wifi driver.*/ +wifi_error nan_transmit_followup_request(transaction_id id, + wifi_interface_handle iface, NanTransmitFollowupRequest* msg) +{ + NanDiscEnginePrimitive *cmd = NULL; + NanRequestType cmdType = NAN_REQUEST_TRANSMIT_FOLLOWUP; + wifi_error ret = WIFI_SUCCESS; + +#ifdef CONFIG_BRCM + dump_NanTransmitFollowupRequest(msg); +#endif /* CONFIG_BRCM */ + counters.transmit_req++; + cmd = new NanDiscEnginePrimitive(iface, id, (void *)msg, cmdType); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + cmd->setTransactionId(id); + + ret = (wifi_error)cmd->start(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in start, error = %d\n", __func__, ret); + } + cmd->releaseRef(); + return ret; +} + +/* Function to send NAN statistics request to the wifi driver */ +wifi_error nan_stats_request(transaction_id id, + wifi_interface_handle iface, NanStatsRequest* msg) +{ + wifi_handle handle = getWifiHandle(iface); + + ALOGI("Nan Stats, halHandle = %p", handle); + +#ifdef NOT_SUPPORTED + NanRequestType cmdType = NAN_REQUEST_STATS; + wifi_error ret = WIFI_SUCCESS; + NanCommand *cmd = new NanCommand(iface, id, (void *)msg, cmdType); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + ret = (wifi_error)cmd->start(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in start, error = %d\n", __func__, ret); + } + cmd->releaseRef(); + return ret; +#else + return WIFI_ERROR_NOT_SUPPORTED; +#endif +} + +/* Function to send NAN configuration request to the wifi driver */ +wifi_error nan_config_request(transaction_id id, + wifi_interface_handle iface, NanConfigRequest* msg) +{ + wifi_error ret = WIFI_SUCCESS; + wifi_handle handle = getWifiHandle(iface); + NanRequestType cmdType = NAN_REQUEST_CONFIG; + + ALOGI("Configuring Nan, halHandle = %p\n", handle); + +#ifdef CONFIG_BRCM + /* check up nan config params from Nan manager level */ + dump_NanConfigRequestRequest(msg); +#endif /* CONFIG_BRCM */ + + NanMacControl *cmd = new NanMacControl(iface, id, (void *)msg, cmdType); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + + cmd->setType(cmdType); + ret = (wifi_error)cmd->start(); + if (ret != WIFI_SUCCESS) { + ALOGE("start failed, error = %d\n", ret); + } else { + ALOGE("Initializing Nan Mac Control = %p\n", cmd); + } + cmd->releaseRef(); + return ret; +} + +/* Function to send NAN request to the wifi driver */ +wifi_error nan_tca_request(transaction_id id, + wifi_interface_handle iface, NanTCARequest* msg) +{ + wifi_handle handle = getWifiHandle(iface); + + ALOGI("Nan TCA, halHandle = %p", handle); + +#ifdef NOT_SUPPORTED + NanRequestType cmdType = NAN_REQUEST_TCA; + wifi_error ret = WIFI_SUCCESS; + NanCommand *cmd = new NanCommand(iface, id, (void *)msg, cmdType); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + + ret = (wifi_error)cmd->start(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in start, error = %d\n", __func__, ret); + } + cmd->releaseRef(); + return ret; +#else + return WIFI_ERROR_NOT_SUPPORTED; +#endif +} + +wifi_error nan_beacon_sdf_payload_request(transaction_id id, + wifi_interface_handle iface, NanBeaconSdfPayloadRequest* msg) +{ + ALOGI("Nan Beacon Sdf Payload Request"); + return WIFI_ERROR_NOT_SUPPORTED; +} + +wifi_error nan_get_capabilities(transaction_id id, wifi_interface_handle iface) +{ + wifi_error ret = WIFI_SUCCESS; + wifi_handle handle = getWifiHandle(iface); + ALOGI("Get Nan Capabilities, id=%d, halHandle=%p\n", id, handle); + + NanRequestType cmdType = NAN_REQUEST_GET_CAPABILITIES; + NanDiscEnginePrimitive *cmd = new NanDiscEnginePrimitive(iface, id, NULL, cmdType); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + + ret = (wifi_error)cmd->start(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in start, error = %d\n", __func__, ret); + } + cmd->releaseRef(); + return ret; +} +wifi_error nan_check_dhd_hal_version(wifi_interface_handle iface, + wifi_handle handle) +{ + NanRequestType cmdType = NAN_VERSION_INFO; + NanMacControl *cmd = new NanMacControl(iface, 0, NULL, cmdType); + wifi_error ret = WIFI_SUCCESS; + u32 version; + + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + + cmd->setType(cmdType); + + ret = (wifi_error)cmd->start(); + if (ret != WIFI_SUCCESS) { + ALOGI("\nVersion subcmd failed ret = %x\n", ret); + ret = WIFI_ERROR_NOT_SUPPORTED; + goto done; + } + version = cmd->getVersion(); + /* check if version handled..can support multiple versions */ + if (version == NAN_HAL_VERSION_1) { + ALOGI("\nGot the supported version %d\n", version); + current_dhd_hal_ver = version; + ret = WIFI_SUCCESS; + goto done; + } else { + ALOGI("\nGot the unsupported version %d\n", version); + ret = WIFI_ERROR_NOT_SUPPORTED; + goto done; + } +done: + cmd->releaseRef(); + return ret; +} +wifi_error nan_deinit_handler() +{ + if (info.nan_mac_control) { + /* register for Nan vendor events with info mac class*/ + NanMacControl *cmd_event = (NanMacControl*)(info.nan_mac_control); + cmd_event->unRegisterNanVendorEvents(); + delete (NanMacControl*)info.nan_mac_control; + info.nan_mac_control = NULL; + } + if (info.nan_disc_control) { + delete (NanDiscEnginePrimitive*)info.nan_disc_control; + info.nan_disc_control = NULL; + } + if (info.nan_dp_control) { + delete (NanDataPathPrimitive*)info.nan_dp_control; + info.nan_dp_control = NULL; + } + if (NAN_HANDLE(info)) { + delete GET_NAN_HANDLE(info); + NAN_HANDLE(info) = NULL; + } + ALOGI("wifi nan internal clean up done"); + return WIFI_SUCCESS; +} +wifi_error nan_register_handler(wifi_interface_handle iface, + NanCallbackHandler handlers) +{ + wifi_handle handle = getWifiHandle(iface); + if (NAN_HANDLE(info)) { + /* cleanup and re-register */ + nan_deinit_handler(); + } + ALOGI("\nChecking version compat\n"); + /* checking version compat b/w DHD and HAL */ + if (nan_check_dhd_hal_version(iface, handle) != WIFI_SUCCESS) { + ALOGE("\n Get version failed..check DHD\n"); + return WIFI_ERROR_NOT_SUPPORTED; + } + memset(&info, 0, sizeof(info)); + NAN_HANDLE(info) = new NanHandle(handle, handlers); + info.nan_mac_control = + (void*)new NanMacControl(iface, 0, NULL, NAN_REQUEST_LAST); + NULL_CHECK_RETURN(info.nan_mac_control, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + info.nan_disc_control = + (void*)new NanDiscEnginePrimitive(iface, 0, NULL, NAN_REQUEST_LAST); + NULL_CHECK_RETURN(info.nan_disc_control, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + info.nan_dp_control = + (void*)new NanDataPathPrimitive(iface, 0, NULL, NAN_REQUEST_LAST); + NULL_CHECK_RETURN(info.nan_dp_control, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + + /* register for Nan vendor events with info mac class*/ + NanMacControl *cmd_event = (NanMacControl*)(info.nan_mac_control); + cmd_event->registerNanVendorEvents(); + return WIFI_SUCCESS; +} + +wifi_error nan_get_version(wifi_handle handle, NanVersion* version) +{ + wifi_error ret = WIFI_SUCCESS; + if (version) { + *version = (NAN_MAJOR_REL_VERSION << 16 | NAN_MINOR_REL_VERSION << 8 | + NAN_PATCH_REL_VERSION); + } else { + ret = WIFI_ERROR_INVALID_ARGS; + } + + return ret; +} + + +/////////////////////////////////////////////////////////////////////////////// +class NanEventCap : public WifiCommand +{ + public: + NanEventCap(wifi_interface_handle iface, int id) + : WifiCommand("NanCommand", iface, id) + {} + + int start() + { + registerNanVendorEvents(); + return WIFI_SUCCESS; + } + + int handleResponse(WifiEvent& reply) { + return NL_SKIP; + } + void unRegisterNanVendorEvents() + { + int i = 0; + for (i = NAN_EVENT_ENABLED; i <= NAN_EVENT_DATA_END; i++) { + unregisterVendorHandler(GOOGLE_OUI, i); + } + unregisterVendorHandler(GOOGLE_OUI, NAN_ASYNC_RESPONSE_DISABLED); + unregisterVendorHandler(GOOGLE_OUI, NAN_EVENT_MATCH_EXPIRY); + } + void registerNanVendorEvents() + { + int i = 0; + for (i = NAN_EVENT_ENABLED; i <= NAN_EVENT_DATA_END; i++) { + registerVendorHandler(GOOGLE_OUI, i); + } + registerVendorHandler(GOOGLE_OUI, NAN_ASYNC_RESPONSE_DISABLED); + registerVendorHandler(GOOGLE_OUI, NAN_EVENT_MATCH_EXPIRY); + } + + int handleEvent(WifiEvent& event) { + int cmd = event.get_vendor_subcmd(); + u16 attr_type; + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + + switch(cmd) { + case NAN_EVENT_DE_EVENT: { + u16 attr_type; + NanDiscEngEventInd de_event; + memset(&de_event, 0, sizeof(NanDiscEngEventInd)); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + if (attr_type == NAN_ATTRIBUTE_CLUSTER_ID) { + memcpy(&de_event.data.cluster.addr, it.get_data(), NAN_MAC_ADDR_LEN); + ALOGI("cluster id = " MACSTR "\n", MAC2STR(de_event.data.cluster.addr)); + } else if (attr_type == NAN_ATTRIBUTE_ENABLE_STATUS) { + ALOGI("nan enable status = %u\n", it.get_u16()); + } else if (attr_type == NAN_ATTRIBUTE_JOIN_STATUS) { + ALOGI("nan joined status = %u\n", it.get_u16()); + } else if (attr_type == NAN_ATTRIBUTE_DE_EVENT_TYPE) { + u8 de_type = it.get_u8(); + ALOGI("nan de event type = %u\n", de_type); + if (de_type == NAN_EVENT_IFACE) { + de_event.event_type = NAN_EVENT_ID_DISC_MAC_ADDR; + ALOGI("received NAN_EVENT_ID_DISC_MAC_ADDR event\n"); + } else if (de_type == NAN_EVENT_START) { + de_event.event_type = NAN_EVENT_ID_STARTED_CLUSTER; + ALOGI("received NAN cluster started event\n"); + } else if (de_type == NAN_EVENT_JOIN) { + /* To be deprecated */ + de_event.event_type = NAN_EVENT_ID_JOINED_CLUSTER; + ALOGI("received join event\n"); + } else if (de_type == NAN_EVENT_ROLE_CHANGE) { + ALOGI("received device role change event\n"); + } else if (de_type == NAN_EVENT_MERGE) { + ALOGI("received Merge Event\n"); + } else { + ALOGI("received unknown DE event, [%d]\n", de_type); + } + } else if (attr_type == NAN_ATTRIBUTE_MAC_ADDR) { + memcpy(&de_event.data.cluster.addr, it.get_data(), NAN_MAC_ADDR_LEN); + memcpy(mNmi, it.get_data(), NAN_MAC_ADDR_LEN); + ALOGI("Primary discovery mac address = " MACSTR "\n", + MAC2STR(mNmi)); + } + } + + GET_NAN_HANDLE(info)->mHandlers.EventDiscEngEvent(&de_event); + break; + } + case NAN_EVENT_DISABLED: { + ALOGI("Received NAN_EVENT_DISABLED\n"); + NanDisabledInd disabled_ind; + memset(&disabled_ind, 0, sizeof(NanDisabledInd)); + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + if (attr_type == NAN_ATTRIBUTE_STATUS) { + disabled_ind.reason = (NanStatusType)it.get_u8(); + ALOGI("Nan Disable:status %u", disabled_ind.reason); + } else if (attr_type == NAN_ATTRIBUTE_REASON) { + u8 len = min(it.get_len(), sizeof(disabled_ind.nan_reason)); + memcpy(disabled_ind.nan_reason, it.get_data(), len); + ALOGI("nan disabled reason: %s, len = %d\n", + disabled_ind.nan_reason, len); + } + } + + GET_NAN_HANDLE(info)->mHandlers.EventDisabled(&disabled_ind); + unRegisterNanVendorEvents(); + break; + } + case NAN_EVENT_PUBLISH_TERMINATED: { + ALOGI("Received NAN_EVENT_PUBLISH_TERMINATED\n"); + NanPublishTerminatedInd pub_term_event; + memset(&pub_term_event, 0, sizeof(NanPublishTerminatedInd)); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (attr_type == NAN_ATTRIBUTE_PUBLISH_ID) { + pub_term_event.publish_id = it.get_u32(); + ALOGI("pub id %u", pub_term_event.publish_id); + } else if (attr_type == NAN_ATTRIBUTE_STATUS) { + pub_term_event.reason = (NanStatusType)it.get_u8(); + ALOGI("pub termination status %u", pub_term_event.reason); + } else if (attr_type == NAN_ATTRIBUTE_REASON) { + u8 len = min(it.get_len(), sizeof(pub_term_event.nan_reason)); + memcpy(pub_term_event.nan_reason, it.get_data(), len); + ALOGI("Pub termination nan reason: %s, len = %d\n", + pub_term_event.nan_reason, len); + } else { + ALOGE("Unknown attr\n"); + } + } + + GET_NAN_HANDLE(info)->mHandlers.EventPublishTerminated(&pub_term_event); + break; + } + case NAN_EVENT_SUBSCRIBE_MATCH: { + NanMatchInd subscribe_event; + memset(&subscribe_event, 0, sizeof(NanMatchInd)); + ALOGI("Received NAN_EVENT_SUBSCRIBE_MATCH\n"); + + /* By default FW is unable to cache this match */ + subscribe_event.out_of_resource_flag = true; + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (attr_type == NAN_ATTRIBUTE_SUBSCRIBE_ID) { + ALOGI("sub id: %u", it.get_u16()); + subscribe_event.publish_subscribe_id = it.get_u16(); + + } else if (attr_type == NAN_ATTRIBUTE_PUBLISH_ID) { + ALOGI("pub id %u", it.get_u32()); + subscribe_event.requestor_instance_id = it.get_u32(); + + } else if (attr_type == NAN_ATTRIBUTE_MAC_ADDR) { + memcpy(subscribe_event.addr, it.get_data(), NAN_MAC_ADDR_LEN); + ALOGI("publisher mac: " MACSTR, MAC2STR(subscribe_event.addr)); + + } else if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN) { + ALOGI("svc length: %d", it.get_u16()); + subscribe_event.service_specific_info_len = it.get_u16(); + + } else if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO) { + memcpy(subscribe_event.service_specific_info, it.get_data(), + subscribe_event.service_specific_info_len); + subscribe_event.service_specific_info + [subscribe_event.service_specific_info_len] = '\0'; + ALOGI("service info: %s", subscribe_event.service_specific_info); + + } else if (attr_type == NAN_ATTRIBUTE_TX_MATCH_FILTER_LEN) { + ALOGI("sdf match filter length: %d", subscribe_event.sdf_match_filter_len); + subscribe_event.sdf_match_filter_len = it.get_u16(); + + } else if (attr_type == NAN_ATTRIBUTE_TX_MATCH_FILTER) { + memcpy(subscribe_event.sdf_match_filter, it.get_data(), + subscribe_event.sdf_match_filter_len); + subscribe_event.sdf_match_filter + [subscribe_event.sdf_match_filter_len] = '\0'; + ALOGI("sdf match filter: %s", subscribe_event.sdf_match_filter); + } else if (attr_type == NAN_ATTRIBUTE_CIPHER_SUITE_TYPE) { + ALOGI("Peer Cipher suite type: %u", it.get_u8()); + subscribe_event.peer_cipher_type = it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_SCID_LEN) { + ALOGI("scid length %d", it.get_u32()); + subscribe_event.scid_len= it.get_u32(); + } else if (attr_type == NAN_ATTRIBUTE_SCID) { + memcpy(subscribe_event.scid, it.get_data(), + subscribe_event.scid_len); + subscribe_event.scid + [subscribe_event.scid_len] = '\0'; + ALOGI("scid: %s", subscribe_event.scid); + } else if (attr_type == NAN_ATTRIBUTE_RANGING_INDICATION) { + subscribe_event.range_info.ranging_event_type = it.get_u32(); + ALOGI("ranging indication %d", it.get_u32()); + } else if (attr_type == NAN_ATTRIBUTE_RANGING_RESULT) { + subscribe_event.range_info.range_measurement_mm = it.get_u32(); + ALOGI("ranging result %d", it.get_u32()); + } else if (attr_type == NAN_ATTRIBUTE_RSSI_PROXIMITY) { + subscribe_event.rssi_value = it.get_u8(); + ALOGI("rssi value : %u", it.get_u8()); + } else if (attr_type == NAN_ATTRIBUTE_SDEA_SERVICE_SPECIFIC_INFO_LEN) { + ALOGI("sdea svc length %d", it.get_u16()); + subscribe_event.sdea_service_specific_info_len = it.get_u16(); + } else if (attr_type == NAN_ATTRIBUTE_SDEA_SERVICE_SPECIFIC_INFO) { + memcpy(subscribe_event.sdea_service_specific_info, it.get_data(), + subscribe_event.sdea_service_specific_info_len); + subscribe_event.sdea_service_specific_info + [subscribe_event.sdea_service_specific_info_len] = '\0'; + ALOGI("sdea service info: %s", subscribe_event.sdea_service_specific_info); + } else if (attr_type == NAN_ATTRIBUTE_MATCH_OCCURRED_FLAG) { + ALOGI("match occurred flag: %u", it.get_u8()); + subscribe_event.match_occured_flag = it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_OUT_OF_RESOURCE_FLAG) { + ALOGI("Out of resource flag: %u", it.get_u8()); + subscribe_event.out_of_resource_flag = it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_SDE_CONTROL_CONFIG_DP) { + ALOGI("Peer config for data path needed: %u", it.get_u8()); + subscribe_event.peer_sdea_params.config_nan_data_path = it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_SDE_CONTROL_DP_TYPE) { + ALOGI("Data Path type: %u", it.get_u8()); + subscribe_event.peer_sdea_params.ndp_type = (NdpType)it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_SDE_CONTROL_SECURITY) { + ALOGI("Security configuration: %u", it.get_u8()); + subscribe_event.peer_sdea_params.security_cfg = + (NanDataPathSecurityCfgStatus)it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_SDE_CONTROL_RANGE_SUPPORT) { + ALOGI("Ranging report state: %u", it.get_u8()); + subscribe_event.peer_sdea_params.range_report = (NanRangeReport)it.get_u8(); + } + } + + GET_NAN_HANDLE(info)->mHandlers.EventMatch(&subscribe_event); + break; + } + case NAN_EVENT_SUBSCRIBE_UNMATCH: { + ALOGI("Received NAN_EVENT_SUBSCRIBE_UNMATCH\n"); + ALOGE("%s: Not applicable yet\n", __func__); + break; + } + case NAN_EVENT_SUBSCRIBE_TERMINATED: { + NanSubscribeTerminatedInd sub_term_event; + memset(&sub_term_event, 0, sizeof(NanSubscribeTerminatedInd)); + ALOGI("Received NAN_EVENT_SUBSCRIBE_TERMINATED\n"); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (attr_type == NAN_ATTRIBUTE_SUBSCRIBE_ID) { + sub_term_event.subscribe_id = it.get_u16(); + ALOGI("sub id: %u", sub_term_event.subscribe_id); + } else if (attr_type == NAN_ATTRIBUTE_STATUS) { + sub_term_event.reason = (NanStatusType)it.get_u8(); + ALOGI("sub termination status %u", sub_term_event.reason); + } else if (attr_type == NAN_ATTRIBUTE_REASON) { + u8 len = min(it.get_len(), sizeof(sub_term_event.nan_reason)); + memcpy(sub_term_event.nan_reason, it.get_data(), len); + ALOGI("sub termination nan reason: %s, len = %d\n", + sub_term_event.nan_reason, len); + } else { + ALOGE("Unknown attr: %u\n", attr_type); + } + } + + GET_NAN_HANDLE(info)->mHandlers.EventSubscribeTerminated(&sub_term_event); + break; + } + case NAN_EVENT_MATCH_EXPIRY: + HandleExpiryEvent(info, vendor_data); + break; + case NAN_EVENT_FOLLOWUP: { + NanFollowupInd followup_event; + memset(&followup_event, 0, sizeof(NanFollowupInd)); + ALOGI("Received NAN_EVENT_FOLLOWUP\n"); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (attr_type == NAN_ATTRIBUTE_MAC_ADDR) { + memcpy(followup_event.addr, it.get_data(), NAN_MAC_ADDR_LEN); + + } else if (attr_type == NAN_ATTRIBUTE_PEER_ID) { + followup_event.publish_subscribe_id = it.get_u16(); + + } else if (attr_type == NAN_ATTRIBUTE_INST_ID) { + followup_event.requestor_instance_id = it.get_u32(); + + } else if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN) { + followup_event.service_specific_info_len = it.get_u16(); + + } else if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO) { + memcpy(followup_event.service_specific_info, it.get_data(), + followup_event.service_specific_info_len); + followup_event.service_specific_info[followup_event.service_specific_info_len] = + '\0'; + } else if (attr_type == NAN_ATTRIBUTE_SDEA_SERVICE_SPECIFIC_INFO_LEN) { + ALOGI("sdea svc length %d", it.get_u16()); + followup_event.sdea_service_specific_info_len = it.get_u16(); + } else if (attr_type == NAN_ATTRIBUTE_SDEA_SERVICE_SPECIFIC_INFO) { + memcpy(followup_event.sdea_service_specific_info, it.get_data(), + followup_event.sdea_service_specific_info_len); + followup_event.sdea_service_specific_info[followup_event.sdea_service_specific_info_len] = '\0'; + ALOGI("sdea service info: %s", followup_event.sdea_service_specific_info); + } + } + + GET_NAN_HANDLE(info)->mHandlers.EventFollowup(&followup_event); + break; + } + case NAN_EVENT_SDF: { + ALOGI("Received NAN_EVENT_SDF:\n"); + NanBeaconSdfPayloadInd sdfInd; + memset(&sdfInd, 0, sizeof(sdfInd)); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN) { + sdfInd.data.frame_len = it.get_u16(); + if (sdfInd.data.frame_len > NAN_MAX_FRAME_DATA_LEN) { + sdfInd.data.frame_len = NAN_MAX_FRAME_DATA_LEN; + } + ALOGI("Received NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN: 0x%x(%d)\n", + sdfInd.data.frame_len, sdfInd.data.frame_len); + + } else if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO) { + ALOGI("Received NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO\n"); + memcpy(&sdfInd.data.frame_data, it.get_data(), sdfInd.data.frame_len); + prhex("sdfInd.data.frame_data: ", (u8*)sdfInd.data.frame_data, + sdfInd.data.frame_len); + } + } + GET_NAN_HANDLE(info)->mHandlers.EventBeaconSdfPayload(&sdfInd); + break; + } +#ifdef NOT_YET + case NAN_EVENT_PUBLISH_REPLIED_IND: { + ALOGI("Received NAN_EVENT_PUBLISH_REPLIED_IND\n"); + NanPublishRepliedInd pub_reply_event; + memset(&pub_reply_event, 0, sizeof(pub_reply_event)); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (attr_type == NAN_ATTRIBUTE_SUBSCRIBE_ID) { + ALOGI("sub id: %u", it.get_u32()); + pub_reply_event.requestor_instance_id = it.get_u32(); + } else if (attr_type == NAN_ATTRIBUTE_MAC_ADDR) { + memcpy(pub_reply_event.addr, it.get_data(), NAN_MAC_ADDR_LEN); + ALOGI("Subscriber mac: " MACSTR, MAC2STR(pub_reply_event.addr)); + } else if (attr_type == NAN_ATTRIBUTE_RSSI_PROXIMITY) { + pub_reply_event.rssi_value = it.get_u8(); + ALOGI("Received rssi value : %u", it.get_u8()); + } + } + GET_NAN_HANDLE(info)->mHandlers.EventPublishReplied(&pub_reply_event); + break; + } +#endif /* NOT_YET */ + case NAN_EVENT_TCA: { + ALOGI("Received NAN_EVENT_TCA\n"); + //GET_NAN_HANDLE(info)->mHandlers.EventTca(&sdfPayload); + break; + } + case NAN_EVENT_DATA_REQUEST: { + ALOGI("Received NAN_EVENT_DATA_REQUEST_INDICATION\n"); + NanDataPathRequestInd ndp_request_event; + memset(&ndp_request_event, 0, sizeof(NanDataPathRequestInd)); + u16 ndp_ind_app_info_len = 0; + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (attr_type == NAN_ATTRIBUTE_PUBLISH_ID) { + ALOGI("publish_id: %u\n", it.get_u32()); + ndp_request_event.service_instance_id = it.get_u32(); + + } else if (attr_type == NAN_ATTRIBUTE_MAC_ADDR) { + memcpy(ndp_request_event.peer_disc_mac_addr, + it.get_data(), NAN_MAC_ADDR_LEN); + ALOGI("Discovery MAC addr of the peer/initiator: " MACSTR "\n", + MAC2STR(ndp_request_event.peer_disc_mac_addr)); + + } else if (attr_type == NAN_ATTRIBUTE_NDP_ID) { + ALOGI("ndp id: %u\n", it.get_u32()); + ndp_request_event.ndp_instance_id = it.get_u32(); + + } else if (attr_type == NAN_ATTRIBUTE_SECURITY) { + ALOGI("security: %u\n", (NanDataPathSecurityCfgStatus) it.get_u8()); + ndp_request_event.ndp_cfg.security_cfg = + (NanDataPathSecurityCfgStatus)it.get_u8(); + + } else if (attr_type == NAN_ATTRIBUTE_QOS) { + ALOGI("QoS: %u", (NanDataPathQosCfg)it.get_u8()); + ndp_request_event.ndp_cfg.qos_cfg = (NanDataPathQosCfg)it.get_u8(); + + } else if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN) { + ALOGI("service info len: %d\n", it.get_u16()); + ndp_ind_app_info_len = it.get_u16(); + ndp_request_event.app_info.ndp_app_info_len = ndp_ind_app_info_len; + + } else if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO) { + memcpy(ndp_request_event.app_info.ndp_app_info, + it.get_data(), ndp_ind_app_info_len); + ndp_request_event.app_info.ndp_app_info[ndp_ind_app_info_len] = '\0'; + ALOGI("service info: %s\n", ndp_request_event.app_info.ndp_app_info); + + } else if (attr_type == NAN_ATTRIBUTE_SCID_LEN) { + ALOGI("scid length %d\n", it.get_u32()); + ndp_request_event.scid_len= it.get_u32(); + + } else if (attr_type == NAN_ATTRIBUTE_SCID) { + memcpy(ndp_request_event.scid, it.get_data(), + ndp_request_event.scid_len); + ndp_request_event.scid[ndp_request_event.scid_len] = '\0'; + ALOGI("scid: %s\n", ndp_request_event.scid); + } + } + + GET_NAN_HANDLE(info)->mHandlers.EventDataRequest(&ndp_request_event); + break; + } + case NAN_EVENT_DATA_CONFIRMATION: { + ALOGI("Received NAN_EVENT_DATA_CONFIRMATION\n"); + NanDataPathConfirmInd ndp_create_confirmation_event; + memset(&ndp_create_confirmation_event, 0, sizeof(NanDataPathConfirmInd)); + u16 ndp_conf_app_info_len = 0; + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + + if (attr_type == NAN_ATTRIBUTE_NDP_ID) { + ALOGI("ndp id: %u", it.get_u32()); + ndp_create_confirmation_event.ndp_instance_id = it.get_u32(); + + } else if (attr_type == NAN_ATTRIBUTE_PEER_NDI_MAC_ADDR) { + memcpy(ndp_create_confirmation_event.peer_ndi_mac_addr, + it.get_data(), NAN_MAC_ADDR_LEN); + ALOGI("NDI mac address of the peer: " MACSTR "\n", + MAC2STR(ndp_create_confirmation_event.peer_ndi_mac_addr)); + + } else if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO_LEN) { + ALOGI("service info string len: %d\n", it.get_u16()); + ndp_conf_app_info_len = it.get_u16(); + ndp_create_confirmation_event.app_info.ndp_app_info_len = + ndp_conf_app_info_len; + + } else if (attr_type == NAN_ATTRIBUTE_SERVICE_SPECIFIC_INFO) { + memcpy(ndp_create_confirmation_event.app_info.ndp_app_info, it.get_data(), + ndp_conf_app_info_len); + ndp_create_confirmation_event.app_info.ndp_app_info[ndp_conf_app_info_len] = + '\0'; + ALOGI("service info string: %s\n", + ndp_create_confirmation_event.app_info.ndp_app_info); + + } else if (attr_type == NAN_ATTRIBUTE_RSP_CODE) { + ALOGI("response code %u\n", (NanDataPathResponseCode) it.get_u8()); + ndp_create_confirmation_event.rsp_code = + (NanDataPathResponseCode)it.get_u8(); + } + } + + GET_NAN_HANDLE(info)->mHandlers.EventDataConfirm(&ndp_create_confirmation_event); + break; + } + case NAN_EVENT_DATA_END: { + ALOGI("Received NAN_EVENT_DATA_END\n"); + NanDataPathEndInd ndp_end_event; + memset(&ndp_end_event, 0, sizeof(NanDataPathEndInd)); + u8 count = 0; + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + if (attr_type == NAN_ATTRIBUTE_INST_COUNT) { + ALOGI("ndp count: %u\n", it.get_u8()); + ndp_end_event.num_ndp_instances = it.get_u8(); + count = it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_NDP_ID) { + ALOGI("count: %u\n", count); + while (count) { + ndp_end_event.ndp_instance_id[count-1] = it.get_u32(); + ALOGI("ndp id: %u\n", ndp_end_event.ndp_instance_id[count-1]); + count -= 1; + } + } else { + ALOGI("Unknown attr_type: %s\n", NanAttrToString(attr_type)); + } + } + + GET_NAN_HANDLE(info)->mHandlers.EventDataEnd(&ndp_end_event); + break; + } + case NAN_EVENT_TRANSMIT_FOLLOWUP_IND: { + ALOGI("Received NAN_EVENT_TRANSMIT_FOLLOWUP_IND\n"); + NanTransmitFollowupInd followup_ind; + memset(&followup_ind, 0, sizeof(NanTransmitFollowupInd)); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + if (attr_type == NAN_ATTRIBUTE_TRANSAC_ID) { + followup_ind.id = it.get_u16(); + } else if (attr_type == NAN_ATTRIBUTE_STATUS) { + followup_ind.reason = (NanStatusType)it.get_u8(); + } else if (attr_type == NAN_ATTRIBUTE_REASON) { + u8 len = min(it.get_len(), sizeof(followup_ind.nan_reason)); + memcpy(followup_ind.nan_reason, it.get_data(), len); + ALOGI("nan transmit followup ind: reason: %s, len = %d\n", + followup_ind.nan_reason, len); + } + } + + GET_NAN_HANDLE(info)->mHandlers.EventTransmitFollowup(&followup_ind); + break; + } + case NAN_EVENT_UNKNOWN: + ALOGI("Received NAN_EVENT_UNKNOWN\n"); + break; + } // end-of-switch + return NL_SKIP; + } +}; + +/* To see event prints in console */ +wifi_error nan_event_check_request(transaction_id id, wifi_interface_handle iface) +{ + NanEventCap *cmd = new NanEventCap(iface, id); + if (cmd == NULL) { + return WIFI_ERROR_NOT_SUPPORTED; + } + return (wifi_error)cmd->start(); +} + +/* Create NAN Data Interface */ +wifi_error nan_data_interface_create(transaction_id id, + wifi_interface_handle iface, char* iface_name) +{ + wifi_error ret = WIFI_SUCCESS; + NAN_DBG_ENTER(); + + NanRequestType cmdType = NAN_DATA_PATH_IFACE_CREATE; + NanDataPathPrimitive *cmd = + new NanDataPathPrimitive(iface, id, (void *)iface_name, cmdType); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + + ret = (wifi_error)cmd->open(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in open, error = %d\n", __func__, ret); + } + cmd->releaseRef(); + + NAN_DBG_EXIT(); + return ret; +} + +/* Delete NAN Data Interface */ +wifi_error nan_data_interface_delete(transaction_id id, + wifi_interface_handle iface, char* iface_name) +{ + wifi_error ret = WIFI_SUCCESS; + NAN_DBG_ENTER(); + + NanRequestType cmdType = NAN_DATA_PATH_IFACE_DELETE; + NanDataPathPrimitive *cmd = + new NanDataPathPrimitive(iface, id, (void *)iface_name, cmdType); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + + ret = (wifi_error)cmd->open(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in open, error = %d\n", __func__, ret); + } + cmd->releaseRef(); + + NAN_DBG_EXIT(); + return ret; +} + +/* Initiate a NDP session: Initiator */ +wifi_error nan_data_request_initiator(transaction_id id, + wifi_interface_handle iface, NanDataPathInitiatorRequest* msg) +{ + wifi_error ret = WIFI_SUCCESS; + + NAN_DBG_ENTER(); + NanRequestType cmdType; + NanDataPathPrimitive *cmd = NULL; + +#ifdef CONFIG_BRCM + dump_NanDataPathInitiatorRequest(msg); +#endif /* CONFIG_BRCM */ + counters.dp_req++; + if (msg->service_name_len) { + if (strncmp(NAN_OOB_INTEROP_SVC_NAME, + (char*)msg->service_name, msg->service_name_len) == 0) { + ALOGI("Use Hardcoded svc_hash\n"); + msg->service_name_len = NAN_SVC_HASH_SIZE; + memcpy(msg->service_name, NAN_OOB_INTEROP_SVC_HASH, NAN_SVC_HASH_SIZE); + } else { + u8 svc_hash[NAN_SVC_HASH_SIZE]; + + ret = (wifi_error)get_svc_hash(msg->service_name, msg->service_name_len, + svc_hash, NAN_SVC_HASH_SIZE); + if (ret < 0) { + ALOGE("%s: Failed to get hashed svc name\n", __func__); + goto done; + } + + ALOGI("Created svc_hash\n"); + msg->service_name_len = NAN_SVC_HASH_SIZE; + memcpy(msg->service_name, svc_hash, msg->service_name_len); + } + } else if (msg->key_info.key_type == NAN_SECURITY_KEY_INPUT_PASSPHRASE) { + NanDataPathSecInfoRequest msg_sec_info; + if (msg->requestor_instance_id == 0) { + ALOGE("Invalid Pub ID = %d, Mandatory param is missing\n", msg->requestor_instance_id); + ret = WIFI_ERROR_INVALID_ARGS; + goto done; + } else { + ALOGI("Pub ID = %d, Mandatory param is present\n", msg->requestor_instance_id); + } + if (ETHER_ISNULLADDR(msg->peer_disc_mac_addr)) { + ALOGE("Invalid Pub NMI, Mandatory param is missing\n"); + ret = WIFI_ERROR_INVALID_ARGS; + goto done; + } + + msg_sec_info.requestor_instance_id = msg->requestor_instance_id; + memcpy(msg_sec_info.peer_disc_mac_addr, msg->peer_disc_mac_addr, NAN_MAC_ADDR_LEN); + msg_sec_info.ndp_instance_id = 0; + cmdType = NAN_DATA_PATH_SEC_INFO; + cmd = new NanDataPathPrimitive(iface, id, (void *)&msg_sec_info, cmdType); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + ret = (wifi_error)cmd->open(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in start, error = %d\n", __func__, ret); + goto done; + } + memcpy(msg->service_name, cmd->mSvcHash, NAN_SVC_HASH_SIZE); + } + /* free old command */ + if (cmd) { + cmd->releaseRef(); + } + cmdType = NAN_DATA_PATH_INIT_REQUEST; + cmd = new NanDataPathPrimitive(iface, id, (void *)msg, cmdType); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + ret = (wifi_error)cmd->open(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in open, error = %d\n", __func__, ret); + goto done; + } +done: + if (cmd) { + cmd->releaseRef(); + } + + NAN_DBG_EXIT(); + return ret; +} + +/* Response to a data indication received corresponding to a NDP session. + * An indication is received with a data request and the responder will send a data response + */ +wifi_error nan_data_indication_response(transaction_id id, + wifi_interface_handle iface, NanDataPathIndicationResponse* msg) +{ + wifi_error ret = WIFI_SUCCESS; + NAN_DBG_ENTER(); + NanRequestType cmdType; + u8 pub_nmi[NAN_MAC_ADDR_LEN] = {0}; + NanDataPathPrimitive *cmd = NULL; + +#ifdef CONFIG_BRCM + dump_NanDataPathIndicationResponse(msg); +#endif /* CONFIG_BRCM */ + counters.dp_resp++; + if (msg->service_name_len) { + if (strncmp(NAN_OOB_INTEROP_SVC_NAME, + (char*)msg->service_name, msg->service_name_len) == 0) { + ALOGI("Use Hardcoded svc_hash\n"); + msg->service_name_len = NAN_SVC_HASH_SIZE; + memcpy(msg->service_name, NAN_OOB_INTEROP_SVC_HASH, NAN_SVC_HASH_SIZE); + } else { + u8 svc_hash[NAN_SVC_HASH_SIZE]; + + ret = (wifi_error)get_svc_hash(msg->service_name, msg->service_name_len, + svc_hash, NAN_SVC_HASH_SIZE); + if (ret < 0) { + ALOGE("%s: Failed to get hashed svc name\n", __func__); + goto done; + } + ALOGI("Created svc_hash\n"); + msg->service_name_len = NAN_SVC_HASH_SIZE; + memcpy(msg->service_name, svc_hash, msg->service_name_len); + } + } + if (msg->key_info.key_type == NAN_SECURITY_KEY_INPUT_PASSPHRASE) { + NanDataPathSecInfoRequest msg_sec_info; + + if (msg->ndp_instance_id == 0) { + ALOGE("Invalid NDP ID, Mandatory info is not present\n"); + ret = WIFI_ERROR_INVALID_ARGS; + goto done; + } else { + ALOGI("NDP ID = %d, Mandatory info is present\n", msg->ndp_instance_id); + } + msg_sec_info.ndp_instance_id = msg->ndp_instance_id; + msg_sec_info.requestor_instance_id = 0; + cmdType = NAN_DATA_PATH_SEC_INFO; + cmd = new NanDataPathPrimitive(iface, id, (void *)&msg_sec_info, cmdType); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + + ret = (wifi_error)cmd->open(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in start, error = %d\n", __func__, ret); + goto done; + } + + if (ETHER_ISNULLADDR(cmd->mPubNmi)) { + ALOGE("Invalid Pub NMI\n"); + ret = WIFI_ERROR_INVALID_ARGS; + goto done; + } + memcpy(pub_nmi, cmd->mPubNmi, NAN_MAC_ADDR_LEN); + + if (!msg->service_name_len) { + if (SVCHASH_ISNULL(cmd->mSvcHash)) { + ALOGE("Invalid svc_hash\n"); + ret = WIFI_ERROR_INVALID_ARGS; + goto done; + } + memcpy(msg->service_name, cmd->mSvcHash, NAN_SVC_HASH_SIZE); + } + } + /* free old command */ + if (cmd) { + cmd->releaseRef(); + } + cmdType = NAN_DATA_PATH_IND_RESPONSE; + cmd = new NanDataPathPrimitive(iface, id, (void *)msg, cmdType); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + memcpy(cmd->mPubNmi, pub_nmi, NAN_MAC_ADDR_LEN); + ret = (wifi_error)cmd->open(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in open, error = %d\n", __func__, ret); + goto done; + } + +done: + if (cmd) { + cmd->releaseRef(); + } + NAN_DBG_EXIT(); + return ret; +} + +/* NDL termination request: from either Initiator/Responder */ +wifi_error nan_data_end(transaction_id id, + wifi_interface_handle iface, NanDataPathEndRequest* msg) +{ + wifi_error ret = WIFI_SUCCESS; + NanDataPathPrimitive *cmd; + NanRequestType cmdType = NAN_DATA_PATH_END; + NAN_DBG_ENTER(); + + cmd = new NanDataPathPrimitive(iface, id, (void *)msg, cmdType); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + + ret = (wifi_error)cmd->open(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in open, error = %d\n", __func__, ret); + } + cmd->releaseRef(); + NAN_DBG_EXIT(); + return ret; +} diff --git a/synadhd/wifi_hal/rtt.cpp b/synadhd/wifi_hal/rtt.cpp new file mode 100644 index 0000000..510714b --- /dev/null +++ b/synadhd/wifi_hal/rtt.cpp @@ -0,0 +1,812 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * 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 <stdint.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/errqueue.h> + +#include <linux/pkt_sched.h> +#include <netlink/object-api.h> +#include <netlink/netlink.h> +#include <netlink/socket.h> +#include <netlink-private/object-api.h> +#include <netlink-private/types.h> + +#include "nl80211_copy.h" + +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include <log/log.h> +#include <utils/String8.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +using namespace android; +#define RTT_RESULT_V2_SIZE (sizeof(wifi_rtt_result_v2)); +typedef enum { + + RTT_SUBCMD_SET_CONFIG = ANDROID_NL80211_SUBCMD_RTT_RANGE_START, + RTT_SUBCMD_CANCEL_CONFIG, + RTT_SUBCMD_GETCAPABILITY, + RTT_SUBCMD_GETAVAILCHANNEL, + RTT_SUBCMD_SET_RESPONDER, + RTT_SUBCMD_CANCEL_RESPONDER, +} RTT_SUB_COMMAND; + +typedef enum { + RTT_ATTRIBUTE_TARGET_INVALID = 0, + RTT_ATTRIBUTE_TARGET_CNT = 1, + RTT_ATTRIBUTE_TARGET_INFO = 2, + RTT_ATTRIBUTE_TARGET_MAC = 3, + RTT_ATTRIBUTE_TARGET_TYPE = 4, + RTT_ATTRIBUTE_TARGET_PEER = 5, + RTT_ATTRIBUTE_TARGET_CHAN = 6, + RTT_ATTRIBUTE_TARGET_PERIOD = 7, + RTT_ATTRIBUTE_TARGET_NUM_BURST = 8, + RTT_ATTRIBUTE_TARGET_NUM_FTM_BURST = 9, + RTT_ATTRIBUTE_TARGET_NUM_RETRY_FTM = 10, + RTT_ATTRIBUTE_TARGET_NUM_RETRY_FTMR = 11, + RTT_ATTRIBUTE_TARGET_LCI = 12, + RTT_ATTRIBUTE_TARGET_LCR = 13, + RTT_ATTRIBUTE_TARGET_BURST_DURATION = 14, + RTT_ATTRIBUTE_TARGET_PREAMBLE = 15, + RTT_ATTRIBUTE_TARGET_BW = 16, + RTT_ATTRIBUTE_RESULTS_COMPLETE = 30, + RTT_ATTRIBUTE_RESULTS_PER_TARGET = 31, + RTT_ATTRIBUTE_RESULT_CNT = 32, + RTT_ATTRIBUTE_RESULT = 33, + RTT_ATTRIBUTE_RESULT_DETAIL = 34, + RTT_ATTRIBUTE_RESULT_EXTRA = 35, + /* Add any new RTT_ATTRIBUTE prior to RTT_ATTRIBUTE_MAX */ + RTT_ATTRIBUTE_MAX +} RTT_ATTRIBUTE; +typedef struct strmap_entry { + int id; + String8 text; +} strmap_entry_t; +struct dot11_rm_ie { + u8 id; + u8 len; + u8 token; + u8 mode; + u8 type; +} __attribute__ ((packed)); +typedef struct dot11_rm_ie dot11_rm_ie_t; + +typedef struct rtt_result_extra { + wifi_channel frequency; + wifi_rtt_bw packet_bw; +} rtt_result_extra_t; + +#define DOT11_HDR_LEN 2 +#define DOT11_RM_IE_LEN 5 +#define DOT11_MNG_MEASURE_REQUEST_ID 38 /* 11H MeasurementRequest */ +#define DOT11_MNG_MEASURE_REPORT_ID 39 /* 11H MeasurementResponse */ +#define DOT11_MEASURE_TYPE_LCI 8 /* d11 measurement LCI type */ +#define DOT11_MEASURE_TYPE_CIVICLOC 11 /* d11 measurement location civic */ + +static const strmap_entry_t err_info[] = { + {RTT_STATUS_SUCCESS, String8("Success")}, + {RTT_STATUS_FAILURE, String8("Failure")}, + {RTT_STATUS_FAIL_NO_RSP, String8("No response")}, + {RTT_STATUS_FAIL_INVALID_TS, String8("Invalid Timestamp")}, + {RTT_STATUS_FAIL_PROTOCOL, String8("Protocol error")}, + {RTT_STATUS_FAIL_REJECTED, String8("Rejected")}, + {RTT_STATUS_FAIL_NOT_SCHEDULED_YET, String8("not scheduled")}, + {RTT_STATUS_FAIL_SCHEDULE, String8("schedule failed")}, + {RTT_STATUS_FAIL_TM_TIMEOUT, String8("timeout")}, + {RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL, String8("AP is on difference channel")}, + {RTT_STATUS_FAIL_NO_CAPABILITY, String8("no capability")}, + {RTT_STATUS_FAIL_BUSY_TRY_LATER, String8("busy and try later")}, + {RTT_STATUS_ABORTED, String8("aborted")} +}; + + static const char* +get_err_info(int status) +{ + int i; + const strmap_entry_t *p_entry; + int num_entries = sizeof(err_info)/ sizeof(err_info[0]); + /* scan thru the table till end */ + p_entry = err_info; + for (i = 0; i < (int) num_entries; i++) + { + if (p_entry->id == status) + return p_entry->text; + p_entry++; /* next entry */ + } + return "unknown error"; /* not found */ +} + +class GetRttCapabilitiesCommand : public WifiCommand +{ + wifi_rtt_capabilities *mCapabilities; +public: + GetRttCapabilitiesCommand(wifi_interface_handle iface, wifi_rtt_capabilities *capabitlites) + : WifiCommand("GetRttCapabilitiesCommand", iface, 0), mCapabilities(capabitlites) + { + memset(mCapabilities, 0, sizeof(*mCapabilities)); + } + + virtual int create() { + ALOGD("Creating message to get scan capabilities; iface = %d", mIfaceInfo->id); + + int ret = mMsg.create(GOOGLE_OUI, RTT_SUBCMD_GETCAPABILITY); + if (ret < 0) { + return ret; + } + + return ret; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + + ALOGD("In GetRttCapabilitiesCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + + void *data = reply.get_vendor_data(); + int len = reply.get_vendor_data_len(); + + ALOGD("Id = %0x, subcmd = %d, len = %d, expected len = %d", id, subcmd, len, + sizeof(*mCapabilities)); + + memcpy(mCapabilities, data, min(len, (int) sizeof(*mCapabilities))); + + return NL_OK; + } +}; + + +class GetRttResponderInfoCommand : public WifiCommand +{ + wifi_rtt_responder* mResponderInfo; +public: + GetRttResponderInfoCommand(wifi_interface_handle iface, wifi_rtt_responder *responderInfo) + : WifiCommand("GetRttResponderInfoCommand", iface, 0), mResponderInfo(responderInfo) + { + memset(mResponderInfo, 0 , sizeof(*mResponderInfo)); + + } + + virtual int create() { + ALOGD("Creating message to get responder info ; iface = %d", mIfaceInfo->id); + + int ret = mMsg.create(GOOGLE_OUI, RTT_SUBCMD_GETAVAILCHANNEL); + if (ret < 0) { + return ret; + } + + return ret; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + + ALOGD("In GetRttResponderInfoCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + + void *data = reply.get_vendor_data(); + int len = reply.get_vendor_data_len(); + + ALOGD("Id = %0x, subcmd = %d, len = %d, expected len = %d", id, subcmd, len, + sizeof(*mResponderInfo)); + + memcpy(mResponderInfo, data, min(len, (int) sizeof(*mResponderInfo))); + + return NL_OK; + } +}; + + +class EnableResponderCommand : public WifiCommand +{ + wifi_channel_info mChannelInfo; + wifi_rtt_responder* mResponderInfo; + unsigned m_max_duration_sec; +public: + EnableResponderCommand(wifi_interface_handle iface, int id, wifi_channel_info channel_hint, + unsigned max_duration_seconds, wifi_rtt_responder *responderInfo) + : WifiCommand("EnableResponderCommand", iface, 0), mChannelInfo(channel_hint), + m_max_duration_sec(max_duration_seconds), mResponderInfo(responderInfo) + { + memset(mResponderInfo, 0, sizeof(*mResponderInfo)); + } + + virtual int create() { + ALOGD("Creating message to set responder ; iface = %d", mIfaceInfo->id); + + int ret = mMsg.create(GOOGLE_OUI, RTT_SUBCMD_SET_RESPONDER); + if (ret < 0) { + return ret; + } + + return ret; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + + ALOGD("In EnableResponderCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + + void *data = reply.get_vendor_data(); + int len = reply.get_vendor_data_len(); + + ALOGD("Id = %0x, subcmd = %d, len = %d, expected len = %d", id, subcmd, len, + sizeof(*mResponderInfo)); + + memcpy(mResponderInfo, data, min(len, (int) sizeof(*mResponderInfo))); + + return NL_OK; + } +}; + + +class CancelResponderCommand : public WifiCommand +{ + +public: + CancelResponderCommand(wifi_interface_handle iface, int id) + : WifiCommand("CancelResponderCommand", iface, 0)/*, mChannelInfo(channel)*/ + { + + } + + virtual int create() { + ALOGD("Creating message to cancel responder ; iface = %d", mIfaceInfo->id); + + int ret = mMsg.create(GOOGLE_OUI, RTT_SUBCMD_CANCEL_RESPONDER); + if (ret < 0) { + return ret; + } + + return ret; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + /* Nothing to do on response! */ + return NL_SKIP; + } + +}; + + +class RttCommand : public WifiCommand +{ + unsigned numRttParams; + int mCompleted; + int currentIdx; + int currDtlIdx; + int totalCnt; + static const int MAX_RESULTS = 1024; + wifi_rtt_result_v2 *rttResults_v2[MAX_RESULTS]; + wifi_rtt_config *rttParams; + wifi_rtt_event_handler rttHandler; +public: + RttCommand(wifi_interface_handle iface, int id, unsigned num_rtt_config, + wifi_rtt_config rtt_config[], wifi_rtt_event_handler handler) + : WifiCommand("RttCommand", iface, id), numRttParams(num_rtt_config), rttParams(rtt_config), + rttHandler(handler) + { + memset(rttResults_v2, 0, sizeof(rttResults_v2)); + currentIdx = 0; + mCompleted = 0; + totalCnt = 0; + currDtlIdx = 0; + } + + RttCommand(wifi_interface_handle iface, int id) + : WifiCommand("RttCommand", iface, id) + { + currentIdx = 0; + mCompleted = 0; + totalCnt = 0; + currDtlIdx = 0; + numRttParams = 0; + memset(rttResults_v2, 0, sizeof(rttResults_v2)); + rttParams = NULL; + rttHandler.on_rtt_results_v2 = NULL; + } + + int createSetupRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, RTT_SUBCMD_SET_CONFIG); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u8(RTT_ATTRIBUTE_TARGET_CNT, numRttParams); + if (result < 0) { + return result; + } + nlattr *rtt_config = request.attr_start(RTT_ATTRIBUTE_TARGET_INFO); + for (unsigned i = 0; i < numRttParams; i++) { + nlattr *attr2 = request.attr_start(i); + if (attr2 == NULL) { + return WIFI_ERROR_OUT_OF_MEMORY; + } + + result = request.put_addr(RTT_ATTRIBUTE_TARGET_MAC, rttParams[i].addr); + if (result < 0) { + return result; + } + + result = request.put_u8(RTT_ATTRIBUTE_TARGET_TYPE, rttParams[i].type); + if (result < 0) { + return result; + } + + result = request.put_u8(RTT_ATTRIBUTE_TARGET_PEER, rttParams[i].peer); + if (result < 0) { + return result; + } + + result = request.put(RTT_ATTRIBUTE_TARGET_CHAN, &rttParams[i].channel, + sizeof(wifi_channel_info)); + if (result < 0) { + return result; + } + + result = request.put_u32(RTT_ATTRIBUTE_TARGET_NUM_BURST, rttParams[i].num_burst); + if (result < 0) { + return result; + } + + result = request.put_u32(RTT_ATTRIBUTE_TARGET_NUM_FTM_BURST, + rttParams[i].num_frames_per_burst); + if (result < 0) { + return result; + } + + result = request.put_u32(RTT_ATTRIBUTE_TARGET_NUM_RETRY_FTM, + rttParams[i].num_retries_per_rtt_frame); + if (result < 0) { + return result; + } + + result = request.put_u32(RTT_ATTRIBUTE_TARGET_NUM_RETRY_FTMR, + rttParams[i].num_retries_per_ftmr); + if (result < 0) { + return result; + } + + result = request.put_u32(RTT_ATTRIBUTE_TARGET_PERIOD, + rttParams[i].burst_period); + if (result < 0) { + return result; + } + + result = request.put_u32(RTT_ATTRIBUTE_TARGET_BURST_DURATION, + rttParams[i].burst_duration); + if (result < 0) { + return result; + } + + result = request.put_u8(RTT_ATTRIBUTE_TARGET_LCI, + rttParams[i].LCI_request); + if (result < 0) { + return result; + } + + result = request.put_u8(RTT_ATTRIBUTE_TARGET_LCR, + rttParams[i].LCR_request); + if (result < 0) { + return result; + } + + result = request.put_u8(RTT_ATTRIBUTE_TARGET_BW, + rttParams[i].bw); + if (result < 0) { + return result; + } + + result = request.put_u8(RTT_ATTRIBUTE_TARGET_PREAMBLE, + rttParams[i].preamble); + if (result < 0) { + return result; + } + request.attr_end(attr2); + } + + request.attr_end(rtt_config); + request.attr_end(data); + return WIFI_SUCCESS; + } + + int createTeardownRequest(WifiRequest& request, unsigned num_devices, mac_addr addr[]) { + int result = request.create(GOOGLE_OUI, RTT_SUBCMD_CANCEL_CONFIG); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u8(RTT_ATTRIBUTE_TARGET_CNT, num_devices); + + if (result < 0) { + return result; + } + for(unsigned i = 0; i < num_devices; i++) { + result = request.put_addr(RTT_ATTRIBUTE_TARGET_MAC, addr[i]); + if (result < 0) { + return result; + } + } + request.attr_end(data); + return result; + } + int start() { + ALOGD("Setting RTT configuration"); + WifiRequest request(familyId(), ifaceId()); + int result = createSetupRequest(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create setup request; result = %d", result); + return result; + } + + registerVendorHandler(GOOGLE_OUI, RTT_EVENT_COMPLETE); + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + unregisterVendorHandler(GOOGLE_OUI, RTT_EVENT_COMPLETE); + ALOGE("failed to configure RTT setup; result = %d", result); + return result; + } + + ALOGI("Successfully started RTT operation"); + return result; + } + + virtual int cancel() { + ALOGD("Stopping RTT"); + + WifiRequest request(familyId(), ifaceId()); + int result = createTeardownRequest(request, 0, NULL); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create stop request; result = %d", result); + } else { + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to stop scan; result = %d", result); + } + } + + unregisterVendorHandler(GOOGLE_OUI, RTT_EVENT_COMPLETE); + return WIFI_SUCCESS; + } + + int cancel_specific(unsigned num_devices, mac_addr addr[]) { + ALOGE("Stopping RTT"); + + WifiRequest request(familyId(), ifaceId()); + int result = createTeardownRequest(request, num_devices, addr); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create stop request; result = %d", result); + } else { + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to stop RTT; result = %d", result); + } + } + + unregisterVendorHandler(GOOGLE_OUI, RTT_EVENT_COMPLETE); + return WIFI_SUCCESS; + } + + virtual int handleResponse(WifiEvent& reply) { + /* Nothing to do on response! */ + return NL_SKIP; + } + + virtual int handleEvent(WifiEvent& event) { + ALOGI("Got an RTT event"); + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = event.get_vendor_data_len(); + int nextidx = 0; + if (vendor_data == NULL || len == 0) { + ALOGI("No rtt results found"); + return NL_STOP; + } + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == RTT_ATTRIBUTE_RESULTS_COMPLETE) { + mCompleted = it.get_u32(); + ALOGI("retrieved completed flag : %d\n", mCompleted); + } else if (it.get_type() == RTT_ATTRIBUTE_RESULTS_PER_TARGET) { + int result_cnt = 0; + mac_addr bssid; + for (nl_iterator it2(it.get()); it2.has_next(); it2.next()) { + if (it2.get_type() == RTT_ATTRIBUTE_TARGET_MAC) { + memcpy(bssid, it2.get_data(), sizeof(mac_addr)); + ALOGI("retrieved target mac : %02x:%02x:%02x:%02x:%02x:%02x\n", + bssid[0], + bssid[1], + bssid[2], + bssid[3], + bssid[4], + bssid[5]); + } else if (it2.get_type() == RTT_ATTRIBUTE_RESULT_CNT) { + result_cnt = it2.get_u32(); + ALOGI("retrieved result_cnt : %d\n", result_cnt); + } else if (it2.get_type() == RTT_ATTRIBUTE_RESULT_EXTRA) { + if (rttResults_v2[currentIdx] == NULL) { + mCompleted = 1; + ALOGE("failed to allocate the rttResults_v2\n"); + break; + } + rtt_result_extra_t *rtt_result_extra = + (rtt_result_extra_t *)malloc(it2.get_len()); + if (rtt_result_extra == NULL) { + mCompleted = 1; + ALOGE("failed to allocate the rtt_result_extra\n"); + break; + } + /* Update RTT frequency & packet_bw */ + memcpy(rtt_result_extra, it2.get_data(), it2.get_len()); + rttResults_v2[currentIdx]->frequency = rtt_result_extra->frequency; + rttResults_v2[currentIdx]->packet_bw = rtt_result_extra->packet_bw; + free(rtt_result_extra); + ALOGI("retrieved currentIdx %d rtt_result_v2 :\n" + "\tfrequency : %d, packet_bw : %d\n", + currentIdx, rttResults_v2[currentIdx]->frequency, + rttResults_v2[currentIdx]->packet_bw); + } else if (it2.get_type() == RTT_ATTRIBUTE_RESULT) { + currentIdx = nextidx; + int result_v2_len = sizeof(wifi_rtt_result_v2) + + it2.get_len() - sizeof(wifi_rtt_result); + int result_len = it2.get_len(); + rttResults_v2[currentIdx] = (wifi_rtt_result_v2 *)malloc(result_v2_len); + wifi_rtt_result_v2 *rtt_result_v2 = rttResults_v2[currentIdx]; + if (rtt_result_v2 == NULL) { + mCompleted = 1; + ALOGE("failed to allocate the wifi_rtt_result_v2\n"); + break; + } + wifi_rtt_result *rtt_result = &rtt_result_v2->rtt_result; + memcpy(rtt_result, it2.get_data(), it2.get_len()); + result_len -= sizeof(wifi_rtt_result); + if (result_len > 0) { + dot11_rm_ie_t *ele_1; + dot11_rm_ie_t *ele_2; + /* The result has LCI or LCR element */ + ele_1 = (dot11_rm_ie_t *)(rtt_result + 1); + if (ele_1->id == DOT11_MNG_MEASURE_REPORT_ID) { + if (ele_1->type == DOT11_MEASURE_TYPE_LCI) { + rtt_result->LCI = (wifi_information_element *)ele_1; + result_len -= (ele_1->len + DOT11_HDR_LEN); + /* get a next rm ie */ + if (result_len > 0) { + ele_2 = (dot11_rm_ie_t *)((char *)ele_1 + + (ele_1->len + DOT11_HDR_LEN)); + if ((ele_2->id == DOT11_MNG_MEASURE_REPORT_ID) && + (ele_2->type == DOT11_MEASURE_TYPE_CIVICLOC)) { + rtt_result->LCR = (wifi_information_element *)ele_2; + } + } + } else if (ele_1->type == DOT11_MEASURE_TYPE_CIVICLOC){ + rtt_result->LCR = (wifi_information_element *)ele_1; + result_len -= (ele_1->len + DOT11_HDR_LEN); + /* get a next rm ie */ + if (result_len > 0) { + ele_2 = (dot11_rm_ie_t *)((char *)ele_1 + + (ele_1->len + DOT11_HDR_LEN)); + if ((ele_2->id == DOT11_MNG_MEASURE_REPORT_ID) && + (ele_2->type == DOT11_MEASURE_TYPE_LCI)) { + rtt_result->LCI = (wifi_information_element *)ele_2; + } + } + } + } + } + totalCnt++; + + /* Fill out rtt_result_v2 */ + rtt_result_v2->frequency = UNSPECIFIED; + rtt_result_v2->packet_bw = WIFI_RTT_BW_UNSPECIFIED; + + ALOGI("retrieved currentIdx %d rtt_result : \n\tburst_num :%d," + " measurement_number : %d, success_number : %d\n" + "\tnumber_per_burst_peer : %d, status : %s, " + "retry_after_duration : %d s\n \trssi : %d dbm," + " rx_rate : %d Kbps, rtt : %lu ns, rtt_sd : %lu\n" + "\tdistance : %d cm, burst_duration : %d ms," + " negotiated_burst_num : %d\n", + currentIdx, rtt_result_v2->rtt_result.burst_num, + rtt_result_v2->rtt_result.measurement_number, + rtt_result_v2->rtt_result.success_number, + rtt_result_v2->rtt_result.number_per_burst_peer, + get_err_info(rtt_result_v2->rtt_result.status), + rtt_result_v2->rtt_result.retry_after_duration, + rtt_result_v2->rtt_result.rssi, + rtt_result_v2->rtt_result.rx_rate.bitrate * 100, + (unsigned long)rtt_result_v2->rtt_result.rtt / 1000, + (unsigned long)rtt_result_v2->rtt_result.rtt_sd, + rtt_result_v2->rtt_result.distance_mm / 10, + rtt_result_v2->rtt_result.burst_duration, + rtt_result_v2->rtt_result.negotiated_burst_num); + nextidx = currentIdx; + nextidx++; + } + } + } + } + if (mCompleted) { + unregisterVendorHandler(GOOGLE_OUI, RTT_EVENT_COMPLETE); + if (*rttHandler.on_rtt_results_v2) { + (*rttHandler.on_rtt_results_v2)(id(), totalCnt, rttResults_v2); + } + for (int i = 0; i < currentIdx; i++) { + free(rttResults_v2[i]); + rttResults_v2[i] = NULL; + } + totalCnt = currentIdx = 0; + WifiCommand *cmd = wifi_unregister_cmd(wifiHandle(), id()); + if (cmd) + cmd->releaseRef(); + } + return NL_SKIP; + } +}; + + +/* API to request RTT measurement */ +wifi_error wifi_rtt_range_request(wifi_request_id id, wifi_interface_handle iface, + unsigned num_rtt_config, wifi_rtt_config rtt_config[], wifi_rtt_event_handler handler) +{ + if (iface == NULL) { + ALOGE("wifi_rtt_range_request: NULL iface pointer provided." + " Exit."); + return WIFI_ERROR_INVALID_ARGS; + } + + wifi_handle handle = getWifiHandle(iface); + if (handle == NULL) { + ALOGE("wifi_rtt_range_request: NULL handle pointer provided." + " Exit."); + return WIFI_ERROR_INVALID_ARGS; + } + + RttCommand *cmd = new RttCommand(iface, id, num_rtt_config, rtt_config, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + +/* API to cancel RTT measurements */ +wifi_error wifi_rtt_range_cancel(wifi_request_id id, wifi_interface_handle iface, + unsigned num_devices, mac_addr addr[]) +{ + if (iface == NULL) { + ALOGE("wifi_rtt_range_cancel: NULL iface pointer provided." + " Exit."); + return WIFI_ERROR_INVALID_ARGS; + } + + wifi_handle handle = getWifiHandle(iface); + if (handle == NULL) { + ALOGE("wifi_rtt_range_cancel: NULL handle pointer provided." + " Exit."); + return WIFI_ERROR_INVALID_ARGS; + } + + RttCommand *cmd = new RttCommand(iface, id); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + cmd->cancel_specific(num_devices, addr); + cmd->releaseRef(); + return WIFI_SUCCESS; +} + +/* API to get RTT capability */ +wifi_error wifi_get_rtt_capabilities(wifi_interface_handle iface, + wifi_rtt_capabilities *capabilities) +{ + if (iface == NULL) { + ALOGE("wifi_get_rtt_capabilities: NULL iface pointer provided." + " Exit."); + return WIFI_ERROR_INVALID_ARGS; + } + + if (capabilities == NULL) { + ALOGE("wifi_get_rtt_capabilities: NULL capabilities pointer provided." + " Exit."); + return WIFI_ERROR_INVALID_ARGS; + } + + GetRttCapabilitiesCommand command(iface, capabilities); + return (wifi_error) command.requestResponse(); +} + +/* API to get the responder information */ +wifi_error wifi_rtt_get_responder_info(wifi_interface_handle iface, + wifi_rtt_responder* responderInfo) +{ + if (iface == NULL) { + ALOGE("wifi_rtt_get_responder_info: NULL iface pointer provided." + " Exit."); + return WIFI_ERROR_INVALID_ARGS; + } + + GetRttResponderInfoCommand command(iface, responderInfo); + return (wifi_error) command.requestResponse(); + +} + +/** + * Enable RTT responder mode. + * channel_hint - hint of the channel information where RTT responder should be enabled on. + * max_duration_seconds - timeout of responder mode. + * wifi_rtt_responder - information for RTT responder e.g. channel used and preamble supported. + */ +wifi_error wifi_enable_responder(wifi_request_id id, wifi_interface_handle iface, + wifi_channel_info channel_hint, unsigned max_duration_seconds, + wifi_rtt_responder* responderInfo) +{ + EnableResponderCommand command(iface, id, channel_hint, max_duration_seconds, responderInfo); + return (wifi_error) command.requestResponse(); +} + +/** + * Disable RTT responder mode. + */ +wifi_error wifi_disable_responder(wifi_request_id id, wifi_interface_handle iface) +{ + CancelResponderCommand command(iface, id); + return (wifi_error) command.requestResponse(); +} + diff --git a/synadhd/wifi_hal/syna_version.h b/synadhd/wifi_hal/syna_version.h new file mode 100755 index 0000000..9323a24 --- /dev/null +++ b/synadhd/wifi_hal/syna_version.h @@ -0,0 +1 @@ +#define HAL_VERSION "SYNA vendor HAL" diff --git a/synadhd/wifi_hal/sync.h b/synadhd/wifi_hal/sync.h new file mode 100644 index 0000000..1118c71 --- /dev/null +++ b/synadhd/wifi_hal/sync.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * 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 <pthread.h> + +#ifndef __WIFI_HAL_SYNC_H__ +#define __WIFI_HAL_SYNC_H__ + +class Mutex +{ +private: + pthread_mutex_t mMutex; +public: + Mutex() { + pthread_mutex_init(&mMutex, NULL); + } + ~Mutex() { + pthread_mutex_destroy(&mMutex); + } + int tryLock() { + return pthread_mutex_trylock(&mMutex); + } + int lock() { + return pthread_mutex_lock(&mMutex); + } + void unlock() { + pthread_mutex_unlock(&mMutex); + } +}; + +class Condition +{ +private: + pthread_cond_t mCondition; + pthread_mutex_t mMutex; + +public: + Condition() { + pthread_mutex_init(&mMutex, NULL); + pthread_cond_init(&mCondition, NULL); + } + ~Condition() { + pthread_cond_destroy(&mCondition); + pthread_mutex_destroy(&mMutex); + } + + int wait() { + return pthread_cond_wait(&mCondition, &mMutex); + } + + void signal() { + pthread_cond_signal(&mCondition); + } +}; + +#endif diff --git a/synadhd/wifi_hal/twt.cpp b/synadhd/wifi_hal/twt.cpp new file mode 100755 index 0000000..15bb738 --- /dev/null +++ b/synadhd/wifi_hal/twt.cpp @@ -0,0 +1,947 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2020 Broadcom Limited + * + * 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 <stdint.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/errqueue.h> + +#include <linux/pkt_sched.h> +#include <netlink/object-api.h> +#include <netlink/netlink.h> +#include <netlink/socket.h> +#include <netlink/handlers.h> + +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include <utils/Log.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +static const char *TwtCmdToString(int cmd); +static void EventGetAttributeData(u8 sub_event_type, nlattr *vendor_data); +typedef void *TwtRequest; + +#define C2S(x) case x: return #x; + +typedef struct _twt_hal_info { + void *twt_handle; + void *twt_feature_request; +} twt_hal_info_t; + +twt_hal_info_t twt_info; + +#define TWT_HANDLE(twt_info) ((twt_info).twt_handle) +#define GET_TWT_HANDLE(twt_info) ((TwtHandle *)twt_info.twt_handle) + +#define WL_TWT_CAP_FLAGS_REQ_SUPPORT (1u << 0u) +#define WL_TWT_CAP_FLAGS_RESP_SUPPORT (1u << 1u) +#define WL_TWT_CAP_FLAGS_BTWT_SUPPORT (1u << 2u) +#define WL_TWT_CAP_FLAGS_FLEX_SUPPORT (1u << 3u) + +class TwtHandle +{ + public: + TwtCallbackHandler mHandlers; + TwtHandle(wifi_handle handle, TwtCallbackHandler handlers):mHandlers(handlers) + {} + +}; + + +static const char *TwtCmdToString(int cmd) +{ + switch (cmd) { + C2S(TWT_SETUP_REQUEST); + C2S(TWT_INFO_FRAME_REQUEST); + C2S(TWT_TEAR_DOWN_REQUEST); + default: + return "UNKNOWN_NAN_CMD"; + } +} + +static bool is_twt_sub_event(int sub_event_type) +{ + bool is_twt_event = false; + switch (sub_event_type) { + case TWT_SETUP_RESPONSE: + case TWT_TEARDOWN_COMPLETION: + case TWT_INFORM_FRAME: + case TWT_NOTIFY: + is_twt_event = true; + } + return is_twt_event; +} + +void EventGetAttributeData(u8 sub_event_type, nlattr *vendor_data) +{ + u8 attr_type = 0; + + switch (sub_event_type) { + case TWT_SETUP_RESPONSE: + TwtSetupResponse setup_response; + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + switch (attr_type) { + case TWT_ATTRIBUTE_CONFIG_ID: + ALOGI("config_id = %u\n", it.get_u8()); + setup_response.config_id = it.get_u8(); + break; + case TWT_ATTRIBUTE_NEG_TYPE: + ALOGI("neg type = %u\n", it.get_u8()); + setup_response.negotiation_type = it.get_u8(); + break; + case TWT_ATTRIBUTE_REASON_CODE: + setup_response.reason_code = (TwtSetupReasonCode)it.get_u8(); + ALOGI("reason code = %u\n", setup_response.reason_code); + break; + case TWT_ATTRIBUTE_STATUS: + setup_response.status = it.get_u8(); + ALOGI("status = %u\n", setup_response.status); + break; + case TWT_ATTRIBUTE_TRIGGER_TYPE: + setup_response.trigger_type = it.get_u8(); + ALOGI("trigger type = %u\n", setup_response.trigger_type); + break; + case TWT_ATTRIBUTE_WAKE_DUR_US: + setup_response.wake_dur_us = it.get_u32(); + ALOGI("wake_dur_us = %d\n", setup_response.wake_dur_us); + break; + case TWT_ATTRIBUTE_WAKE_INT_US: + setup_response.wake_int_us = it.get_u32(); + ALOGI("wake_int_us = %d\n", setup_response.wake_int_us); + break; + case TWT_ATTRIBUTE_WAKE_TIME_OFF_US: + setup_response.wake_time_off_us = it.get_u32(); + ALOGI("wake_time_off_us = %d\n", setup_response.wake_time_off_us); + break; + default: + if (attr_type != TWT_ATTRIBUTE_SUB_EVENT) { + ALOGE("Unknown attr_type: %d\n", attr_type); + } + break; + } + } + GET_TWT_HANDLE(twt_info)->mHandlers.EventTwtSetupResponse(&setup_response); + break; + case TWT_TEARDOWN_COMPLETION: + TwtTeardownCompletion teardown_event; + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + switch (attr_type) { + case TWT_ATTRIBUTE_CONFIG_ID: + ALOGI("config_id = %u\n", it.get_u8()); + teardown_event.config_id = it.get_u8(); + break; + case TWT_ATTRIBUTE_STATUS: + teardown_event.status = it.get_u8(); + ALOGI("status = %u\n", teardown_event.status); + break; + case TWT_ATTRIBUTE_ALL_TWT: + teardown_event.all_twt = it.get_u8(); + ALOGI("all_twt = %d\n", teardown_event.all_twt); + break; + case TWT_ATTRIBUTE_REASON_CODE: + teardown_event.reason = (TwtTeardownReason)it.get_u8(); + ALOGI("reason = %u\n", teardown_event.reason); + break; + default: + if (attr_type != TWT_ATTRIBUTE_SUB_EVENT) { + ALOGE("Unknown attr_type: %d\n", attr_type); + } + break; + } + } + GET_TWT_HANDLE(twt_info)->mHandlers.EventTwtTeardownCompletion(&teardown_event); + break; + case TWT_INFORM_FRAME: + TwtInfoFrameReceived info_frame_event; + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + switch (attr_type) { + case TWT_ATTRIBUTE_CONFIG_ID: + ALOGI("config_id = %u\n", it.get_u8()); + info_frame_event.config_id = it.get_u8(); + break; + case TWT_ATTRIBUTE_REASON_CODE: + info_frame_event.reason = (TwtInfoFrameReason)it.get_u8(); + ALOGI("reason = %u\n", info_frame_event.reason); + break; + case TWT_ATTRIBUTE_STATUS: + info_frame_event.status = it.get_u8(); + ALOGI("status = %u\n", info_frame_event.status); + break; + case TWT_ATTRIBUTE_ALL_TWT: + info_frame_event.all_twt = it.get_u8(); + ALOGI("all_twt = %d\n", info_frame_event.all_twt); + break; + case TWT_ATTRIBUTE_RESUMED: + info_frame_event.twt_resumed = it.get_u8(); + ALOGI("twt_resumed = %u\n", info_frame_event.twt_resumed); + break; + default: + if (attr_type != TWT_ATTRIBUTE_SUB_EVENT) { + ALOGE("Unknown attr_type: %d\n", attr_type); + } + break; + } + } + GET_TWT_HANDLE(twt_info)->mHandlers.EventTwtInfoFrameReceived(&info_frame_event); + break; + case TWT_NOTIFY: + TwtDeviceNotify notif_event; + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + attr_type = it.get_type(); + switch (attr_type) { + case TWT_ATTRIBUTE_NOTIFICATION: + notif_event.notification = (TwtNotification)it.get_u8(); + ALOGI("notification = %u\n", notif_event.notification); + break; + default: + if (attr_type != TWT_ATTRIBUTE_SUB_EVENT) { + ALOGE("Unknown attr_type: %d\n", attr_type); + } + break; + } + } + GET_TWT_HANDLE(twt_info)->mHandlers.EventTwtDeviceNotify(¬if_event); + break; + default: + ALOGE("Unknown event_type: %d\n", sub_event_type); + break; + } + return; +} + +void HandleTwtEvent(nlattr *vendor_data) { + u8 sub_event_type = 0; + u8 event_type = 0; + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + event_type = it.get_type(); + if (event_type == TWT_ATTRIBUTE_SUB_EVENT) { + sub_event_type = it.get_u8(); + if (is_twt_sub_event(sub_event_type)) { + EventGetAttributeData(sub_event_type, vendor_data); + } + } + } + return; +} + +class TwtEventCap : public WifiCommand +{ + public: + TwtEventCap(wifi_interface_handle iface, int id) + : WifiCommand("TwtCommand", iface, id) + {} + + int start() + { + registerTwtVendorEvents(); + return WIFI_SUCCESS; + } + + int handleResponse(WifiEvent& reply) { + return NL_SKIP; + } + + void registerTwtVendorEvents() + { + registerVendorHandler(BRCM_OUI, BRCM_VENDOR_EVENT_TWT); + } + + void unregisterTwtVendorEvents() + { + unregisterVendorHandler(BRCM_OUI, BRCM_VENDOR_EVENT_TWT); + } + + int handleEvent(WifiEvent& event) { + u16 attr_type; + TwtEventType twt_event; + + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = event.get_vendor_data_len(); + int event_id = event.get_vendor_subcmd(); + + ALOGI("EventCapture: Received TWT event: %d\n", event_id); + if (!vendor_data || len == 0) { + ALOGE("No event data found"); + return NL_SKIP; + } + + switch (event_id) { + case BRCM_VENDOR_EVENT_TWT: { + ALOGE("Handle TWT event: %d\n", event_id); + HandleTwtEvent(vendor_data); + break; + } + default: + break; + } + return NL_SKIP; + } +}; + +/* To see event prints in console */ +wifi_error twt_event_check_request(transaction_id id, wifi_interface_handle iface) +{ + TwtEventCap *cmd = new TwtEventCap(iface, id); + if (cmd == NULL) { + return WIFI_ERROR_NOT_SUPPORTED; + } + return (wifi_error)cmd->start(); +} + +////////////////////////////////////////////////////////////////////////// +class GetTwtCapabilitiesCommand : public WifiCommand +{ + TwtCapabilitySet *mCapabilities; +public: + GetTwtCapabilitiesCommand(wifi_interface_handle iface, TwtCapabilitySet *capabilities) + : WifiCommand("GetTwtCapabilitiesCommand", iface, 0), mCapabilities(capabilities) + { + memset(mCapabilities, 0, sizeof(*mCapabilities)); + } + + virtual int create() { + ALOGD("Creating message to get twt capabilities; iface\n"); + + int ret = mMsg.create(GOOGLE_OUI, TWT_SUBCMD_GETCAPABILITY); + if (ret < 0) { + ALOGE("Failed to send the twt cap cmd, err = %d\n", ret); + } + ALOGD("Success to send twt cap cmd, err = %d\n", ret); + return ret; + } + +private: + TwtCapability parseTwtCap(uint32_t twt_peer_cap) { + TwtCapability cap; + cap.requester_supported = (twt_peer_cap & WL_TWT_CAP_FLAGS_REQ_SUPPORT) ? 1 : 0; + cap.responder_supported = (twt_peer_cap & WL_TWT_CAP_FLAGS_RESP_SUPPORT) ? 1 : 0; + cap.broadcast_twt_supported = (twt_peer_cap & WL_TWT_CAP_FLAGS_BTWT_SUPPORT) ? 1 : 0; + cap.flexibile_twt_supported = (twt_peer_cap & WL_TWT_CAP_FLAGS_FLEX_SUPPORT) ? 1 : 0; + return cap; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + + ALOGI("In GetTwtCapabilitiesCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + uint32_t twt_device_cap, twt_peer_cap; + + nlattr *data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + ALOGD("Id = %0x, subcmd = %d, len = %d, expected len = %d", id, subcmd, len); + if (data == NULL || len == 0) { + ALOGE("no vendor data in GetTwtCapabilitiesCommand response; ignoring it\n"); + return NL_SKIP; + } + + for (nl_iterator it(data); it.has_next(); it.next()) { + switch (it.get_type()) { + case TWT_ATTRIBUTE_DEVICE_CAP: + twt_device_cap = it.get_u32(); + mCapabilities->device_capability = parseTwtCap(twt_device_cap); + break; + case TWT_ATTRIBUTE_PEER_CAP: + twt_peer_cap = it.get_u32(); + mCapabilities->peer_capability = parseTwtCap(twt_peer_cap); + break; + default: + ALOGE("Ignoring invalid attribute type = %d, size = %d\n", + it.get_type(), it.get_len()); + break; + } + } + + ALOGE("Out GetTwtCapabilitiesCommand::handleResponse\n"); + return NL_OK; + } +}; + +/* API to get TWT capability */ +wifi_error twt_get_capability(wifi_interface_handle iface, + TwtCapabilitySet *twt_cap_set) +{ + if (iface == NULL) { + ALOGE("twt_get_capability: NULL iface pointer provided." + " Exit."); + return WIFI_ERROR_INVALID_ARGS; + } + + if (twt_cap_set == NULL) { + ALOGE("twt_get_capability: NULL capabilities pointer provided." + " Exit."); + return WIFI_ERROR_INVALID_ARGS; + } + + GetTwtCapabilitiesCommand command(iface, twt_cap_set); + return (wifi_error) command.requestResponse(); +} + +////////////////////////////////////////////////////////////////////////// +class GetTwtStatsCommand : public WifiCommand +{ + TwtStats* mStats; + u8 mConfig_id; +public: + GetTwtStatsCommand(wifi_interface_handle iface, u8 config_id, TwtStats *stats) + : WifiCommand("GetTwtStatsCommand", iface, 0), mConfig_id(config_id), mStats(stats) + { + memset(mStats, 0, sizeof(*mStats)); + } + + virtual int create() { + ALOGD("Creating message to get twt stats; iface = %d", mIfaceInfo->id); + + int ret = mMsg.create(GOOGLE_OUI, TWT_SUBCMD_GETSTATS); + if (ret < 0) { + return ret; + } + + nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); + ret = mMsg.put_u8(TWT_ATTRIBUTE_CONFIG_ID, mConfig_id); + if (ret < 0) { + ALOGE("Failed to set mConfig_id %d\n", mConfig_id); + return ret; + } + + ALOGI("Successfully configured config id %d\n", mConfig_id); + mMsg.attr_end(data); + return WIFI_SUCCESS; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + + ALOGI("In GetTwtStatsCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + + nlattr *data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + ALOGD("Id = %0x, subcmd = %d, len = %d, expected len = %d", id, subcmd, len); + if (data == NULL || len == 0) { + ALOGE("no vendor data in GetTwtStatsCommand response; ignoring it\n"); + return NL_SKIP; + } + + for (nl_iterator it(data); it.has_next(); it.next()) { + switch (it.get_type()) { + case TWT_ATTRIBUTE_CONFIG_ID: + mStats->config_id = it.get_u8(); + break; + case TWT_ATTRIBUTE_AVG_PKT_NUM_TX: + mStats->avg_pkt_num_tx = it.get_u32(); + break; + case TWT_ATTRIBUTE_AVG_PKT_NUM_RX: + mStats->avg_pkt_num_rx = it.get_u32(); + break; + case TWT_ATTRIBUTE_AVG_PKT_SIZE_TX: + mStats->avg_tx_pkt_size = it.get_u32(); + break; + case TWT_ATTRIBUTE_AVG_PKT_SIZE_RX: + mStats->avg_rx_pkt_size = it.get_u32(); + break; + case TWT_ATTRIBUTE_AVG_EOSP_DUR: + mStats->avg_eosp_dur_us = it.get_u32(); + break; + case TWT_ATTRIBUTE_EOSP_COUNT: + mStats->eosp_count = it.get_u32(); + break; + case TWT_ATTRIBUTE_NUM_SP: + mStats->num_sp = it.get_u32(); + break; + default: + ALOGE("Ignoring invalid attribute type = %d, size = %d\n", + it.get_type(), it.get_len()); + break; + } + } + + return NL_OK; + } +}; + +/* API to get TWT stats */ +wifi_error twt_get_stats(wifi_interface_handle iface, u8 config_id, TwtStats* stats) +{ + if (iface == NULL) { + ALOGE("twt_get_stats: NULL iface pointer provided." + " Exit."); + return WIFI_ERROR_INVALID_ARGS; + } + + if (stats == NULL) { + ALOGE("TwtCapabilitySet: NULL capabilities pointer provided." + " Exit."); + return WIFI_ERROR_INVALID_ARGS; + } + + GetTwtStatsCommand command(iface, config_id, stats); + return (wifi_error) command.requestResponse(); +} + +////////////////////////////////////////////////////////////////////////////////////// +class ClearTwtStatsCommand : public WifiCommand +{ + u8 mConfig_id; +public: + ClearTwtStatsCommand(wifi_interface_handle iface, u8 config_id) + : WifiCommand("ClearTwtStatsCommand", iface, 0), mConfig_id(config_id) + { + } + + virtual int create() { + ALOGD("Creating message to clear twt stats; config_id = %d\n", mConfig_id); + + int ret = mMsg.create(GOOGLE_OUI, TWT_SUBCMD_CLR_STATS); + if (ret < 0) { + return ret; + } + + nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); + ret = mMsg.put_u8(TWT_ATTRIBUTE_CONFIG_ID, mConfig_id); + if (ret < 0) { + ALOGE("Failed to set mConfig_id %d\n", mConfig_id); + return ret; + } + + ALOGI("Successfully configured config id %d\n", mConfig_id); + mMsg.attr_end(data); + return WIFI_SUCCESS; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + ALOGD("In ClearTwtStatsCommand::handleResponse"); + /* Nothing to do on response! */ + return NL_SKIP; + } +}; + +/* API to clear TWT stats */ +wifi_error twt_clear_stats(wifi_interface_handle iface, u8 config_id) +{ + if (iface == NULL || !config_id) { + ALOGE("twt_clear_stats: NULL iface pointer provided." + " Exit."); + return WIFI_ERROR_INVALID_ARGS; + } + ALOGE("twt_clear_stats: config id: %d\n", config_id); + + ClearTwtStatsCommand command(iface, config_id); + return (wifi_error) command.requestResponse(); +} + +//////////////////////////////////////////////////////////////////////////////// +class TwtFeatureRequest : public WifiCommand +{ + TwtRequest reqContext; + TwtRequestType mType; + + public: + TwtFeatureRequest(wifi_interface_handle iface, + TwtRequest params, TwtRequestType cmdType) + : WifiCommand("TwtFeatureRequest", iface, 0), reqContext(params), mType(cmdType) + { + } + + int createRequest(WifiRequest& request) + { + ALOGI("TWT CMD: %s\n", TwtCmdToString(mType)); + if (mType == TWT_SETUP_REQUEST) { + return createTwtSetupRequest(request, (TwtSetupRequest *)reqContext); + } else if (mType == TWT_INFO_FRAME_REQUEST) { + return createInfoFrameRequest(request, (TwtInfoFrameRequest *)reqContext); + } else if (mType == TWT_TEAR_DOWN_REQUEST) { + return createTearDownRequest(request, (TwtTeardownRequest *)reqContext); + } else { + ALOGE("%s: Unknown TWT request: %d\n", __func__, mType); + return WIFI_ERROR_UNKNOWN; + } + + return WIFI_SUCCESS; + } + + int createTwtSetupRequest(WifiRequest& request, TwtSetupRequest *mParams) + { + int result = request.create(GOOGLE_OUI, TWT_SUBCMD_SETUP_REQUEST); + if (result < 0) { + ALOGE("%s Failed to create request, result = %d\n", __func__, result); + return result; + } + + /* If handle is 0xFFFF, then update instance_id in response of this request + * otherwise, update not needed + */ + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + if (mParams->config_id) { + result = request.put_u8(TWT_ATTRIBUTE_CONFIG_ID, mParams->config_id); + if (result < 0) { + ALOGE("%s: Failed to fill config_id = %d, result = %d\n", + __func__, mParams->config_id, result); + return result; + } + } + + if (mParams->negotiation_type) { + result = request.put_u8(TWT_ATTRIBUTE_NEG_TYPE, mParams->negotiation_type); + if (result < 0) { + ALOGE("%s: Failed to fill negotiation_type = %d, result = %d\n", + __func__, mParams->negotiation_type, result); + return result; + } + } + if (mParams->trigger_type) { + result = request.put_u8(TWT_ATTRIBUTE_TRIGGER_TYPE, mParams->trigger_type); + if (result < 0) { + ALOGE("%s: Failed to fill trigger_type = %d, result = %d\n", + __func__, mParams->trigger_type, result); + return result; + } + } + if (mParams->wake_dur_us) { + result = request.put_u32(TWT_ATTRIBUTE_WAKE_DUR_US, mParams->wake_dur_us); + if (result < 0) { + ALOGE("%s: Failed to fill wake_dur_us = %d, result = %d\n", + __func__, mParams->wake_dur_us, result); + return result; + } + } + if (mParams->wake_int_us) { + result = request.put_u32(TWT_ATTRIBUTE_WAKE_INT_US, mParams->wake_int_us); + if (result < 0) { + ALOGE("%s: Failed to fill wake_int_us = %d, result = %d\n", + __func__, mParams->wake_int_us, result); + return result; + } + } + if (mParams->wake_int_min_us) { + result = request.put_u32(TWT_ATTRIBUTE_WAKE_INT_MIN_US, mParams->wake_int_min_us); + if (result < 0) { + ALOGE("%s: Failed to fill wake_int_min_us = %d, result = %d\n", + __func__, mParams->wake_int_min_us, result); + return result; + } + } + if (mParams->wake_int_max_us) { + result = request.put_u32(TWT_ATTRIBUTE_WAKE_INT_MAX_US, mParams->wake_int_max_us); + if (result < 0) { + ALOGE("%s: Failed to fill wake_int_max_us = %d, result = %d\n", + __func__, mParams->wake_int_max_us, result); + return result; + } + } + if (mParams->wake_dur_min_us) { + result = request.put_u32(TWT_ATTRIBUTE_WAKE_DUR_MIN_US, mParams->wake_dur_min_us); + if (result < 0) { + ALOGE("%s: Failed to fill wake_dur_min_us = %d, result = %d\n", + __func__, mParams->wake_dur_min_us, result); + return result; + } + } + if (mParams->wake_dur_max_us) { + result = request.put_u32(TWT_ATTRIBUTE_WAKE_DUR_MAX_US, mParams->wake_dur_max_us); + if (result < 0) { + ALOGE("%s: Failed to fill wake_dur_max_us = %d, result = %d\n", + __func__, mParams->wake_dur_max_us, result); + return result; + } + } + if (mParams->avg_pkt_size) { + result = request.put_u32(TWT_ATTRIBUTE_AVG_PKT_SIZE, mParams->avg_pkt_size); + if (result < 0) { + ALOGE("%s: Failed to fill avg_pkt_size = %d, result = %d\n", + __func__, mParams->avg_pkt_size, result); + return result; + } + } + if (mParams->avg_pkt_num) { + result = request.put_u32(TWT_ATTRIBUTE_AVG_PKT_NUM, mParams->avg_pkt_num); + if (result < 0) { + ALOGE("%s: Failed to fill avg_pkt_num = %d, result = %d\n", + __func__, mParams->avg_pkt_num, result); + return result; + } + } + if (mParams->wake_time_off_us) { + result = request.put_u32(TWT_ATTRIBUTE_WAKE_TIME_OFF_US, mParams->wake_time_off_us); + if (result < 0) { + ALOGE("%s: Failed to fill wake_time_off_us = %d, result = %d\n", + __func__, mParams->wake_time_off_us, result); + return result; + } + } + request.attr_end(data); + + ALOGI("Returning successfully\n"); + return result; + } + + int createInfoFrameRequest(WifiRequest& request, TwtInfoFrameRequest *mParams) + { + int result = request.create(GOOGLE_OUI, TWT_SUBCMD_INFO_FRAME_REQUEST); + if (result < 0) { + ALOGE("%s: Failed to create request, result = %d\n", __func__, result); + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + if (mParams->config_id) { + result = request.put_u8(TWT_ATTRIBUTE_CONFIG_ID, mParams->config_id); + if (result < 0) { + ALOGE("%s: Failed to fill config_id = %d, result = %d\n", + __func__, mParams->config_id, result); + return result; + } + } + if (mParams->resume_time_us) { + result = request.put_u32(TWT_ATTRIBUTE_RESUME_TIME_US, mParams->resume_time_us); + if (result < 0) { + ALOGE("%s: Failed to fill resume_time_us = %d, result = %d\n", + __func__, mParams->resume_time_us, result); + return result; + } + } + if (mParams->all_twt) { + result = request.put_u8(TWT_ATTRIBUTE_ALL_TWT, mParams->all_twt); + if (result < 0) { + ALOGE("%s: Failed to fill all_twt = %d, result = %d\n", + __func__, mParams->all_twt, result); + return result; + } + } + request.attr_end(data); + return WIFI_SUCCESS; + } + + int createTearDownRequest(WifiRequest& request, TwtTeardownRequest *mParams) + { + int result = request.create(GOOGLE_OUI, TWT_SUBCMD_TEAR_DOWN_REQUEST); + if (result < 0) { + ALOGE("%s: Failed to create request, result = %d\n", __func__, result); + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + if (mParams->config_id) { + result = request.put_u8(TWT_ATTRIBUTE_CONFIG_ID, mParams->config_id); + if (result < 0) { + ALOGE("%s: Failed to fill config_id = %d, result = %d\n", + __func__, mParams->config_id, result); + return result; + } + } + if (mParams->negotiation_type) { + result = request.put_u8(TWT_ATTRIBUTE_NEG_TYPE, mParams->negotiation_type); + if (result < 0) { + ALOGE("%s: Failed to fill negotiation_type = %d, result = %d\n", + __func__, mParams->negotiation_type, result); + return result; + } + } + if (mParams->all_twt) { + result = request.put_u8(TWT_ATTRIBUTE_ALL_TWT, mParams->all_twt); + if (result < 0) { + ALOGE("%s: Failed to fill all_twt = %d, result = %d\n", + __func__, mParams->all_twt, result); + return result; + } + } + request.attr_end(data); + return WIFI_SUCCESS; + } + + int open() + { + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request); + if (result != WIFI_SUCCESS) { + ALOGE("%s: failed to create setup request; result = %d", __func__, result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("%s: failed to configure setup; result = %d", __func__, result); + return result; + } + + request.destroy(); + return WIFI_SUCCESS; + } + + void registerTwtVendorEvents() + { + registerVendorHandler(BRCM_OUI, BRCM_VENDOR_EVENT_TWT); + } + + void unregisterTwtVendorEvents() + { + unregisterVendorHandler(BRCM_OUI, BRCM_VENDOR_EVENT_TWT); + } + + virtual int handleResponse(WifiEvent& reply) { + ALOGD("Request complete!"); + /* Nothing to do on response! */ + return NL_SKIP; + } + + int handleEvent(WifiEvent& event) { + u16 attr_type; + TwtEventType twt_event; + + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = event.get_vendor_data_len(); + int event_id = event.get_vendor_subcmd(); + ALOGI("Received TWT event: %d\n", event_id); + + if (!vendor_data || len == 0) { + ALOGE("No event data found"); + return NL_SKIP; + } + + switch (event_id) { + case BRCM_VENDOR_EVENT_TWT: { + HandleTwtEvent(vendor_data); + break; + } + default: + ALOGE("Unknown event: %d\n", event_id); + break; + } + return NL_SKIP; + } + +}; + +void twt_deinit_handler() +{ + if (twt_info.twt_feature_request) { + /* register for Twt vendor events with info mac class*/ + TwtFeatureRequest *cmd_event = (TwtFeatureRequest*)(twt_info.twt_feature_request); + cmd_event->unregisterTwtVendorEvents(); + delete (TwtFeatureRequest*)twt_info.twt_feature_request; + twt_info.twt_feature_request = NULL; + } + if (TWT_HANDLE(twt_info)) { + delete GET_TWT_HANDLE(twt_info); + TWT_HANDLE(twt_info) = NULL; + } + ALOGI("wifi twt internal clean up done"); + return; +} + +wifi_error twt_register_handler(wifi_interface_handle iface, + TwtCallbackHandler handlers) +{ + wifi_handle handle = getWifiHandle(iface); + if (TWT_HANDLE(twt_info)) { + /* cleanup and re-register */ + twt_deinit_handler(); + } + memset(&twt_info, 0, sizeof(twt_info)); + TWT_HANDLE(twt_info) = new TwtHandle(handle, handlers); + twt_info.twt_feature_request = + (void*)new TwtFeatureRequest(iface, NULL, TWT_LAST); + NULL_CHECK_RETURN(twt_info.twt_feature_request, + "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + TwtFeatureRequest *cmd_event = (TwtFeatureRequest*)(twt_info.twt_feature_request); + cmd_event->registerTwtVendorEvents(); + return WIFI_SUCCESS; +} + +wifi_error twt_setup_request(wifi_interface_handle iface, TwtSetupRequest* msg) +{ + wifi_error ret = WIFI_SUCCESS; + TwtFeatureRequest *cmd; + TwtRequestType cmdType = TWT_SETUP_REQUEST; + + cmd = new TwtFeatureRequest(iface, (void *)msg, cmdType); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + + ret = (wifi_error)cmd->open(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in open, error = %d\n", __func__, ret); + } + cmd->releaseRef(); + return ret; +} + +wifi_error twt_info_frame_request(wifi_interface_handle iface, TwtInfoFrameRequest* msg) +{ + wifi_error ret = WIFI_SUCCESS; + TwtFeatureRequest *cmd; + TwtRequestType cmdType = TWT_INFO_FRAME_REQUEST; + + cmd = new TwtFeatureRequest(iface, (void *)msg, cmdType); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + + ret = (wifi_error)cmd->open(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in open, error = %d\n", __func__, ret); + } + cmd->releaseRef(); + return ret; +} + +wifi_error twt_teardown_request(wifi_interface_handle iface, TwtTeardownRequest* msg) +{ + wifi_error ret = WIFI_SUCCESS; + TwtFeatureRequest *cmd; + TwtRequestType cmdType = TWT_TEAR_DOWN_REQUEST; + + cmd = new TwtFeatureRequest(iface, (void *)msg, cmdType); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + + ret = (wifi_error)cmd->open(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s : failed in open, error = %d\n", __func__, ret); + } + cmd->releaseRef(); + return ret; +} diff --git a/synadhd/wifi_hal/wifi_hal.cpp b/synadhd/wifi_hal/wifi_hal.cpp new file mode 100755 index 0000000..239a0c1 --- /dev/null +++ b/synadhd/wifi_hal/wifi_hal.cpp @@ -0,0 +1,3053 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * 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 <stdint.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/errqueue.h> +#include <errno.h> + +#include <linux/pkt_sched.h> +#include <netlink/object-api.h> +#include <netlink/netlink.h> +#include <netlink/socket.h> +#include <netlink/attr.h> +#include <netlink/handlers.h> +#include <netlink/msg.h> + +#include <dirent.h> +#include <net/if.h> + +#include <sys/types.h> +#include <unistd.h> + +#include "sync.h" + +#define LOG_TAG "WifiHAL" +#include <log/log.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" +#include "rtt.h" +#include "syna_version.h" +#include <stdio.h> +#include <string> +#include <vector> +/* + BUGBUG: normally, libnl allocates ports for all connections it makes; but + being a static library, it doesn't really know how many other netlink connections + are made by the same process, if connections come from different shared libraries. + These port assignments exist to solve that problem - temporarily. We need to fix + libnl to try and allocate ports across the entire process. + */ + +#define WIFI_HAL_CMD_SOCK_PORT 644 +#define WIFI_HAL_EVENT_SOCK_PORT 645 +#define MAX_VIRTUAL_IFACES 5 +#define WIFI_HAL_EVENT_BUFFER_NOT_AVAILABLE 105 + +/* + * Defines for wifi_wait_for_driver_ready() + * Specify durations between polls and max wait time + */ +#define POLL_DRIVER_DURATION_US (100000) +#define POLL_DRIVER_MAX_TIME_MS (10000) +#define EVENT_BUF_SIZE 2048 +#define C2S(x) case x: return #x; + +static int internal_no_seq_check(nl_msg *msg, void *arg); +static int internal_valid_message_handler(nl_msg *msg, void *arg); +static int wifi_get_multicast_id(wifi_handle handle, const char *name, const char *group); +static int wifi_add_membership(wifi_handle handle, const char *group); +static wifi_error wifi_init_interfaces(wifi_handle handle); +static wifi_error wifi_start_rssi_monitoring(wifi_request_id id, wifi_interface_handle + iface, s8 max_rssi, s8 min_rssi, wifi_rssi_event_handler eh); +static wifi_error wifi_stop_rssi_monitoring(wifi_request_id id, wifi_interface_handle iface); +static wifi_error wifi_set_packet_filter(wifi_interface_handle handle, + const u8 *program, u32 len); +static wifi_error wifi_read_packet_filter(wifi_interface_handle handle, u32 src_offset, + u8 *host_dst, u32 length); +static wifi_error wifi_get_packet_filter_capabilities(wifi_interface_handle handle, + u32 *version, u32 *max_len); +static wifi_error wifi_configure_nd_offload(wifi_interface_handle iface, u8 enable); +static wifi_error wifi_get_usable_channels(wifi_handle handle, u32 band_mask, u32 iface_mode_mask, + u32 filter_mask, u32 max_size, u32* size, wifi_usable_channel* channels); +static wifi_error wifi_get_supported_radio_combinations_matrix(wifi_handle handle, + u32 max_size, u32* size, wifi_radio_combination_matrix *radio_combination_matrix); +static wifi_error wifi_set_indoor_state(wifi_handle handle, bool isIndoor); + +static void wifi_cleanup_dynamic_ifaces(wifi_handle handle); +typedef enum wifi_attr { + ANDR_WIFI_ATTRIBUTE_INVALID = 0, + ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET = 1, + ANDR_WIFI_ATTRIBUTE_FEATURE_SET = 2, + ANDR_WIFI_ATTRIBUTE_PNO_RANDOM_MAC_OUI = 3, + ANDR_WIFI_ATTRIBUTE_NODFS_SET = 4, + ANDR_WIFI_ATTRIBUTE_COUNTRY = 5, + ANDR_WIFI_ATTRIBUTE_ND_OFFLOAD_VALUE = 6, + ANDR_WIFI_ATTRIBUTE_TCPACK_SUP_VALUE = 7, + ANDR_WIFI_ATTRIBUTE_LATENCY_MODE = 8, + ANDR_WIFI_ATTRIBUTE_RANDOM_MAC = 9, + ANDR_WIFI_ATTRIBUTE_TX_POWER_SCENARIO = 10, + ANDR_WIFI_ATTRIBUTE_THERMAL_MITIGATION = 11, + ANDR_WIFI_ATTRIBUTE_THERMAL_COMPLETION_WINDOW = 12, + ANDR_WIFI_ATTRIBUTE_VOIP_MODE = 13, + ANDR_WIFI_ATTRIBUTE_DTIM_MULTIPLIER = 14, + ANDR_WIFI_ATTRIBUTE_INDOOR = 15, + // Add more attribute here + ANDR_WIFI_ATTRIBUTE_MAX +} wifi_attr_t; + +enum wifi_radio_combo_attributes { + ANDR_WIFI_ATTRIBUTE_RADIO_COMBO_INVALID = 0, + ANDR_WIFI_ATTRIBUTE_RADIO_COMBO_MATRIX = 1, + // Add more attribute here + ANDR_WIFI_ATTRIBUTE_RADIO_COMBO_MAX +}; + +enum wifi_rssi_monitor_attr { + RSSI_MONITOR_ATTRIBUTE_INVALID = 0, + RSSI_MONITOR_ATTRIBUTE_MAX_RSSI = 1, + RSSI_MONITOR_ATTRIBUTE_MIN_RSSI = 2, + RSSI_MONITOR_ATTRIBUTE_START = 3, + // Add more attribute here + RSSI_MONITOR_ATTRIBUTE_MAX +}; + +enum wifi_apf_attr { + APF_ATTRIBUTE_VERSION, + APF_ATTRIBUTE_MAX_LEN, + APF_ATTRIBUTE_PROGRAM, + APF_ATTRIBUTE_PROGRAM_LEN +}; + +enum apf_request_type { + GET_APF_CAPABILITIES, + SET_APF_PROGRAM, + READ_APF_PROGRAM +}; + +enum wifi_dscp_attr { + DSCP_ATTRIBUTE_INVALID = 0, + DSCP_ATTRIBUTE_START = 1, + DSCP_ATTRIBUTE_END = 2, + DSCP_ATTRIBUTE_AC = 3, + /* Add more attributes here */ + DSCP_ATTRIBUTE_MAX +}; + +enum wifi_dscp_request_type { + SET_DSCP_TABLE, + RESET_DSCP_TABLE +}; + +enum wifi_chavoid_attr { + CHAVOID_ATTRIBUTE_INVALID = 0, + CHAVOID_ATTRIBUTE_CNT = 1, + CHAVOID_ATTRIBUTE_CONFIG = 2, + CHAVOID_ATTRIBUTE_BAND = 3, + CHAVOID_ATTRIBUTE_CHANNEL = 4, + CHAVOID_ATTRIBUTE_PWRCAP = 5, + CHAVOID_ATTRIBUTE_MANDATORY = 6, + /* Add more attributes here */ + CHAVOID_ATTRIBUTE_MAX +}; + +enum wifi_usable_channel_attributes { + USABLECHAN_ATTRIBUTE_INVALID = 0, + USABLECHAN_ATTRIBUTE_BAND = 1, + USABLECHAN_ATTRIBUTE_IFACE = 2, + USABLECHAN_ATTRIBUTE_FILTER = 3, + USABLECHAN_ATTRIBUTE_MAX_SIZE = 4, + USABLECHAN_ATTRIBUTE_SIZE = 5, + USABLECHAN_ATTRIBUTE_CHANNELS = 6, + USABLECHAN_ATTRIBUTE_MAX +}; + +enum wifi_multista_attr { + MULTISTA_ATTRIBUTE_PRIM_CONN_IFACE, + MULTISTA_ATTRIBUTE_USE_CASE, + /* Add more attributes here */ + MULTISTA_ATTRIBUTE_MAX +}; + +enum multista_request_type { + SET_PRIMARY_CONNECTION, + SET_USE_CASE +}; + +/* Initialize/Cleanup */ + +void wifi_socket_set_local_port(struct nl_sock *sock, uint32_t port) +{ + uint32_t pid = getpid() & 0x3FFFFF; + nl_socket_set_local_port(sock, pid + (port << 22)); +} + +static nl_sock * wifi_create_nl_socket(int port) +{ + // ALOGI("Creating socket"); + struct nl_sock *sock = nl_socket_alloc(); + if (sock == NULL) { + ALOGE("Could not create handle"); + return NULL; + } + + wifi_socket_set_local_port(sock, port); + + if (nl_connect(sock, NETLINK_GENERIC)) { + ALOGE("Could not connect handle"); + nl_socket_free(sock); + return NULL; + } + return sock; +} + +static const char *IfaceTypeToString(wifi_interface_type iface_type) +{ + switch (iface_type) { + C2S(WIFI_INTERFACE_TYPE_STA) + C2S(WIFI_INTERFACE_TYPE_AP) + C2S(WIFI_INTERFACE_TYPE_P2P) + C2S(WIFI_INTERFACE_TYPE_NAN) + default: + return "UNKNOWN_WIFI_INTERFACE_TYPE"; + } +} + +/*initialize function pointer table with Broadcom HHAL API*/ +wifi_error init_wifi_vendor_hal_func_table(wifi_hal_fn *fn) +{ + if (fn == NULL) { + return WIFI_ERROR_UNKNOWN; + } + fn->wifi_initialize = wifi_initialize; + fn->wifi_wait_for_driver_ready = wifi_wait_for_driver_ready; + fn->wifi_cleanup = wifi_cleanup; + fn->wifi_event_loop = wifi_event_loop; + fn->wifi_get_supported_feature_set = wifi_get_supported_feature_set; + fn->wifi_get_concurrency_matrix = wifi_get_concurrency_matrix; + fn->wifi_set_scanning_mac_oui = wifi_set_scanning_mac_oui; + fn->wifi_get_ifaces = wifi_get_ifaces; + fn->wifi_get_iface_name = wifi_get_iface_name; + fn->wifi_start_gscan = wifi_start_gscan; + fn->wifi_stop_gscan = wifi_stop_gscan; + fn->wifi_get_cached_gscan_results = wifi_get_cached_gscan_results; + fn->wifi_set_bssid_hotlist = wifi_set_bssid_hotlist; + fn->wifi_reset_bssid_hotlist = wifi_reset_bssid_hotlist; + fn->wifi_set_significant_change_handler = wifi_set_significant_change_handler; + fn->wifi_reset_significant_change_handler = wifi_reset_significant_change_handler; + fn->wifi_get_gscan_capabilities = wifi_get_gscan_capabilities; + fn->wifi_get_link_stats = wifi_get_link_stats; + fn->wifi_set_link_stats = wifi_set_link_stats; + fn->wifi_clear_link_stats = wifi_clear_link_stats; + fn->wifi_get_valid_channels = wifi_get_valid_channels; + fn->wifi_rtt_range_request = wifi_rtt_range_request; + fn->wifi_rtt_range_cancel = wifi_rtt_range_cancel; + fn->wifi_get_rtt_capabilities = wifi_get_rtt_capabilities; + fn->wifi_rtt_get_responder_info = wifi_rtt_get_responder_info; + fn->wifi_enable_responder = wifi_enable_responder; + fn->wifi_disable_responder = wifi_disable_responder; + fn->wifi_set_nodfs_flag = wifi_set_nodfs_flag; + fn->wifi_start_logging = wifi_start_logging; + fn->wifi_set_epno_list = wifi_set_epno_list; + fn->wifi_reset_epno_list = wifi_reset_epno_list; + fn->wifi_set_country_code = wifi_set_country_code; + fn->wifi_get_firmware_memory_dump = wifi_get_firmware_memory_dump; + fn->wifi_set_log_handler = wifi_set_log_handler; + fn->wifi_reset_log_handler = wifi_reset_log_handler; + fn->wifi_set_alert_handler = wifi_set_alert_handler; + fn->wifi_reset_alert_handler = wifi_reset_alert_handler; + fn->wifi_get_firmware_version = wifi_get_firmware_version; + fn->wifi_get_ring_buffers_status = wifi_get_ring_buffers_status; + fn->wifi_get_logger_supported_feature_set = wifi_get_logger_supported_feature_set; + fn->wifi_get_ring_data = wifi_get_ring_data; + fn->wifi_get_driver_version = wifi_get_driver_version; + fn->wifi_start_rssi_monitoring = wifi_start_rssi_monitoring; + fn->wifi_stop_rssi_monitoring = wifi_stop_rssi_monitoring; + fn->wifi_configure_nd_offload = wifi_configure_nd_offload; + fn->wifi_start_sending_offloaded_packet = wifi_start_sending_offloaded_packet; + fn->wifi_stop_sending_offloaded_packet = wifi_stop_sending_offloaded_packet; + fn->wifi_start_pkt_fate_monitoring = wifi_start_pkt_fate_monitoring; + fn->wifi_get_tx_pkt_fates = wifi_get_tx_pkt_fates; + fn->wifi_get_rx_pkt_fates = wifi_get_rx_pkt_fates; + fn->wifi_get_packet_filter_capabilities = wifi_get_packet_filter_capabilities; + fn->wifi_get_wake_reason_stats = wifi_get_wake_reason_stats; + fn->wifi_set_packet_filter = wifi_set_packet_filter; + fn->wifi_enable_firmware_roaming = wifi_enable_firmware_roaming; + fn->wifi_get_roaming_capabilities = wifi_get_roaming_capabilities; + fn->wifi_configure_roaming = wifi_configure_roaming; + fn->wifi_nan_register_handler = nan_register_handler; + fn->wifi_nan_enable_request = nan_enable_request; + fn->wifi_nan_disable_request = nan_disable_request; + fn->wifi_nan_publish_request = nan_publish_request; + fn->wifi_nan_publish_cancel_request = nan_publish_cancel_request; + fn->wifi_nan_subscribe_request = nan_subscribe_request; + fn->wifi_nan_subscribe_cancel_request = nan_subscribe_cancel_request; + fn->wifi_nan_transmit_followup_request = nan_transmit_followup_request; + fn->wifi_nan_stats_request = nan_stats_request; + fn->wifi_nan_config_request = nan_config_request; + fn->wifi_nan_tca_request = nan_tca_request; + fn->wifi_nan_beacon_sdf_payload_request = nan_beacon_sdf_payload_request; + fn->wifi_nan_get_version = nan_get_version; + fn->wifi_nan_get_capabilities = nan_get_capabilities; + fn->wifi_nan_data_interface_create = nan_data_interface_create; + fn->wifi_nan_data_interface_delete = nan_data_interface_delete; + fn->wifi_nan_data_request_initiator = nan_data_request_initiator; + fn->wifi_nan_data_indication_response = nan_data_indication_response; + fn->wifi_nan_data_end = nan_data_end; + fn->wifi_set_latency_mode = wifi_set_latency_mode; + fn->wifi_select_tx_power_scenario = wifi_select_tx_power_scenario; + fn->wifi_reset_tx_power_scenario = wifi_reset_tx_power_scenario; + fn->wifi_read_packet_filter = wifi_read_packet_filter; + fn->wifi_set_subsystem_restart_handler = wifi_set_subsystem_restart_handler; + fn->wifi_set_thermal_mitigation_mode = wifi_set_thermal_mitigation_mode; + fn->wifi_map_dscp_access_category = wifi_map_dscp_access_category; + fn->wifi_reset_dscp_mapping = wifi_reset_dscp_mapping; + fn->wifi_virtual_interface_create = wifi_virtual_interface_create; + fn->wifi_virtual_interface_delete = wifi_virtual_interface_delete; + fn->wifi_set_coex_unsafe_channels = wifi_set_coex_unsafe_channels; + fn->wifi_twt_get_capability = twt_get_capability; + fn->wifi_twt_register_handler = twt_register_handler; + fn->wifi_twt_setup_request = twt_setup_request; + fn->wifi_twt_teardown_request = twt_teardown_request; + fn->wifi_twt_info_frame_request = twt_info_frame_request; + fn->wifi_twt_get_stats = twt_get_stats; + fn->wifi_twt_clear_stats = twt_clear_stats; + fn->wifi_multi_sta_set_primary_connection = wifi_multi_sta_set_primary_connection; + fn->wifi_multi_sta_set_use_case = wifi_multi_sta_set_use_case; + fn->wifi_set_voip_mode = wifi_set_voip_mode; + fn->wifi_set_dtim_config = wifi_set_dtim_config; + fn->wifi_get_usable_channels = wifi_get_usable_channels; + fn->wifi_trigger_subsystem_restart = wifi_trigger_subsystem_restart; + fn->wifi_get_supported_radio_combinations_matrix = wifi_get_supported_radio_combinations_matrix; + fn->wifi_set_indoor_state = wifi_set_indoor_state; + + return WIFI_SUCCESS; +} +#ifdef GOOGLE_WIFI_FW_CONFIG_VERSION_C_WRAPPER +#include <google_wifi_firmware_config_version_info.h> + +static void +wifi_check_valid_ota_version(wifi_interface_handle handle) +{ + bool valid = false; + int32_t default_ver = get_google_default_vendor_wifi_config_version(); + int32_t ota_ver = get_google_ota_updated_wifi_config_version(); + ALOGE("default_ver %d, ota_ver %d", default_ver, ota_ver); + + if (ota_ver > default_ver) { + valid = verify_google_ota_updated_wifi_config_integrity(); + } + + if (valid) { + ALOGE("Valid config files of OTA."); + wifi_hal_ota_update(handle, ota_ver); + } + else { + ALOGE("Do not valid config files of OTA."); + } +} +#endif // GOOGLE_WIFI_FW_CONFIG_VERSION_C_WRAPPER + +hal_info *halInfo = NULL; +wifi_error wifi_pre_initialize(void) +{ + srand(getpid()); + + int numIfaceHandles = 0; + wifi_interface_handle *ifaceHandles = NULL; + wifi_interface_handle wlan0Handle; + wifi_error result = WIFI_SUCCESS; + wifi_handle handle; + + ALOGE("wifi_pre_initialize"); + ALOGE("--- HAL version: %s ---\n", HAL_VERSION); + halInfo = (hal_info *)malloc(sizeof(hal_info)); + if (halInfo == NULL) { + ALOGE("Could not allocate hal_info"); + return WIFI_ERROR_UNKNOWN; + } + + memset(halInfo, 0, sizeof(*halInfo)); + + ALOGI("Creating socket"); + if (socketpair(AF_UNIX, SOCK_STREAM, 0, halInfo->cleanup_socks) == -1) { + ALOGE("Could not create cleanup sockets"); + free(halInfo); + return WIFI_ERROR_UNKNOWN; + } + + struct nl_sock *cmd_sock = wifi_create_nl_socket(WIFI_HAL_CMD_SOCK_PORT); + if (cmd_sock == NULL) { + ALOGE("Could not create handle"); + free(halInfo); + return WIFI_ERROR_UNKNOWN; + } + + /* Set the socket buffer size */ + if (nl_socket_set_buffer_size(cmd_sock, (256*1024), 0) < 0) { + ALOGE("Could not set size for cmd_sock: %s", + strerror(errno)); + } else { + ALOGV("nl_socket_set_buffer_size successful for cmd_sock"); + } + struct nl_sock *event_sock = wifi_create_nl_socket(WIFI_HAL_EVENT_SOCK_PORT); + if (event_sock == NULL) { + ALOGE("Could not create handle"); + nl_socket_free(cmd_sock); + free(halInfo); + return WIFI_ERROR_UNKNOWN; + } + + /* Set the socket buffer size */ + if (nl_socket_set_buffer_size(event_sock, (4*1024*1024), 0) < 0) { + ALOGE("Could not set size for event_sock: %s", + strerror(errno)); + } else { + ALOGV("nl_socket_set_buffer_size successful for event_sock"); + } + + struct nl_cb *cb = nl_socket_get_cb(event_sock); + if (cb == NULL) { + ALOGE("Could not create handle"); + nl_socket_free(cmd_sock); + nl_socket_free(event_sock); + free(halInfo); + return WIFI_ERROR_UNKNOWN; + } + + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, internal_no_seq_check, halInfo); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, internal_valid_message_handler, halInfo); + nl_cb_put(cb); + + halInfo->cmd_sock = cmd_sock; + halInfo->event_sock = event_sock; + halInfo->clean_up = false; + halInfo->in_event_loop = false; + + halInfo->event_cb = (cb_info *)malloc(sizeof(cb_info) * DEFAULT_EVENT_CB_SIZE); + halInfo->alloc_event_cb = DEFAULT_EVENT_CB_SIZE; + halInfo->num_event_cb = 0; + + halInfo->cmd = (cmd_info *)malloc(sizeof(cmd_info) * DEFAULT_CMD_SIZE); + halInfo->alloc_cmd = DEFAULT_CMD_SIZE; + halInfo->num_cmd = 0; + + halInfo->nl80211_family_id = genl_ctrl_resolve(cmd_sock, "nl80211"); + if (halInfo->nl80211_family_id < 0) { + ALOGE("Could not resolve nl80211 familty id"); + nl_socket_free(cmd_sock); + nl_socket_free(event_sock); + free(halInfo); + return WIFI_ERROR_UNKNOWN; + } + + pthread_mutex_init(&halInfo->cb_lock, NULL); + InitResponseLock(); + + handle = (wifi_handle) halInfo; + + if (wifi_init_interfaces(handle) != WIFI_SUCCESS) { + ALOGE("No wifi interface found"); + nl_socket_free(cmd_sock); + nl_socket_free(event_sock); + pthread_mutex_destroy(&halInfo->cb_lock); + free(halInfo); + return WIFI_ERROR_NOT_AVAILABLE; + } + + if ((wifi_add_membership(handle, "scan") < 0) || + (wifi_add_membership(handle, "mlme") < 0) || + (wifi_add_membership(handle, "regulatory") < 0) || + (wifi_add_membership(handle, "vendor") < 0)) { + ALOGE("Add membership failed"); + nl_socket_free(cmd_sock); + nl_socket_free(event_sock); + pthread_mutex_destroy(&halInfo->cb_lock); + free(halInfo); + return WIFI_ERROR_NOT_AVAILABLE; + } + + ALOGI("Initialized Wifi HAL Successfully; vendor cmd = %d", NL80211_CMD_VENDOR); + wlan0Handle = wifi_get_wlan_interface((wifi_handle)halInfo, ifaceHandles, numIfaceHandles); + + if (wlan0Handle != NULL) { + ALOGE("Calling preInit"); + if (!get_halutil_mode()) { +#ifdef GOOGLE_WIFI_FW_CONFIG_VERSION_C_WRAPPER + (void) wifi_check_valid_ota_version(wlan0Handle); +#endif // GOOGLE_WIFI_FW_CONFIG_VERSION_C_WRAPPER + result = wifi_hal_preInit(wlan0Handle); + if (result != WIFI_SUCCESS) { + ALOGE("wifi_hal_preInit failed"); + } + } + } + + return WIFI_SUCCESS; +} + +wifi_error wifi_initialize(wifi_handle *handle) +{ + + int numIfaceHandles = 0; + wifi_interface_handle *ifaceHandles = NULL; + wifi_interface_handle wlan0Handle; + wifi_error result = WIFI_SUCCESS; + + ALOGE("wifi_initialize"); + + if (halInfo == NULL) { + result = wifi_pre_initialize(); + if (result != WIFI_SUCCESS) { + ALOGE("wifi_initialize wifi_pre_initialize failed"); + return result; + } else { + ALOGE("wifi_initialize wifi_pre_initialize succeeded"); + } + } + + *handle = (wifi_handle) halInfo; + wlan0Handle = wifi_get_wlan_interface((wifi_handle)halInfo, ifaceHandles, numIfaceHandles); + + if (wlan0Handle != NULL) { + ALOGE("Calling Hal_init"); + if (!get_halutil_mode()) { + result = wifi_start_hal(wlan0Handle); + if (result != WIFI_SUCCESS) { + ALOGE("wifi_start_hal failed"); + } + } + } else { + ALOGI("Not Calling set alert handler as global_iface is NULL"); + return WIFI_ERROR_UNKNOWN; + } + return WIFI_SUCCESS; +} + +wifi_error wifi_wait_for_driver_ready(void) +{ + // This function will wait to make sure basic client netdev is created + // Function times out after 10 seconds + int count = (POLL_DRIVER_MAX_TIME_MS * 1000) / POLL_DRIVER_DURATION_US; + FILE *fd; + + do { + if ((fd = fopen("/sys/class/net/wlan0", "r")) != NULL) { + fclose(fd); + wifi_pre_initialize(); + return WIFI_SUCCESS; + } + usleep(POLL_DRIVER_DURATION_US); + } while(--count > 0); + + ALOGE("Timed out waiting on Driver ready ... "); + return WIFI_ERROR_TIMED_OUT; +} + +static int wifi_add_membership(wifi_handle handle, const char *group) +{ + hal_info *info = getHalInfo(handle); + + int id = wifi_get_multicast_id(handle, "nl80211", group); + if (id < 0) { + ALOGE("Could not find group %s", group); + return id; + } + + int ret = nl_socket_add_membership(info->event_sock, id); + if (ret < 0) { + ALOGE("Could not add membership to group %s", group); + } + + // ALOGI("Successfully added membership for group %s", group); + return ret; +} + +static void internal_cleaned_up_handler(wifi_handle handle) +{ + hal_info *info = getHalInfo(handle); + wifi_cleaned_up_handler cleaned_up_handler = info->cleaned_up_handler; + + ALOGI("internal clean up"); + + if (info->cmd_sock != 0) { + ALOGI("cmd_sock non null. clean up"); + close(info->cleanup_socks[0]); + close(info->cleanup_socks[1]); + nl_socket_free(info->cmd_sock); + nl_socket_free(info->event_sock); + info->cmd_sock = NULL; + info->event_sock = NULL; + } + + if (cleaned_up_handler) { + ALOGI("cleanup_handler cb"); + (*cleaned_up_handler)(handle); + } else { + ALOGI("!! clean up handler is null!!"); + } + DestroyResponseLock(); + pthread_mutex_destroy(&info->cb_lock); + free(info); + + ALOGI("Internal cleanup completed"); +} + +void wifi_internal_module_cleanup() +{ + nan_deinit_handler(); + twt_deinit_handler(); +} + +void wifi_cleanup(wifi_handle handle, wifi_cleaned_up_handler handler) +{ + if (!handle) { + ALOGE("Handle is null"); + return; + } + + hal_info *info = getHalInfo(handle); + wifi_error result; + + int numIfaceHandles = 0; + wifi_interface_handle *ifaceHandles = NULL; + wifi_interface_handle wlan0Handle; + + info->cleaned_up_handler = handler; + + wlan0Handle = wifi_get_wlan_interface((wifi_handle) info, ifaceHandles, numIfaceHandles); + + if (wlan0Handle != NULL) { + ALOGE("Calling hal cleanup"); + if (!get_halutil_mode()) { + result = wifi_stop_hal(wlan0Handle); + if (result != WIFI_SUCCESS) { + ALOGE("wifi_stop_hal failed"); + } + } + + } else { + ALOGE("Not cleaning up hal as global_iface is NULL"); + } + + /* calling internal modules or cleanup */ + wifi_internal_module_cleanup(); + pthread_mutex_lock(&info->cb_lock); + + int bad_commands = 0; + + ALOGI("event_cb callbacks left: %d ", info->num_event_cb); + for (int i = 0; i < info->num_event_cb; i++) { + ALOGI("event_cb cleanup. index:%d", i); + cb_info *cbi = &(info->event_cb[i]); + if (!cbi) { + ALOGE("cbi null for index %d", i); + continue; + } + ALOGI("event_cb cleanup. vendor cmd:%d sub_cmd:%d", cbi->vendor_id, cbi->vendor_subcmd); + WifiCommand *cmd = (WifiCommand *)cbi->cb_arg; + if (cmd != NULL) { + ALOGI("Command left in event_cb %p", cmd); + } + } + + ALOGI("Check bad commands: num_cmd:%d bad_commands:%d", info->num_cmd, bad_commands); + while (info->num_cmd > bad_commands) { + int num_cmd = info->num_cmd; + cmd_info *cmdi = &(info->cmd[bad_commands]); + WifiCommand *cmd = cmdi->cmd; + if (cmd != NULL) { + ALOGI("Cancelling command %p:%s", cmd, cmd->getType()); + pthread_mutex_unlock(&info->cb_lock); + cmd->cancel(); + pthread_mutex_lock(&info->cb_lock); + if (num_cmd == info->num_cmd) { + ALOGI("Cancelling command %p:%s did not work", cmd, (cmd ? cmd->getType(): "")); + bad_commands++; + } + /* release reference added when command is saved */ + cmd->releaseRef(); + } + } + + for (int i = 0; i < info->num_event_cb; i++) { + cb_info *cbi = &(info->event_cb[i]); + if (!cbi) { + ALOGE("cbi null for index %d", i); + continue; + } + WifiCommand *cmd = (WifiCommand *)cbi->cb_arg; + ALOGE("Leaked command %p", cmd); + } + if (!get_halutil_mode()) { + wifi_cleanup_dynamic_ifaces(handle); + } + pthread_mutex_unlock(&info->cb_lock); + + info->clean_up = true; + + if (TEMP_FAILURE_RETRY(write(info->cleanup_socks[0], "Exit", 4)) < 1) { + // As a fallback set the cleanup flag to TRUE + ALOGE("could not write to the cleanup socket"); + } + ALOGE("wifi_clean_up done"); +} + +static int internal_pollin_handler(wifi_handle handle) +{ + hal_info *info = getHalInfo(handle); + struct nl_cb *cb = nl_socket_get_cb(info->event_sock); + int res = nl_recvmsgs(info->event_sock, cb); + // ALOGD("nl_recvmsgs returned %d", res); + nl_cb_put(cb); + return res; +} + +/* Run event handler */ +void wifi_event_loop(wifi_handle handle) +{ + hal_info *info = getHalInfo(handle); + if (info->in_event_loop) { + return; + } else { + info->in_event_loop = true; + } + + pollfd pfd[2]; + memset(&pfd[0], 0, sizeof(pollfd) * 2); + + pfd[0].fd = nl_socket_get_fd(info->event_sock); + pfd[0].events = POLLIN; + pfd[1].fd = info->cleanup_socks[1]; + pfd[1].events = POLLIN; + + char buf[2048]; + /* TODO: Add support for timeouts */ + + do { + int timeout = -1; /* Infinite timeout */ + pfd[0].revents = 0; + pfd[1].revents = 0; + // ALOGI("Polling socket"); + int result = TEMP_FAILURE_RETRY(poll(pfd, 2, timeout)); + if (result < 0) { + // ALOGE("Error polling socket"); + } else if (pfd[0].revents & POLLERR) { + ALOGE("POLL Error; error no = %d (%s)", errno, strerror(errno)); + ssize_t result2 = TEMP_FAILURE_RETRY(read(pfd[0].fd, buf, sizeof(buf))); + ALOGE("Read after POLL returned %zd, error no = %d (%s)", result2, + errno, strerror(errno)); + if (errno == WIFI_HAL_EVENT_BUFFER_NOT_AVAILABLE) { + ALOGE("Exit, No buffer space"); + break; + } + } else if (pfd[0].revents & POLLHUP) { + ALOGE("Remote side hung up"); + break; + } else if (pfd[0].revents & POLLIN && !info->clean_up) { + // ALOGI("Found some events!!!"); + internal_pollin_handler(handle); + } else if (pfd[1].revents & POLLIN) { + ALOGI("Got a signal to exit!!!"); + } else { + ALOGE("Unknown event - %0x, %0x", pfd[0].revents, pfd[1].revents); + } + } while (!info->clean_up); + + internal_cleaned_up_handler(handle); + ALOGE("Exit %s", __FUNCTION__); +} + +/////////////////////////////////////////////////////////////////////////////////////// + +static int internal_no_seq_check(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + +static int internal_valid_message_handler(nl_msg *msg, void *arg) +{ + // ALOGI("got an event"); + + wifi_handle handle = (wifi_handle)arg; + hal_info *info = getHalInfo(handle); + + WifiEvent event(msg); + int res = event.parse(); + if (res < 0) { + ALOGE("Failed to parse event: %d", res); + return NL_SKIP; + } + + int cmd = event.get_cmd(); + uint32_t vendor_id = 0; + int subcmd = 0; + + if (cmd == NL80211_CMD_VENDOR) { + vendor_id = event.get_u32(NL80211_ATTR_VENDOR_ID); + subcmd = event.get_u32(NL80211_ATTR_VENDOR_SUBCMD); + ALOGV("event received %s, vendor_id = 0x%0x, subcmd = 0x%0x", + event.get_cmdString(), vendor_id, subcmd); + } else { + // ALOGV("event received %s", event.get_cmdString()); + } + + // ALOGV("event received %s, vendor_id = 0x%0x", event.get_cmdString(), vendor_id); + // event.log(); + + pthread_mutex_lock(&info->cb_lock); + + for (int i = 0; i < info->num_event_cb; i++) { + if (cmd == info->event_cb[i].nl_cmd) { + if (cmd == NL80211_CMD_VENDOR + && ((vendor_id != info->event_cb[i].vendor_id) + || (subcmd != info->event_cb[i].vendor_subcmd))) + { + /* event for a different vendor, ignore it */ + continue; + } + + cb_info *cbi = &(info->event_cb[i]); + nl_recvmsg_msg_cb_t cb_func = cbi->cb_func; + void *cb_arg = cbi->cb_arg; + WifiCommand *cmd = (WifiCommand *)cbi->cb_arg; + if (cmd != NULL) { + cmd->addRef(); + } + pthread_mutex_unlock(&info->cb_lock); + if (cb_func) + (*cb_func)(msg, cb_arg); + if (cmd != NULL) { + cmd->releaseRef(); + } + + return NL_OK; + } + } + + pthread_mutex_unlock(&info->cb_lock); + return NL_OK; +} + +/////////////////////////////////////////////////////////////////////////////////////// + +class GetMulticastIdCommand : public WifiCommand +{ +private: + const char *mName; + const char *mGroup; + int mId; +public: + GetMulticastIdCommand(wifi_handle handle, const char *name, const char *group) + : WifiCommand("GetMulticastIdCommand", handle, 0) + { + mName = name; + mGroup = group; + mId = -1; + } + + int getId() { + return mId; + } + + virtual int create() { + int nlctrlFamily = genl_ctrl_resolve(mInfo->cmd_sock, "nlctrl"); + // ALOGI("ctrl family = %d", nlctrlFamily); + int ret = mMsg.create(nlctrlFamily, CTRL_CMD_GETFAMILY, 0, 0); + if (ret < 0) { + return ret; + } + ret = mMsg.put_string(CTRL_ATTR_FAMILY_NAME, mName); + return ret; + } + + virtual int handleResponse(WifiEvent& reply) { + + // ALOGI("handling response in %s", __func__); + + struct nlattr **tb = reply.attributes(); + struct nlattr *mcgrp = NULL; + int i; + + if (!tb[CTRL_ATTR_MCAST_GROUPS]) { + ALOGI("No multicast groups found"); + return NL_SKIP; + } else { + // ALOGI("Multicast groups attr size = %d", nla_len(tb[CTRL_ATTR_MCAST_GROUPS])); + } + + for_each_attr(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) { + + // ALOGI("Processing group"); + struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1]; + nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, (nlattr *)nla_data(mcgrp), + nla_len(mcgrp), NULL); + if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] || !tb2[CTRL_ATTR_MCAST_GRP_ID]) { + continue; + } + + char *grpName = (char *)nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]); + int grpNameLen = nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME]); + + // ALOGI("Found group name %s", grpName); + + if (strncmp(grpName, mGroup, grpNameLen) != 0) + continue; + + mId = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]); + break; + } + + return NL_SKIP; + } + +}; + +class SetPnoMacAddrOuiCommand : public WifiCommand { + +private: + byte *mOui; + feature_set *fset; + feature_set *feature_matrix; + int *fm_size; + int set_size_max; +public: + SetPnoMacAddrOuiCommand(wifi_interface_handle handle, oui scan_oui) + : WifiCommand("SetPnoMacAddrOuiCommand", handle, 0) + { + mOui = scan_oui; + fset = NULL; + feature_matrix = NULL; + fm_size = NULL; + set_size_max = 0; + } + + int createRequest(WifiRequest& request, int subcmd, byte *scan_oui) { + int result = request.create(GOOGLE_OUI, subcmd); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put(ANDR_WIFI_ATTRIBUTE_PNO_RANDOM_MAC_OUI, scan_oui, DOT11_OUI_LEN); + if (result < 0) { + return result; + } + + request.attr_end(data); + return WIFI_SUCCESS; + + } + + int start() { + ALOGD("Sending mac address OUI"); + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request, WIFI_SUBCMD_SET_PNO_RANDOM_MAC_OUI, mOui); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create request; result = %d", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to set scanning mac OUI; result = %d", result); + } + + return result; + } +protected: + virtual int handleResponse(WifiEvent& reply) { + ALOGD("Request complete!"); + /* Nothing to do on response! */ + return NL_SKIP; + } +}; + +class SetNodfsCommand : public WifiCommand { + +private: + u32 mNoDfs; +public: + SetNodfsCommand(wifi_interface_handle handle, u32 nodfs) + : WifiCommand("SetNodfsCommand", handle, 0) { + mNoDfs = nodfs; + } + virtual int create() { + int ret; + + ret = mMsg.create(GOOGLE_OUI, WIFI_SUBCMD_NODFS_SET); + if (ret < 0) { + ALOGE("Can't create message to send to driver - %d", ret); + return ret; + } + + nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); + ret = mMsg.put_u32(ANDR_WIFI_ATTRIBUTE_NODFS_SET, mNoDfs); + if (ret < 0) { + return ret; + } + + mMsg.attr_end(data); + return WIFI_SUCCESS; + } +}; + +class SetCountryCodeCommand : public WifiCommand { +private: + const char *mCountryCode; +public: + SetCountryCodeCommand(wifi_interface_handle handle, const char *country_code) + : WifiCommand("SetCountryCodeCommand", handle, 0) { + mCountryCode = country_code; + } + virtual int create() { + int ret; + + ret = mMsg.create(GOOGLE_OUI, WIFI_SUBCMD_SET_COUNTRY_CODE); + if (ret < 0) { + ALOGE("Can't create message to send to driver - %d", ret); + return ret; + } + + nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); + ret = mMsg.put_string(ANDR_WIFI_ATTRIBUTE_COUNTRY, mCountryCode); + if (ret < 0) { + return ret; + } + + mMsg.attr_end(data); + return WIFI_SUCCESS; + + } +}; + +class SetRSSIMonitorCommand : public WifiCommand { +private: + s8 mMax_rssi; + s8 mMin_rssi; + wifi_rssi_event_handler mHandler; +public: + SetRSSIMonitorCommand(wifi_request_id id, wifi_interface_handle handle, + s8 max_rssi, s8 min_rssi, wifi_rssi_event_handler eh) + : WifiCommand("SetRSSIMonitorCommand", handle, id), mMax_rssi(max_rssi), mMin_rssi + (min_rssi), mHandler(eh) + { + ALOGI("SetRSSIMonitorCommand %p created", this); + } + + virtual ~SetRSSIMonitorCommand() { + /* + * Mostly, this call will be no effect. However, this could be valid + * when object destroy without calling unregisterVendorHandler(). + * This is added to protect hal crash due to use-after-free. + */ + ALOGI("Try to remove event handler if exist, vendor 0x%0x, subcmd 0x%x", + GOOGLE_OUI, GOOGLE_RSSI_MONITOR_EVENT); + unregisterVendorHandlerWithoutLock(GOOGLE_OUI, GOOGLE_RSSI_MONITOR_EVENT); + ALOGI("SetRSSIMonitorCommand %p destroyed", this); + } + + virtual void addRef() { + int refs = __sync_add_and_fetch(&mRefs, 1); + ALOGI("addRef: WifiCommand %p has %d references", this, refs); + } + + virtual void releaseRef() { + int refs = __sync_sub_and_fetch(&mRefs, 1); + if (refs == 0) { + ALOGI("releaseRef: WifiCommand %p has deleted", this); + delete this; + } else { + ALOGI("releaseRef: WifiCommand %p has %d references", this, refs); + } + } + + int createRequest(WifiRequest& request, int enable) { + int result = request.create(GOOGLE_OUI, WIFI_SUBCMD_SET_RSSI_MONITOR); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(RSSI_MONITOR_ATTRIBUTE_MAX_RSSI, (enable ? mMax_rssi: 0)); + if (result < 0) { + return result; + } + ALOGD("create request"); + result = request.put_u32(RSSI_MONITOR_ATTRIBUTE_MIN_RSSI, (enable? mMin_rssi: 0)); + if (result < 0) { + return result; + } + result = request.put_u32(RSSI_MONITOR_ATTRIBUTE_START, enable); + if (result < 0) { + return result; + } + request.attr_end(data); + return result; + } + + int start() { + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request, 1); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to create request; result = %d", result); + return result; + } + + registerVendorHandler(GOOGLE_OUI, GOOGLE_RSSI_MONITOR_EVENT); + ALOGI("Register GOOGLE_RSSI_MONITOR_EVENT handler"); + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + unregisterVendorHandler(GOOGLE_OUI, GOOGLE_RSSI_MONITOR_EVENT); + ALOGE("Failed to set RSSI Monitor, result = %d", result); + return result; + } + + ALOGI("Successfully set RSSI monitoring"); + return result; + } + + virtual int cancel() { + unregisterVendorHandler(GOOGLE_OUI, GOOGLE_RSSI_MONITOR_EVENT); + + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request, 0); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create request; result = %d", result); + } else { + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to stop RSSI monitoring = %d", result); + } + } + return WIFI_SUCCESS; + } + + virtual int handleResponse(WifiEvent& reply) { + /* Nothing to do on response! */ + return NL_SKIP; + } + + virtual int handleEvent(WifiEvent& event) { + ALOGI("Got a RSSI monitor event"); + + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = event.get_vendor_data_len(); + + if (vendor_data == NULL || len == 0) { + ALOGI("RSSI monitor: No data"); + return NL_SKIP; + } + /* driver<->HAL event structure */ + #define RSSI_MONITOR_EVT_VERSION 1 + typedef struct { + u8 version; + s8 cur_rssi; + mac_addr BSSID; + } rssi_monitor_evt; + + rssi_monitor_evt *data = (rssi_monitor_evt *)event.get_vendor_data(); + + if (data->version != RSSI_MONITOR_EVT_VERSION) { + ALOGI("Event version mismatch %d, expected %d", data->version, RSSI_MONITOR_EVT_VERSION); + return NL_SKIP; + } + + if (*mHandler.on_rssi_threshold_breached) { + (*mHandler.on_rssi_threshold_breached)(id(), data->BSSID, data->cur_rssi); + } else { + ALOGW("No RSSI monitor handler registered"); + } + + return NL_SKIP; + } + +}; + +class AndroidPktFilterCommand : public WifiCommand { + private: + const u8* mProgram; + u8* mReadProgram; + u32 mProgramLen; + u32* mVersion; + u32* mMaxLen; + int mReqType; + public: + AndroidPktFilterCommand(wifi_interface_handle handle, + u32* version, u32* max_len) + : WifiCommand("AndroidPktFilterCommand", handle, 0), + mVersion(version), mMaxLen(max_len), + mReqType(GET_APF_CAPABILITIES) + { + mProgram = NULL; + mProgramLen = 0; + } + + AndroidPktFilterCommand(wifi_interface_handle handle, + const u8* program, u32 len) + : WifiCommand("AndroidPktFilterCommand", handle, 0), + mProgram(program), mProgramLen(len), + mReqType(SET_APF_PROGRAM) + { + mVersion = NULL; + mMaxLen = NULL; + } + + AndroidPktFilterCommand(wifi_interface_handle handle, + u8* host_dst, u32 length) + : WifiCommand("AndroidPktFilterCommand", handle, 0), + mReadProgram(host_dst), mProgramLen(length), + mReqType(READ_APF_PROGRAM) + { + } + + int createRequest(WifiRequest& request) { + if (mReqType == SET_APF_PROGRAM) { + ALOGI("\n%s: APF set program request\n", __FUNCTION__); + return createSetPktFilterRequest(request); + } else if (mReqType == GET_APF_CAPABILITIES) { + ALOGI("\n%s: APF get capabilities request\n", __FUNCTION__); + return createGetPktFilterCapabilitesRequest(request); + } else if (mReqType == READ_APF_PROGRAM) { + ALOGI("\n%s: APF read packet filter request\n", __FUNCTION__); + return createReadPktFilterRequest(request); + } else { + ALOGE("\n%s Unknown APF request\n", __FUNCTION__); + return WIFI_ERROR_NOT_SUPPORTED; + } + return WIFI_SUCCESS; + } + + int createSetPktFilterRequest(WifiRequest& request) { + u8 *program = new u8[mProgramLen]; + NULL_CHECK_RETURN(program, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + int result = request.create(GOOGLE_OUI, APF_SUBCMD_SET_FILTER); + if (result < 0) { + delete[] program; + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(APF_ATTRIBUTE_PROGRAM_LEN, mProgramLen); + if (result < 0) { + goto exit; + } + memcpy(program, mProgram, mProgramLen); + result = request.put(APF_ATTRIBUTE_PROGRAM, program, mProgramLen); + if (result < 0) { + goto exit; + } +exit: request.attr_end(data); + delete[] program; + return result; + } + + int createGetPktFilterCapabilitesRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, APF_SUBCMD_GET_CAPABILITIES); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + request.attr_end(data); + return result; + } + + int createReadPktFilterRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, APF_SUBCMD_READ_FILTER); + if (result < 0) { + return result; + } + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + request.attr_end(data); + return result; + } + + int start() { + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request); + if (result < 0) { + return result; + } + result = requestResponse(request); + if (result < 0) { + ALOGI("Request Response failed for APF, result = %d", result); + return result; + } + ALOGI("Done!"); + return result; + } + + int cancel() { + return WIFI_SUCCESS; + } + + int handleResponse(WifiEvent& reply) { + ALOGD("In SetAPFCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + ALOGD("Id = %0x, subcmd = %d, len = %d", id, subcmd, len); + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in SetAPFCommand response; ignoring it"); + return NL_SKIP; + } + if( mReqType == SET_APF_PROGRAM) { + ALOGD("Response received for set packet filter command\n"); + } else if (mReqType == GET_APF_CAPABILITIES) { + *mVersion = 0; + *mMaxLen = 0; + ALOGD("Response received for get packet filter capabilities command\n"); + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == APF_ATTRIBUTE_VERSION) { + *mVersion = it.get_u32(); + ALOGI("APF version is %d\n", *mVersion); + } else if (it.get_type() == APF_ATTRIBUTE_MAX_LEN) { + *mMaxLen = it.get_u32(); + ALOGI("APF max len is %d\n", *mMaxLen); + } else { + ALOGE("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + } else if (mReqType == READ_APF_PROGRAM) { + ALOGD("Read packet filter, mProgramLen = %d\n", mProgramLen); + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == APF_ATTRIBUTE_PROGRAM) { + u8 *buffer = NULL; + buffer = (u8 *)it.get_data(); + memcpy(mReadProgram, buffer, mProgramLen); + } else if (it.get_type() == APF_ATTRIBUTE_PROGRAM_LEN) { + int apf_length = it.get_u32(); + ALOGD("apf program length = %d\n", apf_length); + } + } + } + return NL_OK; + } + + int handleEvent(WifiEvent& event) { + /* No Event to receive for APF commands */ + return NL_SKIP; + } +}; + +class SetNdoffloadCommand : public WifiCommand { + +private: + u8 mEnable; +public: + SetNdoffloadCommand(wifi_interface_handle handle, u8 enable) + : WifiCommand("SetNdoffloadCommand", handle, 0) { + mEnable = enable; + } + virtual int create() { + int ret; + + ret = mMsg.create(GOOGLE_OUI, WIFI_SUBCMD_CONFIG_ND_OFFLOAD); + if (ret < 0) { + ALOGE("Can't create message to send to driver - %d", ret); + return ret; + } + + nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); + ret = mMsg.put_u8(ANDR_WIFI_ATTRIBUTE_ND_OFFLOAD_VALUE, mEnable); + if (ret < 0) { + return ret; + } + + mMsg.attr_end(data); + return WIFI_SUCCESS; + } +}; + +class GetFeatureSetCommand : public WifiCommand { + +private: + int feature_type; + feature_set *fset; + feature_set *feature_matrix; + int *fm_size; + int set_size_max; +public: + GetFeatureSetCommand(wifi_interface_handle handle, int feature, feature_set *set, + feature_set set_matrix[], int *size, int max_size) + : WifiCommand("GetFeatureSetCommand", handle, 0) { + feature_type = feature; + fset = set; + feature_matrix = set_matrix; + fm_size = size; + set_size_max = max_size; + } + + virtual int create() { + int ret; + + if(feature_type == ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET) { + ret = mMsg.create(GOOGLE_OUI, WIFI_SUBCMD_GET_FEATURE_SET); + } else if (feature_type == ANDR_WIFI_ATTRIBUTE_FEATURE_SET) { + ret = mMsg.create(GOOGLE_OUI, WIFI_SUBCMD_GET_FEATURE_SET_MATRIX); + } else { + ALOGE("Unknown feature type %d", feature_type); + return -1; + } + + if (ret < 0) { + ALOGE("Can't create message to send to driver - %d", ret); + } + + return ret; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + + ALOGV("In GetFeatureSetCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + ALOGV("Id = %0x, subcmd = %d, len = %d", id, subcmd, len); + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in GetFeatureSetCommand response; ignoring it"); + return NL_SKIP; + } + if(feature_type == ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET) { + void *data = reply.get_vendor_data(); + if(!fset) { + ALOGE("Buffers pointers not set"); + return NL_SKIP; + } + memcpy(fset, data, min(len, (int) sizeof(*fset))); + } else { + int num_features_set = 0; + int i = 0; + + if(!feature_matrix || !fm_size) { + ALOGE("Buffers pointers not set"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET) { + num_features_set = it.get_u32(); + ALOGV("Got feature list with %d concurrent sets", num_features_set); + if(set_size_max && (num_features_set > set_size_max)) + num_features_set = set_size_max; + *fm_size = num_features_set; + } else if ((it.get_type() == ANDR_WIFI_ATTRIBUTE_FEATURE_SET) && + i < num_features_set) { + feature_matrix[i] = it.get_u32(); + i++; + } else { + ALOGW("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + + } + return NL_OK; + } + +}; +///////////////////////////////////////////////////////////////////// +class GetRadioComboCommand : public WifiCommand { +private: + wifi_radio_combination_matrix *rcmatrix; + u32* rc_size; + u32 set_size_max; + int ret = 0; + +public: + GetRadioComboCommand(wifi_interface_handle handle, u32 max_size, u32* size, + wifi_radio_combination_matrix *radio_combination_matrix) + : WifiCommand("GetRadioComboCommand", handle, 0), rcmatrix(radio_combination_matrix), + rc_size(size), set_size_max(max_size) + { + } + + virtual int createRequest(WifiRequest& mMsg) { + ret = mMsg.create(GOOGLE_OUI, WIFI_SUBCMD_GET_RADIO_COMBO_MATRIX); + if (ret < 0) { + ALOGE("Can't create message to send to driver - %d", ret); + } + nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); + mMsg.attr_end(data); + + return ret; + } + + int start() { + WifiRequest request(familyId(), ifaceId()); + ret = createRequest(request); + if (ret < 0) { + ALOGE("Request failed for radio_combo_matrix, result = %d", ret); + return ret; + } + ret = requestResponse(request); + if (ret < 0) { + ALOGE("Request Response failed for radio_combo_matrix, result = %d", ret); + return ret; + } + ALOGD("Done! %s", __FUNCTION__); + return ret; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + ALOGD("In GetRadioComboCommand::handleResponse"); + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + ALOGV("Id = %0x, subcmd = %d, len = %d", id, subcmd, len); + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in GetRadioComboCommand response; ignoring it"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == ANDR_WIFI_ATTRIBUTE_RADIO_COMBO_MATRIX) { + void *data = it.get_data(); + *rc_size = it.get_len(); + if (!data || !*rc_size) { + ALOGE("Buffers pointers not set"); + return NL_SKIP; + } + if (set_size_max < *rc_size) { + ALOGE("Unexpected buffers size"); + return NL_SKIP; + } + memcpy(rcmatrix, data, min(len, *rc_size)); + } else { + ALOGW("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + + ALOGD("GetRadioComboCommand::Success"); + return NL_OK; + } +}; +///////////////////////////////////////////////////////////////////// + +class SetLatencyModeCommand : public WifiCommand { +private: + u32 mLatencyMode; +public: + SetLatencyModeCommand(wifi_interface_handle handle, u32 LatencyMode) + : WifiCommand("SetLatencyModeCommand", handle, 0) { + mLatencyMode = LatencyMode; + } + + virtual int create() { + int ret; + + /* Check for invalid latency Mode */ + if ((mLatencyMode != WIFI_LATENCY_MODE_NORMAL) && + (mLatencyMode != WIFI_LATENCY_MODE_LOW)) { + ALOGE("SetLatencyModeCommand: Invalid mode: %d", mLatencyMode); + return WIFI_ERROR_UNKNOWN; + } + + ret = mMsg.create(GOOGLE_OUI, WIFI_SUBCMD_SET_LATENCY_MODE); + if (ret < 0) { + ALOGE("Can't create message to send to driver - %d", ret); + return ret; + } + + nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); + ret = mMsg.put_u32(ANDR_WIFI_ATTRIBUTE_LATENCY_MODE, mLatencyMode); + if (ret < 0) { + return ret; + } + + mMsg.attr_end(data); + return WIFI_SUCCESS; + } +}; + +class SetIndoorCommand : public WifiCommand { + private: + bool mIndoor; + public: + SetIndoorCommand(wifi_interface_handle handle, bool isIndoor) + : WifiCommand("SetIndoorCommand", handle, 0) { + mIndoor = isIndoor; + } + + virtual int create() { + int ret; + + if ((mIndoor != 0) && + (mIndoor != 1)) { + ALOGE("SetIndoorCommand: Invalid mode: %d", mIndoor); + return WIFI_ERROR_UNKNOWN; + } + + ret = mMsg.create(GOOGLE_OUI, WIFI_SUBCMD_CONFIG_INDOOR_STATE); + if (ret < 0) { + ALOGE("Can't create message to send to driver - %d", ret); + return ret; + } + + nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); + ret = mMsg.put_u8(ANDR_WIFI_ATTRIBUTE_INDOOR, mIndoor); + if (ret < 0) { + return ret; + } + + mMsg.attr_end(data); + return WIFI_SUCCESS; + } +}; + +static int wifi_get_multicast_id(wifi_handle handle, const char *name, const char *group) +{ + GetMulticastIdCommand cmd(handle, name, group); + int res = cmd.requestResponse(); + if (res < 0) + return res; + else + return cmd.getId(); +} + +///////////////////////////////////////////////////////////////////////// + +static bool is_wifi_interface(const char *name) +{ + if (strncmp(name, "wlan", 4) != 0 && strncmp(name, "swlan", 5) != 0 && + strncmp(name, "p2p", 3) != 0 && strncmp(name, "aware", 5) != 0) { + /* not a wifi interface; ignore it */ + return false; + } else { + return true; + } +} + +static int get_interface(const char *name, interface_info *info) +{ + int size = 0; + size = strlcpy(info->name, name, sizeof(info->name)); + if (size >= sizeof(info->name)) { + return WIFI_ERROR_OUT_OF_MEMORY; + } + info->id = if_nametoindex(name); + // ALOGI("found an interface : %s, id = %d", name, info->id); + return WIFI_SUCCESS; +} + +wifi_error wifi_init_interfaces(wifi_handle handle) +{ + hal_info *info = (hal_info *)handle; + + struct dirent *de; + + DIR *d = opendir("/sys/class/net"); + if (d == 0) + return WIFI_ERROR_UNKNOWN; + + int n = 0; + while ((de = readdir(d))) { + if (de->d_name[0] == '.') + continue; + if (is_wifi_interface(de->d_name) ) { + n++; + } + } + + closedir(d); + + if (n == 0) + return WIFI_ERROR_NOT_AVAILABLE; + + d = opendir("/sys/class/net"); + if (d == 0) + return WIFI_ERROR_UNKNOWN; + + /* Have place holder for 3 virtual interfaces */ + n += MAX_VIRTUAL_IFACES; + info->interfaces = (interface_info **)calloc(n, sizeof(interface_info *) * n); + if (!info->interfaces) { + info->num_interfaces = 0; + closedir(d); + return WIFI_ERROR_OUT_OF_MEMORY; + } + + int i = 0; + while ((de = readdir(d))) { + if (de->d_name[0] == '.') + continue; + if (is_wifi_interface(de->d_name)) { + interface_info *ifinfo = (interface_info *)malloc(sizeof(interface_info)); + if (!ifinfo) { + free(info->interfaces); + info->num_interfaces = 0; + closedir(d); + return WIFI_ERROR_OUT_OF_MEMORY; + } + memset(ifinfo, 0, sizeof(interface_info)); + if (get_interface(de->d_name, ifinfo) != WIFI_SUCCESS) { + continue; + } + /* Mark as static iface */ + ifinfo->is_virtual = false; + ifinfo->handle = handle; + info->interfaces[i] = ifinfo; + i++; + } + } + + closedir(d); + + info->num_interfaces = i; + info->max_num_interfaces = n; + return WIFI_SUCCESS; +} + +wifi_error wifi_get_ifaces(wifi_handle handle, int *num, wifi_interface_handle **interfaces) +{ + hal_info *info = (hal_info *)handle; + + *interfaces = (wifi_interface_handle *)info->interfaces; + *num = info->num_interfaces; + + return WIFI_SUCCESS; +} + +wifi_error wifi_add_iface_hal_info(wifi_handle handle, const char* ifname) +{ + hal_info *info = NULL; + int i = 0; + + info = (hal_info *)handle; + if (info == NULL) { + ALOGE("Could not find info\n"); + return WIFI_ERROR_UNKNOWN; + } + + ALOGI("%s: add interface_info for iface: %s\n", __FUNCTION__, ifname); + if (info->num_interfaces == MAX_VIRTUAL_IFACES) { + ALOGE("No space. max limit reached for virtual interfaces %d\n", info->num_interfaces); + return WIFI_ERROR_OUT_OF_MEMORY; + } + + interface_info *ifinfo = (interface_info *)malloc(sizeof(interface_info)); + if (!ifinfo) { + free(info->interfaces); + info->num_interfaces = 0; + return WIFI_ERROR_OUT_OF_MEMORY; + } + + ifinfo->handle = handle; + while (i < info->max_num_interfaces) { + if (info->interfaces[i] == NULL) { + if (get_interface(ifname, ifinfo) != WIFI_SUCCESS) { + continue; + } + ifinfo->is_virtual = true; + info->interfaces[i] = ifinfo; + info->num_interfaces++; + ALOGI("%s: Added iface: %s at the index %d\n", __FUNCTION__, ifname, i); + break; + } + i++; + } + return WIFI_SUCCESS; +} + +wifi_error wifi_clear_iface_hal_info(wifi_handle handle, const char* ifname) +{ + hal_info *info = (hal_info *)handle; + int i = 0; + + ALOGI("%s: clear hal info for iface: %s\n", __FUNCTION__, ifname); + while (i < info->max_num_interfaces) { + if ((info->interfaces[i] != NULL) && + strncmp(info->interfaces[i]->name, ifname, + sizeof(info->interfaces[i]->name)) == 0) { + free(info->interfaces[i]); + info->interfaces[i] = NULL; + info->num_interfaces--; + ALOGI("%s: Cleared the index = %d for iface: %s\n", __FUNCTION__, i, ifname); + break; + } + i++; + } + if (i < info->num_interfaces) { + for (int j = i; j < info->num_interfaces; j++) { + info->interfaces[j] = info->interfaces[j+1]; + } + info->interfaces[info->num_interfaces] = NULL; + } + return WIFI_SUCCESS; +} + +wifi_interface_handle wifi_get_wlan_interface(wifi_handle info, wifi_interface_handle *ifaceHandles, int numIfaceHandles) +{ + char buf[EVENT_BUF_SIZE]; + wifi_interface_handle wlan0Handle; + wifi_error res = wifi_get_ifaces((wifi_handle)info, &numIfaceHandles, &ifaceHandles); + if (res < 0) { + return NULL; + } + for (int i = 0; i < numIfaceHandles; i++) { + if (wifi_get_iface_name(ifaceHandles[i], buf, sizeof(buf)) == WIFI_SUCCESS) { + if (strncmp(buf, "wlan0", 5) == 0) { + ALOGI("found interface %s\n", buf); + wlan0Handle = ifaceHandles[i]; + return wlan0Handle; + } + } + } + return NULL; +} +wifi_error wifi_get_iface_name(wifi_interface_handle handle, char *name, size_t size) +{ + interface_info *info = (interface_info *)handle; + strncpy(name, info->name, (IFNAMSIZ)); + name[IFNAMSIZ - 1] = '\0'; + return WIFI_SUCCESS; +} + +wifi_interface_handle wifi_get_iface_handle(wifi_handle handle, char *name) +{ + char buf[EVENT_BUF_SIZE]; + wifi_interface_handle *ifaceHandles; + int numIfaceHandles; + wifi_interface_handle ifHandle; + + wifi_error res = wifi_get_ifaces((wifi_handle)handle, &numIfaceHandles, &ifaceHandles); + if (res < 0) { + return NULL; + } + for (int i = 0; i < numIfaceHandles; i++) { + if (wifi_get_iface_name(ifaceHandles[i], buf, sizeof(buf)) == WIFI_SUCCESS) { + if (strcmp(buf, name) == 0) { + ALOGI("found interface %s\n", buf); + ifHandle = ifaceHandles[i]; + return ifHandle; + } + } + } + return NULL; +} + +wifi_error wifi_get_supported_feature_set(wifi_interface_handle handle, feature_set *set) +{ + GetFeatureSetCommand command(handle, ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET, set, NULL, NULL, 1); + return (wifi_error) command.requestResponse(); +} + +wifi_error wifi_get_concurrency_matrix(wifi_interface_handle handle, int set_size_max, + feature_set set[], int *set_size) +{ + GetFeatureSetCommand command(handle, ANDR_WIFI_ATTRIBUTE_FEATURE_SET, NULL, + set, set_size, set_size_max); + return (wifi_error) command.requestResponse(); +} + +wifi_error wifi_get_supported_radio_combinations_matrix(wifi_handle handle, + u32 max_size, u32* size, wifi_radio_combination_matrix *radio_combination_matrix) +{ + int numIfaceHandles = 0; + wifi_interface_handle *ifaceHandles = NULL; + wifi_interface_handle wlan0Handle; + + wlan0Handle = wifi_get_wlan_interface((wifi_handle)handle, ifaceHandles, numIfaceHandles); + GetRadioComboCommand *cmd = new GetRadioComboCommand(wlan0Handle, max_size, + size, radio_combination_matrix); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + if (result == WIFI_SUCCESS) { + ALOGD("Get radio combo matrix success"); + } else { + ALOGE("Get radio combo matrix failed\n"); + } + cmd->releaseRef(); + return result; +} + +wifi_error wifi_set_scanning_mac_oui(wifi_interface_handle handle, oui scan_oui) +{ + SetPnoMacAddrOuiCommand command(handle, scan_oui); + return (wifi_error)command.start(); + +} + +wifi_error wifi_set_nodfs_flag(wifi_interface_handle handle, u32 nodfs) +{ + SetNodfsCommand command(handle, nodfs); + return (wifi_error) command.requestResponse(); +} + +wifi_error wifi_set_country_code(wifi_interface_handle handle, const char *country_code) +{ + SetCountryCodeCommand command(handle, country_code); + return (wifi_error) command.requestResponse(); +} + +static wifi_error wifi_start_rssi_monitoring(wifi_request_id id, wifi_interface_handle + iface, s8 max_rssi, s8 min_rssi, wifi_rssi_event_handler eh) +{ + ALOGI("Starting RSSI monitor %d", id); + wifi_handle handle = getWifiHandle(iface); + SetRSSIMonitorCommand *cmd = new SetRSSIMonitorCommand(id, iface, max_rssi, min_rssi, eh); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + ALOGI("wifi_register_cmd() is failed %d", id); + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + ALOGI("start() is failed %d", id); + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + +static wifi_error wifi_stop_rssi_monitoring(wifi_request_id id, wifi_interface_handle iface) +{ + ALOGI("Stopping RSSI monitor %d", id); + + if(id == -1) { + wifi_rssi_event_handler handler; + s8 max_rssi = 0, min_rssi = 0; + memset(&handler, 0, sizeof(handler)); + SetRSSIMonitorCommand *cmd = new SetRSSIMonitorCommand(id, iface, + max_rssi, min_rssi, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + return wifi_cancel_cmd(id, iface); +} + +static wifi_error wifi_get_packet_filter_capabilities(wifi_interface_handle handle, + u32 *version, u32 *max_len) +{ + ALOGD("Getting APF capabilities, halHandle = %p\n", handle); + AndroidPktFilterCommand *cmd = new AndroidPktFilterCommand(handle, version, max_len); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + if (result == WIFI_SUCCESS) { + ALOGD("Getting APF capability, version = %d, max_len = %d\n", *version, *max_len); + } + cmd->releaseRef(); + return result; +} + +static wifi_error wifi_set_packet_filter(wifi_interface_handle handle, + const u8 *program, u32 len) +{ + ALOGD("Setting APF program, halHandle = %p\n", handle); + AndroidPktFilterCommand *cmd = new AndroidPktFilterCommand(handle, program, len); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; +} + +static wifi_error wifi_read_packet_filter(wifi_interface_handle handle, + u32 src_offset, u8 *host_dst, u32 length) +{ + ALOGD("Read APF program, halHandle = %p, length = %d\n", handle, length); + AndroidPktFilterCommand *cmd = new AndroidPktFilterCommand(handle, host_dst, length); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + if (result == WIFI_SUCCESS) { + ALOGI("Read APF program success\n"); + } + cmd->releaseRef(); + return result; +} +static wifi_error wifi_configure_nd_offload(wifi_interface_handle handle, u8 enable) +{ + SetNdoffloadCommand command(handle, enable); + return (wifi_error) command.requestResponse(); +} + +wifi_error wifi_set_indoor_state(wifi_handle handle, bool isIndoor) +{ + ALOGD("Setting Wifi Indoor state, halHandle = %p isIndoor = %d\n", handle, isIndoor); + int numIfaceHandles = 0; + wifi_interface_handle *ifaceHandles = NULL; + wifi_interface_handle wlan0Handle; + + wlan0Handle = wifi_get_wlan_interface((wifi_handle)handle, ifaceHandles, numIfaceHandles); + + SetIndoorCommand command(wlan0Handle, isIndoor); + return (wifi_error) command.requestResponse(); +} + +wifi_error wifi_set_latency_mode(wifi_interface_handle handle, wifi_latency_mode mode) +{ + ALOGD("Setting Wifi Latency mode, halHandle = %p LatencyMode = %d\n", handle, mode); + SetLatencyModeCommand command(handle, mode); + return (wifi_error) command.requestResponse(); +} + +///////////////////////////////////////////////////////////////////////// +class TxPowerScenario : public WifiCommand { + wifi_power_scenario mScenario; +public: + // constructor for tx power scenario setting + TxPowerScenario(wifi_interface_handle handle, wifi_power_scenario scenario) + : WifiCommand("TxPowerScenario", handle, 0), mScenario(scenario) + { + mScenario = scenario; + } + + // constructor for tx power scenario resetting + TxPowerScenario(wifi_interface_handle handle) + : WifiCommand("TxPowerScenario", handle, 0) + { + mScenario = WIFI_POWER_SCENARIO_DEFAULT; + } + + int createRequest(WifiRequest& request, int subcmd, wifi_power_scenario mScenario) { + int result = request.create(GOOGLE_OUI, subcmd); + if (result < 0) { + return result; + } + + if ((mScenario <= WIFI_POWER_SCENARIO_INVALID) || + (mScenario >= SAR_CONFIG_SCENARIO_COUNT)) { + ALOGE("Unsupported tx power value:%d\n", mScenario); + return WIFI_ERROR_NOT_SUPPORTED; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_s8(ANDR_WIFI_ATTRIBUTE_TX_POWER_SCENARIO, mScenario); + if (result < 0) { + ALOGE("Failed to put tx power scenario request; result = %d", result); + return result; + } + request.attr_end(data); + return WIFI_SUCCESS; + } + + int start(wifi_power_scenario mScenario) { + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request, WIFI_SUBCMD_TX_POWER_SCENARIO, mScenario); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create request; result = %d", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to send tx power scenario; result = %d", result); + } + return result; + } +protected: + virtual int handleResponse(WifiEvent& reply) { + ALOGD("Request complete!"); + /* Nothing to do on response! */ + return NL_SKIP; + } +}; + + +wifi_error wifi_select_tx_power_scenario(wifi_interface_handle handle, wifi_power_scenario scenario) +{ + ALOGE("wifi_select_tx_power_scenario"); + TxPowerScenario command(handle); + return (wifi_error)command.start(scenario); +} + +wifi_error wifi_reset_tx_power_scenario(wifi_interface_handle handle) +{ + wifi_power_scenario scenario = WIFI_POWER_SCENARIO_DEFAULT; + ALOGE("wifi_reset_tx_power_scenario"); + TxPowerScenario command(handle); + return (wifi_error)command.start(scenario); +} + +///////////////////////////////////////////////////////////////////////////// + +class ThermalMitigation : public WifiCommand { +private: + wifi_thermal_mode mMitigation; + u32 mCompletionWindow; +public: + // constructor for thermal mitigation setting + ThermalMitigation(wifi_interface_handle handle, + wifi_thermal_mode mitigation, u32 completion_window) + : WifiCommand("ThermalMitigation", handle, 0) + { + mMitigation = mitigation; + mCompletionWindow = completion_window; + } + + int createRequest(WifiRequest& request, int subcmd, + wifi_thermal_mode mitigation, u32 completion_window) { + int result = request.create(GOOGLE_OUI, subcmd); + if (result < 0) { + return result; + } + + if ((mitigation < WIFI_MITIGATION_NONE) || + (mitigation > WIFI_MITIGATION_EMERGENCY)) { + ALOGE("Unsupported tx mitigation value:%d\n", mitigation); + return WIFI_ERROR_NOT_SUPPORTED; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_s8(ANDR_WIFI_ATTRIBUTE_THERMAL_MITIGATION, mitigation); + if (result < 0) { + ALOGE("Failed to put tx power scenario request; result = %d", result); + return result; + } + result = request.put_u32(ANDR_WIFI_ATTRIBUTE_THERMAL_COMPLETION_WINDOW, + completion_window); + if (result < 0) { + ALOGE("Failed to put tx power scenario request; result = %d", result); + return result; + } + + request.attr_end(data); + return WIFI_SUCCESS; + } + + int start() { + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request, WIFI_SUBCMD_THERMAL_MITIGATION, mMitigation, + mCompletionWindow); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create request; result = %d", result); + return result; + } + + ALOGD("try to get resp; mitigation=%d, delay=%d", mMitigation, mCompletionWindow); + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to send thermal mitigation; result = %d", result); + } + return result; + } +protected: + virtual int handleResponse(WifiEvent& reply) { + ALOGD("Request complete!"); + /* Nothing to do on response! */ + return NL_SKIP; + } +}; + +wifi_error wifi_set_thermal_mitigation_mode(wifi_handle handle, + wifi_thermal_mode mode, + u32 completion_window) +{ + int numIfaceHandles = 0; + wifi_interface_handle *ifaceHandles = NULL; + wifi_interface_handle wlan0Handle; + + wlan0Handle = wifi_get_wlan_interface((wifi_handle)handle, ifaceHandles, numIfaceHandles); + ThermalMitigation command(wlan0Handle, mode, completion_window); + return (wifi_error)command.start(); +} + +///////////////////////////////////////////////////////////////////////////// + +class ChAvoidCommand : public WifiCommand { + private: + u32 mNumParams; + wifi_coex_unsafe_channel *chavoidParams; + u32 mMandatory; + + public: + ChAvoidCommand(wifi_interface_handle handle, + u32 num, wifi_coex_unsafe_channel channels[], u32 mandatory) + : WifiCommand("ChAvoidCommand", handle, 0), + mNumParams(num), chavoidParams(channels), mMandatory(mandatory) + { + } + + int createRequest(WifiRequest& request) { + return createSetChAvoidRequest(request); + } + + int createSetChAvoidRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, CHAVOID_SUBCMD_SET_CONFIG); + if (result < 0) { + ALOGE("%s : Failed to create SUBCMD\n", __func__); + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(CHAVOID_ATTRIBUTE_CNT, mNumParams); + if (result < 0) { + ALOGE("%s : Failed to set cound\n", __func__); + return result; + } + result = request.put_u32(CHAVOID_ATTRIBUTE_MANDATORY, mMandatory); + if (result < 0) { + ALOGE("%s : Failed to set mandatory cap\n", __func__); + return result; + } + + nlattr *chavoid_config = request.attr_start(CHAVOID_ATTRIBUTE_CONFIG); + for (int i = 0; i< mNumParams; i++) { + nlattr *item = request.attr_start(i); + if (item == NULL) { + ALOGE("%s : Failed to alloc item\n", __func__); + return WIFI_ERROR_OUT_OF_MEMORY; + } + result = request.put_u32(CHAVOID_ATTRIBUTE_BAND, chavoidParams[i].band); + if (result < 0) { + ALOGE("%s : Failed to set band\n", __func__); + return result; + } + result = request.put_u32(CHAVOID_ATTRIBUTE_CHANNEL, chavoidParams[i].channel); + if (result < 0) { + ALOGE("%s : Failed to set channel\n", __func__); + return result; + } + result = request.put_u32(CHAVOID_ATTRIBUTE_PWRCAP, chavoidParams[i].power_cap_dbm); + if (result < 0) { + ALOGE("%s : Failed to set power cap\n", __func__); + return result; + } + request.attr_end(item); + } + request.attr_end(chavoid_config); + request.attr_end(data); + return WIFI_SUCCESS; + } + + int start() { + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request); + if (result < 0) { + return result; + } + result = requestResponse(request); + if (result < 0) { + ALOGI("Request Response failed for ChAvoid, result = %d", result); + return result; + } + return result; + } + + int handleResponse(WifiEvent& reply) { + ALOGD("In ChAvoidCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in ChAvoidCommand response; ignoring it"); + return NL_SKIP; + } + ALOGD("Response received for ChAvoid command\n"); + return NL_OK; + } + + int handleEvent(WifiEvent& event) { + /* No Event to receive for ChAvoid commands */ + ALOGD("ChAvoid command %s\n", __FUNCTION__); + return NL_SKIP; + } +}; + +wifi_error wifi_set_coex_unsafe_channels(wifi_handle handle, + u32 num, wifi_coex_unsafe_channel channels[], u32 mandatory) +{ + int numIfaceHandles = 0; + wifi_interface_handle *ifaceHandles = NULL; + wifi_interface_handle wlan0Handle; + + wlan0Handle = wifi_get_wlan_interface((wifi_handle)handle, ifaceHandles, numIfaceHandles); + + ChAvoidCommand *cmd = new ChAvoidCommand(wlan0Handle, num, channels, mandatory); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + if (result == WIFI_SUCCESS) { + ALOGI("Setting Channel Avoidance success\n"); + } else { + ALOGE("Setting Channel Avoidance failed\n"); + } + cmd->releaseRef(); + return result; +} + +///////////////////////////////////////////////////////////////////////////// + +class DscpCommand : public WifiCommand { + private: + u32 mStart; + u32 mEnd; + u32 mAc; + int mReqType; + public: + DscpCommand(wifi_interface_handle handle, + u32 start, u32 end, u32 ac) + : WifiCommand("DscpCommand", handle, 0), + mStart(start), mEnd(end), mAc(ac), + mReqType(SET_DSCP_TABLE) + { + } + + DscpCommand(wifi_interface_handle handle) + : WifiCommand("DscpCommand", handle, 0), + mReqType(RESET_DSCP_TABLE) + { + } + + int createRequest(WifiRequest& request) { + if (mReqType == SET_DSCP_TABLE) { + ALOGI("\n%s: DSCP set table request\n", __FUNCTION__); + return createSetDscpRequest(request); + } else if (mReqType == RESET_DSCP_TABLE) { + ALOGI("\n%s: DSCP reset table request\n", __FUNCTION__); + return createResetDscpRequest(request); + } else { + ALOGE("\n%s Unknown DSCP request\n", __FUNCTION__); + return WIFI_ERROR_NOT_SUPPORTED; + } + return WIFI_SUCCESS; + } + + int createSetDscpRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, DSCP_SUBCMD_SET_TABLE); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(DSCP_ATTRIBUTE_START, mStart); + if (result < 0) { + goto exit; + } + result = request.put_u32(DSCP_ATTRIBUTE_END, mEnd); + if (result < 0) { + goto exit; + } + result = request.put_u32(DSCP_ATTRIBUTE_AC, mAc); + if (result < 0) { + goto exit; + } + request.attr_end(data); +exit: + return result; + } + + int createResetDscpRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, DSCP_SUBCMD_RESET_TABLE); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + request.attr_end(data); + return result; + } + + int start() { + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request); + if (result < 0) { + return result; + } + result = requestResponse(request); + if (result < 0) { + ALOGI("Request Response failed for DSCP, result = %d", result); + return result; + } + return result; + } + + int handleResponse(WifiEvent& reply) { + ALOGD("In DscpCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in DscpCommand response; ignoring it"); + return NL_SKIP; + } + if( mReqType == SET_DSCP_TABLE) { + ALOGD("Response received for Set DSCP command\n"); + } else if (mReqType == RESET_DSCP_TABLE) { + ALOGD("Response received for Reset DSCP command\n"); + } + return NL_OK; + } + + int handleEvent(WifiEvent& event) { + /* No Event to receive for DSCP commands */ + ALOGD("DSCP command %s\n", __FUNCTION__); + return NL_SKIP; + } +}; + +wifi_error wifi_map_dscp_access_category(wifi_handle handle, + u32 start, u32 end, u32 ac) +{ + int numIfaceHandles = 0; + wifi_interface_handle *ifaceHandles = NULL; + wifi_interface_handle wlan0Handle; + + wlan0Handle = wifi_get_wlan_interface((wifi_handle)handle, ifaceHandles, numIfaceHandles); + + DscpCommand *cmd = new DscpCommand(wlan0Handle, start, end, ac); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + if (result == WIFI_SUCCESS) { + ALOGI("Mapping DSCP table success\n"); + } else { + ALOGE("Mapping DSCP table fail\n"); + } + cmd->releaseRef(); + return result; +} + +wifi_error wifi_reset_dscp_mapping(wifi_handle handle) +{ + int numIfaceHandles = 0; + wifi_interface_handle *ifaceHandles = NULL; + wifi_interface_handle wlan0Handle; + + wlan0Handle = wifi_get_wlan_interface((wifi_handle)handle, ifaceHandles, numIfaceHandles); + + DscpCommand *cmd = new DscpCommand(wlan0Handle); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + if (result == WIFI_SUCCESS) { + ALOGI("Resetting DSCP table success\n"); + } else { + ALOGE("Resetting DSCP table fail\n"); + } + cmd->releaseRef(); + return result; +} + +class VirtualIfaceConfig : public WifiCommand { + const char *mIfname; + nl80211_iftype mType; + u32 mwlan0_id; + +public: + VirtualIfaceConfig(wifi_interface_handle handle, const char* ifname, + nl80211_iftype iface_type, u32 wlan0_id) + : WifiCommand("VirtualIfaceConfig", handle, 0), mIfname(ifname), mType(iface_type), + mwlan0_id(wlan0_id) + { + mIfname = ifname; + mType = iface_type; + mwlan0_id = wlan0_id; + } + int createRequest(WifiRequest& request, const char* ifname, + nl80211_iftype iface_type, u32 wlan0_id) { + ALOGD("add ifname = %s, iface_type = %d, wlan0_id = %d", + ifname, iface_type, wlan0_id); + + int result = request.create(NL80211_CMD_NEW_INTERFACE); + if (result < 0) { + ALOGE("failed to create NL80211_CMD_NEW_INTERFACE; result = %d", result); + return result; + } + + result = request.put_u32(NL80211_ATTR_IFINDEX, wlan0_id); + if (result < 0) { + ALOGE("failed to put NL80211_ATTR_IFINDEX; result = %d", result); + return result; + } + + result = request.put_string(NL80211_ATTR_IFNAME, ifname); + if (result < 0) { + ALOGE("failed to put NL80211_ATTR_IFNAME = %s; result = %d", ifname, result); + return result; + } + + result = request.put_u32(NL80211_ATTR_IFTYPE, iface_type); + if (result < 0) { + ALOGE("failed to put NL80211_ATTR_IFTYPE = %d; result = %d", iface_type, result); + return result; + } + return WIFI_SUCCESS; + } + + int deleteRequest(WifiRequest& request, const char* ifname) { + ALOGD("delete ifname = %s\n", ifname); + int result = request.create(NL80211_CMD_DEL_INTERFACE); + if (result < 0) { + ALOGE("failed to create NL80211_CMD_DEL_INTERFACE; result = %d", result); + return result; + } + result = request.put_u32(NL80211_ATTR_IFINDEX, if_nametoindex(ifname)); + if (result < 0) { + ALOGE("failed to put NL80211_ATTR_IFINDEX = %d; result = %d", + if_nametoindex(ifname), result); + return result; + } + result = request.put_string(NL80211_ATTR_IFNAME, ifname); + if (result < 0) { + ALOGE("failed to put NL80211_ATTR_IFNAME = %s; result = %d", ifname, result); + return result; + } + return WIFI_SUCCESS; + } + + int createIface() { + ALOGE("Creating virtual interface"); + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request, mIfname, mType, mwlan0_id); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create virtual iface request; result = %d\n", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to get the virtual iface create response; result = %d\n", result); + return result; + } + ALOGE("Created virtual interface"); + return WIFI_SUCCESS; + } + + int deleteIface() { + ALOGD("Deleting virtual interface"); + WifiRequest request(familyId(), ifaceId()); + int result = deleteRequest(request, mIfname); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create virtual iface delete request; result = %d\n", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to get response of delete virtual interface; result = %d\n", result); + return result; + } + return WIFI_SUCCESS; + } +protected: + virtual int handleResponse(WifiEvent& reply) { + ALOGD("Request complete!"); + /* Nothing to do on response! */ + return NL_SKIP; + } +}; + +static std::vector<std::string> added_ifaces; + +static void wifi_cleanup_dynamic_ifaces(wifi_handle handle) +{ + int len = added_ifaces.size(); + while (len--) { + wifi_virtual_interface_delete(handle, added_ifaces.front().c_str()); + } + added_ifaces.clear(); +} + +wifi_error wifi_virtual_interface_create(wifi_handle handle, const char* ifname, + wifi_interface_type iface_type) +{ + int numIfaceHandles = 0; + wifi_error ret = WIFI_SUCCESS; + wifi_interface_handle *ifaceHandles = NULL; + wifi_interface_handle wlan0Handle; + nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; + u32 wlan0_id = if_nametoindex("wlan0"); + + if (!handle || !wlan0_id) { + ALOGE("%s: Error wifi_handle NULL or wlan0 not present\n", __FUNCTION__); + return WIFI_ERROR_UNKNOWN; + } + + /* Do not create interface if already exist. */ + if (if_nametoindex(ifname)) { + ALOGD("%s: if_nametoindex(%s) = %d already exists, skip create \n", + __FUNCTION__, ifname, if_nametoindex(ifname)); + return WIFI_SUCCESS; + } + + ALOGD("%s: ifname name = %s, type = %s\n", __FUNCTION__, ifname, + IfaceTypeToString(iface_type)); + + switch (iface_type) { + case WIFI_INTERFACE_TYPE_STA: + type = NL80211_IFTYPE_STATION; + break; + case WIFI_INTERFACE_TYPE_AP: + type = NL80211_IFTYPE_AP; + break; + case WIFI_INTERFACE_TYPE_P2P: + type = NL80211_IFTYPE_P2P_DEVICE; + break; + case WIFI_INTERFACE_TYPE_NAN: + type = NL80211_IFTYPE_NAN; + break; + default: + ALOGE("%s: Wrong interface type %u\n", __FUNCTION__, iface_type); + return WIFI_ERROR_UNKNOWN; + } + + wlan0Handle = wifi_get_wlan_interface((wifi_handle)handle, ifaceHandles, numIfaceHandles); + VirtualIfaceConfig command(wlan0Handle, ifname, type, wlan0_id); + + ret = (wifi_error)command.createIface(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s: Iface add Error:%d", __FUNCTION__,ret); + return ret; + } + /* Update dynamic interface list */ + added_ifaces.push_back(std::string(ifname)); + ret = wifi_add_iface_hal_info((wifi_handle)handle, ifname); + return ret; +} + +wifi_error wifi_virtual_interface_delete(wifi_handle handle, const char* ifname) +{ + int numIfaceHandles = 0; + int i = 0; + wifi_error ret = WIFI_SUCCESS; + wifi_interface_handle *ifaceHandles = NULL; + wifi_interface_handle wlan0Handle; + hal_info *info = (hal_info *)handle; + u32 wlan0_id = if_nametoindex("wlan0"); + + if (!handle || !wlan0_id) { + ALOGE("%s: Error wifi_handle NULL or wlan0 not present\n", __FUNCTION__); + return WIFI_ERROR_UNKNOWN; + } + + while (i < info->max_num_interfaces) { + if (info->interfaces[i] != NULL && + strncmp(info->interfaces[i]->name, + ifname, sizeof(info->interfaces[i]->name)) == 0) { + if (info->interfaces[i]->is_virtual == false) { + ALOGI("%s: %s is static iface, skip delete\n", + __FUNCTION__, ifname); + return WIFI_SUCCESS; + } + } + i++; + } + + ALOGD("%s: iface name=%s\n", __FUNCTION__, ifname); + wlan0Handle = wifi_get_wlan_interface((wifi_handle)handle, ifaceHandles, numIfaceHandles); + VirtualIfaceConfig command(wlan0Handle, ifname, (nl80211_iftype)0, 0); + ret = (wifi_error)command.deleteIface(); + if (ret != WIFI_SUCCESS) { + ALOGE("%s: Iface delete Error:%d", __FUNCTION__,ret); + return ret; + } + /* Update dynamic interface list */ + added_ifaces.erase(std::remove(added_ifaces.begin(), added_ifaces.end(), std::string(ifname)), + added_ifaces.end()); + ret = wifi_clear_iface_hal_info((wifi_handle)handle, ifname); + return ret; +} +///////////////////////////////////////////////////////////////////////////// + +class MultiStaConfig : public WifiCommand { + wifi_multi_sta_use_case mUseCase; + int mReqType; + +public: + MultiStaConfig(wifi_interface_handle handle) + : WifiCommand("MultiStaConfig", handle, 0), mReqType(SET_PRIMARY_CONNECTION) + { + } + MultiStaConfig(wifi_interface_handle handle, wifi_multi_sta_use_case use_case) + : WifiCommand("MultiStaConfig", handle, 0), mUseCase(use_case), mReqType(SET_USE_CASE) + { + mUseCase = use_case; + } + + int createRequest(WifiRequest& request) { + if (mReqType == SET_PRIMARY_CONNECTION) { + ALOGI("\n%s: MultiSta set primary connection\n", __FUNCTION__); + return createSetPrimaryConnectionRequest(request); + } else if (mReqType == SET_USE_CASE) { + ALOGI("\n%s: MultiSta set use case\n", __FUNCTION__); + return createSetUsecaseRequest(request); + } else { + ALOGE("\n%s Unknown MultiSta request\n", __FUNCTION__); + return WIFI_ERROR_NOT_SUPPORTED; + } + return WIFI_SUCCESS; + } + + int createSetPrimaryConnectionRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, WIFI_SUBCMD_SET_MULTISTA_PRIMARY_CONNECTION); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + request.attr_end(data); + + return result; + } + + int createSetUsecaseRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, WIFI_SUBCMD_SET_MULTISTA_USE_CASE); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(MULTISTA_ATTRIBUTE_USE_CASE, mUseCase); + if (result < 0) { + ALOGE("failed to put MULTISTA_ATTRIBUTE_USE_CASE = %d; result = %d", mUseCase, result); + goto exit; + } + +exit: request.attr_end(data); + return result; + } + + int start() { + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request); + if (result < 0) { + return result; + } + result = requestResponse(request); + if (result < 0) { + ALOGI("Request Response failed for MultiSta, result = %d", result); + return result; + } + ALOGI("Done!"); + return result; + } +protected: + virtual int handleResponse(WifiEvent& reply) { + ALOGD("Request complete!"); + /* Nothing to do on response! */ + return NL_SKIP; + } +}; + +wifi_error wifi_multi_sta_set_primary_connection(wifi_handle handle, wifi_interface_handle iface) +{ + wifi_error ret = WIFI_SUCCESS; + char buf[IFNAMSIZ]; + + if (!handle || !iface) { + ALOGE("%s: Error wifi_handle NULL or invalid wifi interface handle\n", __FUNCTION__); + return WIFI_ERROR_UNKNOWN; + } + + if (wifi_get_iface_name(iface, buf, sizeof(buf)) != WIFI_SUCCESS) { + ALOGE("%s : Invalid interface handle\n", __func__); + return WIFI_ERROR_INVALID_ARGS; + } + + ALOGD("Setting Multista primary connection for iface = %s\n", buf); + + MultiStaConfig *cmd = new MultiStaConfig(iface); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + ret = (wifi_error)cmd->start(); + cmd->releaseRef(); + return ret; +} + +wifi_error wifi_multi_sta_set_use_case(wifi_handle handle, wifi_multi_sta_use_case use_case) +{ + int numIfaceHandles = 0; + wifi_error ret = WIFI_SUCCESS; + wifi_interface_handle *ifaceHandles = NULL; + wifi_interface_handle wlan0Handle; + + if (!handle) { + ALOGE("%s: Error wifi_handle NULL\n", __FUNCTION__); + return WIFI_ERROR_UNKNOWN; + } + + if (!(use_case >= WIFI_DUAL_STA_TRANSIENT_PREFER_PRIMARY && + use_case <= WIFI_DUAL_STA_NON_TRANSIENT_UNBIASED)) { + ALOGE("%s: Invalid multi_sta usecase %d\n", __FUNCTION__, use_case); + return WIFI_ERROR_INVALID_ARGS; + } + + wlan0Handle = wifi_get_wlan_interface((wifi_handle)handle, ifaceHandles, numIfaceHandles); + ALOGD("Setting Multista usecase = %d\n", use_case); + MultiStaConfig *cmd = new MultiStaConfig(wlan0Handle, use_case); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + ret = (wifi_error)cmd->start(); + cmd->releaseRef(); + return ret; +} +///////////////////////////////////////////////////////////////////////////////////////////////// + +class SetVoipModeCommand : public WifiCommand { + +private: + wifi_voip_mode mMode; +public: + SetVoipModeCommand(wifi_interface_handle handle, wifi_voip_mode mode) + : WifiCommand("SetVoipModeCommand", handle, 0) { + mMode = mode; + } + virtual int create() { + int ret; + + ret = mMsg.create(GOOGLE_OUI, WIFI_SUBCMD_CONFIG_VOIP_MODE); + if (ret < 0) { + ALOGE("Can't create message to send to driver - %d", ret); + return ret; + } + + nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); + ret = mMsg.put_u32(ANDR_WIFI_ATTRIBUTE_VOIP_MODE, mMode); + ALOGE("mMode - %d", mMode); + if (ret < 0) { + ALOGE("Failed to set voip mode %d\n", mMode); + return ret; + } + + ALOGI("Successfully configured voip mode %d\n", mMode); + mMsg.attr_end(data); + return WIFI_SUCCESS; + } +}; + +wifi_error wifi_set_voip_mode(wifi_interface_handle handle, wifi_voip_mode mode) +{ + ALOGD("Setting VOIP mode, halHandle = %p Mode = %d\n", handle, mode); + SetVoipModeCommand command(handle, mode); + return (wifi_error) command.requestResponse(); +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +class SetDtimConfigCommand : public WifiCommand { + +private: + uint32_t multiplier; +public: + SetDtimConfigCommand(wifi_interface_handle handle, u32 dtim_multiplier) + : WifiCommand("SetDtimConfigCommand", handle, 0) { + multiplier = dtim_multiplier; + } + virtual int create() { + int ret; + + ret = mMsg.create(GOOGLE_OUI, WIFI_SUBCMD_SET_DTIM_CONFIG); + if (ret < 0) { + ALOGE("Can't create message to send to driver - %d", ret); + return ret; + } + + nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); + ret = mMsg.put_u32(ANDR_WIFI_ATTRIBUTE_DTIM_MULTIPLIER, multiplier); + if (ret < 0) { + ALOGE("Failed to set dtim multiplier %d\n", multiplier); + return ret; + } + + ALOGI("Successfully configured dtim multiplier %d\n", multiplier); + mMsg.attr_end(data); + return WIFI_SUCCESS; + } +}; + +wifi_error wifi_set_dtim_config(wifi_interface_handle handle, u32 multiplier) +{ + ALOGD("Setting DTIM config , halHandle = %p Multiplier = %d\n", handle, multiplier); + SetDtimConfigCommand command(handle, multiplier); + return (wifi_error) command.requestResponse(); +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +class UsableChannelCommand : public WifiCommand { + +private: + u32 mBandMask; + u32 mIfaceModeMask; + u32 mFilterMask; + u32 mMaxSize; + u32* mSize; + wifi_usable_channel* mChannels; + +public: + UsableChannelCommand(wifi_interface_handle handle, u32 band_mask, u32 iface_mode_mask, + u32 filter_mask, u32 max_size, u32* size, wifi_usable_channel* channels) + : WifiCommand("UsableChannelCommand", handle, 0), + mBandMask(band_mask), mIfaceModeMask(iface_mode_mask), + mFilterMask(filter_mask), mMaxSize(max_size), + mSize(size), mChannels(channels) + { + } + + int createRequest(WifiRequest& request) { + createUsableChannelRequest(request); + return WIFI_SUCCESS; + } + + int createUsableChannelRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, WIFI_SUBCMD_USABLE_CHANNEL); + if (result < 0) { + ALOGE("Failed to create UsableChannel request; result = %d", result); + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + result = request.put_u32(USABLECHAN_ATTRIBUTE_BAND, mBandMask); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put log level; result = %d", result); + return result; + } + result = request.put_u32(USABLECHAN_ATTRIBUTE_IFACE, mIfaceModeMask); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put ring flags; result = %d", result); + return result; + } + result = request.put_u32(USABLECHAN_ATTRIBUTE_FILTER, mFilterMask); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put usablechan filter; result = %d", result); + return result; + } + result = request.put_u32(USABLECHAN_ATTRIBUTE_MAX_SIZE, mMaxSize); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put usablechan max_size; result = %d", result); + return result; + } + request.attr_end(data); + + return WIFI_SUCCESS; + } + + int start() { + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create request; result = %d", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to set scanning mac OUI; result = %d", result); + } + + return result; + } + + virtual int handleResponse(WifiEvent& reply) { + ALOGD("In DebugCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + wifi_usable_channel *channels(mChannels); + + if (vendor_data == NULL || len == 0) { + ALOGE("No Debug data found"); + return NL_SKIP; + } + + nl_iterator it(vendor_data); + if (it.get_type() == USABLECHAN_ATTRIBUTE_SIZE) { + *mSize = it.get_u32(); + } else { + ALOGE("Unknown attribute: %d expecting %d", + it.get_type(), USABLECHAN_ATTRIBUTE_SIZE); + return NL_SKIP; + } + + it.next(); + if (it.get_type() == USABLECHAN_ATTRIBUTE_CHANNELS) { + memcpy(channels, it.get_data(), sizeof(wifi_usable_channel) * *mSize); + } else { + ALOGE("Unknown attribute: %d expecting %d", + it.get_type(), USABLECHAN_ATTRIBUTE_SIZE); + return NL_SKIP; + } + + return NL_OK; + } + + virtual int handleEvent(WifiEvent& event) { + /* NO events! */ + return NL_SKIP; + } +}; + +wifi_error wifi_get_usable_channels(wifi_handle handle, u32 band_mask, u32 iface_mode_mask, + u32 filter_mask, u32 max_size, u32* size, wifi_usable_channel* channels) +{ + int numIfaceHandles = 0; + wifi_interface_handle *ifaceHandles = NULL; + wifi_interface_handle wlan0Handle; + + wlan0Handle = wifi_get_wlan_interface((wifi_handle)handle, ifaceHandles, numIfaceHandles); + UsableChannelCommand command(wlan0Handle, band_mask, iface_mode_mask, + filter_mask, max_size, size, channels); + return (wifi_error)command.start(); +} diff --git a/synadhd/wifi_hal/wifi_logger.cpp b/synadhd/wifi_hal/wifi_logger.cpp new file mode 100755 index 0000000..0338270 --- /dev/null +++ b/synadhd/wifi_hal/wifi_logger.cpp @@ -0,0 +1,2831 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * 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 <stdint.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/errqueue.h> +#include <errno.h> + +#include <linux/pkt_sched.h> +#include <netlink/object-api.h> +#include <netlink/netlink.h> +#include <netlink/socket.h> +#include <netlink-private/object-api.h> +#include <netlink-private/types.h> +#include <unistd.h> +#include <cutils/properties.h> + + +#include "nl80211_copy.h" +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include <log/log.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" +#include <sys/stat.h> +#include "syna_version.h" +#define WIFI_HAL_EVENT_SOCK_PORT 645 + +#define ARRAYSIZE(a) (u8)(sizeof(a) / sizeof(a[0])) +typedef enum { + LOGGER_START_LOGGING = ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START, + LOGGER_TRIGGER_MEM_DUMP, + LOGGER_GET_MEM_DUMP, + LOGGER_GET_VER, + LOGGER_GET_RING_STATUS, + LOGGER_GET_RING_DATA, + LOGGER_GET_FEATURE, + LOGGER_RESET_LOGGING, + LOGGER_TRIGGER_DRIVER_MEM_DUMP, + LOGGER_GET_DRIVER_MEM_DUMP, + LOGGER_START_PKT_FATE_MONITORING, + LOGGER_GET_TX_PKT_FATES, + LOGGER_GET_RX_PKT_FATES, + LOGGER_GET_WAKE_REASON_STATS, + LOGGER_DEBUG_GET_DUMP, + LOGGER_FILE_DUMP_DONE_IND, + LOGGER_SET_HAL_START, + LOGGER_HAL_STOP, + LOGGER_SET_HAL_PID, + LOGGER_SET_TPUT_DEBUG_DUMP_CMD, + LOGGER_GET_BUF_RING_MAP +} DEBUG_SUB_COMMAND; + +#define MAX_NV_FILE 4 +#define MAX_SKU_NAME_LEN 5 +#define OTA_PATH "/data/vendor/firmware/wifi/" +#define OTA_CLM_FILE "bcmdhd_clm.blob" +#define OTA_NVRAM_FILE "bcmdhd.cal" +#define HW_DEV_PROP "ro.revision" +#define HW_SKU_PROP "ro.boot.hardware.sku" + +typedef enum { + NVRAM, + CLM_BLOB +} OTA_TYPE; + +char ota_nvram_ext[10]; +typedef struct ota_info_buf { + u32 ota_clm_len; + const void *ota_clm_buf[1]; + u32 ota_nvram_len; + const void *ota_nvram_buf[1]; +} ota_info_buf_t; +u32 applied_ota_version = 0; + +typedef enum { + LOGGER_ATTRIBUTE_INVALID = 0, + LOGGER_ATTRIBUTE_DRIVER_VER = 1, + LOGGER_ATTRIBUTE_FW_VER = 2, + LOGGER_ATTRIBUTE_RING_ID = 3, + LOGGER_ATTRIBUTE_RING_NAME = 4, + LOGGER_ATTRIBUTE_RING_FLAGS = 5, + LOGGER_ATTRIBUTE_LOG_LEVEL = 6, + LOGGER_ATTRIBUTE_LOG_TIME_INTVAL = 7, + LOGGER_ATTRIBUTE_LOG_MIN_DATA_SIZE = 8, + LOGGER_ATTRIBUTE_FW_DUMP_LEN = 9, + LOGGER_ATTRIBUTE_FW_DUMP_DATA = 10, + LOGGER_ATTRIBUTE_FW_ERR_CODE = 11, + LOGGER_ATTRIBUTE_RING_DATA = 12, + LOGGER_ATTRIBUTE_RING_STATUS = 13, + LOGGER_ATTRIBUTE_RING_NUM = 14, + LOGGER_ATTRIBUTE_DRIVER_DUMP_LEN = 15, + LOGGER_ATTRIBUTE_DRIVER_DUMP_DATA = 16, + LOGGER_ATTRIBUTE_PKT_FATE_NUM = 17, + LOGGER_ATTRIBUTE_PKT_FATE_DATA = 18, + LOGGER_ATTRIBUTE_HANG_REASON = 19, + LOGGER_ATTRIBUTE_BUF_RING_NUM = 20, + LOGGER_ATTRIBUTE_BUF_RING_MAP = 21, + /* Add new attributes just above this */ + LOGGER_ATTRIBUTE_MAX +} LOGGER_ATTRIBUTE; + +typedef enum { + DEBUG_OFF = 0, + DEBUG_NORMAL, + DEBUG_VERBOSE, + DEBUG_VERY, + DEBUG_VERY_VERY, +} LOGGER_LEVEL; + +typedef enum { + GET_FW_VER, + GET_DRV_VER, + GET_RING_DATA, + GET_RING_STATUS, + GET_FEATURE, + START_RING_LOG, + GET_BUF_RING_MAP, +} GetCmdType; + +typedef enum { + PACKET_MONITOR_START, + TX_PACKET_FATE, + RX_PACKET_FATE, +} PktFateReqType; + +enum wake_stat_attributes { + WAKE_STAT_ATTRIBUTE_INVALID, + WAKE_STAT_ATTRIBUTE_TOTAL, + WAKE_STAT_ATTRIBUTE_WAKE, + WAKE_STAT_ATTRIBUTE_COUNT, + WAKE_STAT_ATTRIBUTE_CMD_COUNT_USED, + WAKE_STAT_ATTRIBUTE_TOTAL_DRIVER_FW, + WAKE_STAT_ATTRIBUTE_DRIVER_FW_WAKE, + WAKE_STAT_ATTRIBUTE_DRIVER_FW_COUNT, + WAKE_STAT_ATTRIBUTE_DRIVER_FW_COUNT_USED, + WAKE_STAT_ATTRIBUTE_TOTAL_RX_DATA_WAKE, + WAKE_STAT_ATTRIBUTE_RX_UNICAST_COUNT, + WAKE_STAT_ATTRIBUTE_RX_MULTICAST_COUNT, + WAKE_STAT_ATTRIBUTE_RX_BROADCAST_COUNT, + WAKE_STAT_ATTRIBUTE_RX_ICMP_PKT, + WAKE_STAT_ATTRIBUTE_RX_ICMP6_PKT, + WAKE_STAT_ATTRIBUTE_RX_ICMP6_RA, + WAKE_STAT_ATTRIBUTE_RX_ICMP6_NA, + WAKE_STAT_ATTRIBUTE_RX_ICMP6_NS, + WAKE_STAT_ATTRIBUTE_IPV4_RX_MULTICAST_ADD_CNT, + WAKE_STAT_ATTRIBUTE_IPV6_RX_MULTICAST_ADD_CNT, + WAKE_STAT_ATTRIBUTE_OTHER_RX_MULTICAST_ADD_CNT, + WAKE_STAT_ATTRIBUTE_RX_MULTICAST_PKT_INFO, + WAKE_STAT_ATTRIBUTE_MAX +}; + +typedef enum { + SET_HAL_START_ATTRIBUTE_DEINIT = 0x0001, + SET_HAL_START_ATTRIBUTE_PRE_INIT = 0x0002, + SET_HAL_START_ATTRIBUTE_EVENT_SOCK_PID = 0x0003 +} SET_HAL_START_ATTRIBUTE; + +typedef enum { + OTA_DOWNLOAD_CLM_LENGTH_ATTR = 0x0001, + OTA_DOWNLOAD_CLM_ATTR = 0x0002, + OTA_DOWNLOAD_NVRAM_LENGTH_ATTR = 0x0003, + OTA_DOWNLOAD_NVRAM_ATTR = 0x0004, + OTA_SET_FORCE_REG_ON = 0x0005, + OTA_CUR_NVRAM_EXT_ATTR = 0x0006, +} OTA_DOWNLOAD_ATTRIBUTE; + +#define HAL_START_REQUEST_ID 2 +#define HAL_RESTART_ID 3 +#define FILE_NAME_LEN 256 +#define RING_NAME_LEN 32 +#if defined(RING_DUMP) +/* Loglevel */ +#define DUMP_DEBUG(x) +#define DUMP_INFO(x) ALOGI x +#define FILE_DUMP_REQUEST_ID 2 +#define C2S(x) case x: return #x; +static const char *EWP_EventAttrToString(int len_attr); +static const char *EWP_CmdAttrToString(int data_attr); + +typedef struct buf_data { + u32 ver; /* version of struct */ + u32 len; /* Total len */ + /* size of each buffer in case of split buffers (0 - single buffer). */ + u32 buf_threshold; + const void *data_buf[1]; /* array of user space buffer pointers.*/ +} buf_data_t; + +/* Attributes associated with GOOGLE_FILE_DUMP_EVENT */ +typedef enum { + DUMP_LEN_ATTR_INVALID = 0, + DUMP_LEN_ATTR_MEMDUMP = 1, + DUMP_LEN_ATTR_SSSR_C0_BEFORE = 2, + DUMP_LEN_ATTR_SSSR_C0_AFTER = 3, + DUMP_LEN_ATTR_SSSR_C1_BEFORE = 4, + DUMP_LEN_ATTR_SSSR_C1_AFTER = 5, + DUMP_LEN_ATTR_SSSR_C2_BEFORE = 6, + DUMP_LEN_ATTR_SSSR_C2_AFTER = 7, + DUMP_LEN_ATTR_SSSR_DIG_BEFORE = 8, + DUMP_LEN_ATTR_SSSR_DIG_AFTER = 9, + DUMP_LEN_ATTR_TIMESTAMP = 10, + DUMP_LEN_ATTR_GENERAL_LOG = 11, + DUMP_LEN_ATTR_ECNTRS = 12, + DUMP_LEN_ATTR_SPECIAL_LOG = 13, + DUMP_LEN_ATTR_DHD_DUMP = 14, + DUMP_LEN_ATTR_EXT_TRAP = 15, + DUMP_LEN_ATTR_HEALTH_CHK = 16, + DUMP_LEN_ATTR_PRESERVE_LOG = 17, + DUMP_LEN_ATTR_COOKIE = 18, + DUMP_LEN_ATTR_FLOWRING_DUMP = 19, + DUMP_LEN_ATTR_PKTLOG = 20, + DUMP_LEN_ATTR_PKTLOG_DEBUG = 21, + DUMP_FILENAME_ATTR_DEBUG_DUMP = 22, + DUMP_FILENAME_ATTR_MEM_DUMP = 23, + DUMP_FILENAME_ATTR_SSSR_CORE_0_BEFORE_DUMP = 24, + DUMP_FILENAME_ATTR_SSSR_CORE_0_AFTER_DUMP = 25, + DUMP_FILENAME_ATTR_SSSR_CORE_1_BEFORE_DUMP = 26, + DUMP_FILENAME_ATTR_SSSR_CORE_1_AFTER_DUMP = 27, + DUMP_FILENAME_ATTR_SSSR_CORE_2_BEFORE_DUMP = 28, + DUMP_FILENAME_ATTR_SSSR_CORE_2_AFTER_DUMP = 29, + DUMP_FILENAME_ATTR_SSSR_DIG_BEFORE_DUMP = 30, + DUMP_FILENAME_ATTR_SSSR_DIG_AFTER_DUMP = 31, + DUMP_FILENAME_ATTR_PKTLOG_DUMP = 32, + DUMP_FILENAME_ATTR_PKTLOG_DEBUG_DUMP = 33, + DUMP_LEN_ATTR_STATUS_LOG = 34, + DUMP_LEN_ATTR_AXI_ERROR = 35, + DUMP_FILENAME_ATTR_AXI_ERROR_DUMP = 36, + DUMP_LEN_ATTR_RTT_LOG = 37, + DUMP_LEN_ATTR_SDTC_ETB_DUMP = 38, + DUMP_FILENAME_ATTR_SDTC_ETB_DUMP = 39, + DUMP_LEN_ATTR_PKTID_MAP_LOG = 40, + DUMP_LEN_ATTR_PKTID_UNMAP_LOG = 41, + DUMP_LEN_ATTR_EWP_HW_INIT_LOG = 42, + DUMP_LEN_ATTR_EWP_HW_MOD_DUMP = 43, + DUMP_LEN_ATTR_EWP_HW_REG_DUMP = 44, + /* Please add new attributes from here to sync up old DHD */ + DUMP_EVENT_ATTR_MAX = 45, +} EWP_DUMP_EVENT_ATTRIBUTE; + +/* Attributes associated with DEBUG_GET_DUMP_BUF */ +typedef enum { + DUMP_BUF_ATTR_INVALID = 0, + DUMP_BUF_ATTR_MEMDUMP = 1, + DUMP_BUF_ATTR_SSSR_C0_BEFORE = 2, + DUMP_BUF_ATTR_SSSR_C0_AFTER = 3, + DUMP_BUF_ATTR_SSSR_C1_BEFORE = 4, + DUMP_BUF_ATTR_SSSR_C1_AFTER = 5, + DUMP_BUF_ATTR_SSSR_C2_BEFORE = 6, + DUMP_BUF_ATTR_SSSR_C2_AFTER = 7, + DUMP_BUF_ATTR_SSSR_DIG_BEFORE = 8, + DUMP_BUF_ATTR_SSSR_DIG_AFTER = 9, + DUMP_BUF_ATTR_TIMESTAMP = 10, + DUMP_BUF_ATTR_GENERAL_LOG = 11, + DUMP_BUF_ATTR_ECNTRS = 12, + DUMP_BUF_ATTR_SPECIAL_LOG = 13, + DUMP_BUF_ATTR_DHD_DUMP = 14, + DUMP_BUF_ATTR_EXT_TRAP = 15, + DUMP_BUF_ATTR_HEALTH_CHK = 16, + DUMP_BUF_ATTR_PRESERVE_LOG = 17, + DUMP_BUF_ATTR_COOKIE = 18, + DUMP_BUF_ATTR_FLOWRING_DUMP = 19, + DUMP_BUF_ATTR_PKTLOG = 20, + DUMP_BUF_ATTR_PKTLOG_DEBUG = 21, + DUMP_BUF_ATTR_STATUS_LOG = 22, + DUMP_BUF_ATTR_AXI_ERROR = 23, + DUMP_BUF_ATTR_RTT_LOG = 24, + DUMP_BUF_ATTR_SDTC_ETB_DUMP = 25, + DUMP_BUF_ATTR_PKTID_MAP_LOG = 26, + DUMP_BUF_ATTR_PKTID_UNMAP_LOG = 27, + DUMP_BUF_ATTR_EWP_HW_INIT_LOG = 28, + DUMP_BUF_ATTR_EWP_HW_MOD_DUMP = 29, + DUMP_BUF_ATTR_EWP_HW_REG_DUMP = 30, + /* Please add new attributes from here to sync up old DHD */ + DUMP_BUF_ATTR_MAX = 31, +} EWP_DUMP_CMD_ATTRIBUTE; + +typedef enum { + DUMP_TYPE_MEM_DUMP = 0, + DUMP_TYPE_DEBUG_DUMP = 1, + DUMP_TYPE_SSSR_CORE0_BEF_DUMP = 2, + DUMP_TYPE_SSSR_CORE0_AFT_DUMP = 3, + DUMP_TYPE_SSSR_CORE1_BEF_DUMP = 4, + DUMP_TYPE_SSSR_CORE1_AFT_DUMP = 5, + DUMP_TYPE_SSSR_CORE2_BEF_DUMP = 6, + DUMP_TYPE_SSSR_CORE2_AFT_DUMP = 7, + DUMP_TYPE_SSSR_DIG_BEF_DUMP = 8, + DUMP_TYPE_SSSR_DIG_AFT_DUMP = 9, + DUMP_TYPE_PKTLOG_DUMP = 10, + DUMP_TYPE_PKTLOG_DEBUG_DUMP = 11, + DUMP_TYPE_AXI_ERROR_DUMP = 12, + DUMP_TYPE_D2H_MINI_DUMP = 13, + DUMP_TYPE_SDTC_ETB_DUMP = 14, + /* Please add new attributes from here to sync up old DHD */ + DUMP_TYPE_MAX = 15, +} EWP_DUMP_TYPE; + +/* Struct for table which has len_attr, data_attr and dump file type attr */ +typedef struct logger_attr_entry { + u8 attr_type; /* Type of attribute */ + u8 buf_attr; /* Buffer associated with the attribute */ + u8 dump_type; /* Each attribute will be linked to a dump type */ +} logger_attr_entry_t; + +logger_attr_entry_t attr_lookup_tbl[] = { + /* Mem Dump Block */ + {DUMP_FILENAME_ATTR_MEM_DUMP, 0, DUMP_TYPE_MEM_DUMP}, + {DUMP_LEN_ATTR_MEMDUMP, DUMP_BUF_ATTR_MEMDUMP, DUMP_TYPE_MEM_DUMP}, + /* SSSR Dump Block */ + {DUMP_FILENAME_ATTR_SSSR_CORE_0_BEFORE_DUMP, 0, DUMP_TYPE_SSSR_CORE0_BEF_DUMP}, + {DUMP_LEN_ATTR_SSSR_C0_BEFORE, DUMP_BUF_ATTR_SSSR_C0_BEFORE, DUMP_TYPE_SSSR_CORE0_BEF_DUMP}, + + {DUMP_FILENAME_ATTR_SSSR_CORE_0_AFTER_DUMP, 0, DUMP_TYPE_SSSR_CORE0_AFT_DUMP}, + {DUMP_LEN_ATTR_SSSR_C0_AFTER, DUMP_BUF_ATTR_SSSR_C0_AFTER, DUMP_TYPE_SSSR_CORE0_AFT_DUMP}, + + {DUMP_FILENAME_ATTR_SSSR_CORE_1_BEFORE_DUMP, 0, DUMP_TYPE_SSSR_CORE1_BEF_DUMP}, + {DUMP_LEN_ATTR_SSSR_C1_BEFORE, DUMP_BUF_ATTR_SSSR_C1_BEFORE, DUMP_TYPE_SSSR_CORE1_BEF_DUMP}, + + {DUMP_FILENAME_ATTR_SSSR_CORE_1_AFTER_DUMP, 0, DUMP_TYPE_SSSR_CORE1_AFT_DUMP}, + {DUMP_LEN_ATTR_SSSR_C1_AFTER, DUMP_BUF_ATTR_SSSR_C1_AFTER, DUMP_TYPE_SSSR_CORE1_AFT_DUMP}, + + {DUMP_FILENAME_ATTR_SSSR_CORE_2_BEFORE_DUMP, 0, DUMP_TYPE_SSSR_CORE2_BEF_DUMP}, + {DUMP_LEN_ATTR_SSSR_C2_BEFORE, DUMP_BUF_ATTR_SSSR_C2_BEFORE, DUMP_TYPE_SSSR_CORE2_BEF_DUMP}, + + {DUMP_FILENAME_ATTR_SSSR_CORE_2_AFTER_DUMP, 0, DUMP_TYPE_SSSR_CORE2_AFT_DUMP}, + {DUMP_LEN_ATTR_SSSR_C2_AFTER, DUMP_BUF_ATTR_SSSR_C2_AFTER, DUMP_TYPE_SSSR_CORE2_AFT_DUMP}, + + {DUMP_FILENAME_ATTR_SSSR_DIG_BEFORE_DUMP, 0, DUMP_TYPE_SSSR_DIG_BEF_DUMP}, + {DUMP_LEN_ATTR_SSSR_DIG_BEFORE, DUMP_BUF_ATTR_SSSR_DIG_BEFORE, DUMP_TYPE_SSSR_DIG_BEF_DUMP}, + + {DUMP_FILENAME_ATTR_SSSR_DIG_AFTER_DUMP, 0, DUMP_TYPE_SSSR_DIG_AFT_DUMP}, + {DUMP_LEN_ATTR_SSSR_DIG_AFTER, DUMP_BUF_ATTR_SSSR_DIG_AFTER, DUMP_TYPE_SSSR_DIG_AFT_DUMP}, + + /* Debug Dump Block */ + {DUMP_FILENAME_ATTR_DEBUG_DUMP, 0, DUMP_TYPE_DEBUG_DUMP}, + {DUMP_LEN_ATTR_TIMESTAMP, DUMP_BUF_ATTR_TIMESTAMP, DUMP_TYPE_DEBUG_DUMP}, + {DUMP_LEN_ATTR_GENERAL_LOG, DUMP_BUF_ATTR_GENERAL_LOG, DUMP_TYPE_DEBUG_DUMP}, + {DUMP_LEN_ATTR_ECNTRS, DUMP_BUF_ATTR_ECNTRS, DUMP_TYPE_DEBUG_DUMP}, + {DUMP_LEN_ATTR_SPECIAL_LOG, DUMP_BUF_ATTR_SPECIAL_LOG, DUMP_TYPE_DEBUG_DUMP}, + {DUMP_LEN_ATTR_DHD_DUMP, DUMP_BUF_ATTR_DHD_DUMP, DUMP_TYPE_DEBUG_DUMP}, + {DUMP_LEN_ATTR_EXT_TRAP, DUMP_BUF_ATTR_EXT_TRAP, DUMP_TYPE_DEBUG_DUMP}, + {DUMP_LEN_ATTR_HEALTH_CHK, DUMP_BUF_ATTR_HEALTH_CHK, DUMP_TYPE_DEBUG_DUMP}, + {DUMP_LEN_ATTR_PRESERVE_LOG, DUMP_BUF_ATTR_PRESERVE_LOG, DUMP_TYPE_DEBUG_DUMP}, + {DUMP_LEN_ATTR_COOKIE, DUMP_BUF_ATTR_COOKIE, DUMP_TYPE_DEBUG_DUMP}, + {DUMP_LEN_ATTR_FLOWRING_DUMP, DUMP_BUF_ATTR_FLOWRING_DUMP, DUMP_TYPE_DEBUG_DUMP}, + {DUMP_LEN_ATTR_STATUS_LOG, DUMP_BUF_ATTR_STATUS_LOG, DUMP_TYPE_DEBUG_DUMP}, + {DUMP_LEN_ATTR_RTT_LOG, DUMP_BUF_ATTR_RTT_LOG, DUMP_TYPE_DEBUG_DUMP}, + {DUMP_LEN_ATTR_PKTID_MAP_LOG, DUMP_BUF_ATTR_PKTID_MAP_LOG, DUMP_TYPE_DEBUG_DUMP}, + {DUMP_LEN_ATTR_PKTID_UNMAP_LOG, DUMP_BUF_ATTR_PKTID_UNMAP_LOG, DUMP_TYPE_DEBUG_DUMP}, + {DUMP_LEN_ATTR_EWP_HW_INIT_LOG, DUMP_BUF_ATTR_EWP_HW_INIT_LOG, DUMP_TYPE_DEBUG_DUMP}, + {DUMP_LEN_ATTR_EWP_HW_MOD_DUMP, DUMP_BUF_ATTR_EWP_HW_MOD_DUMP, DUMP_TYPE_DEBUG_DUMP}, + {DUMP_LEN_ATTR_EWP_HW_REG_DUMP, DUMP_BUF_ATTR_EWP_HW_REG_DUMP, DUMP_TYPE_DEBUG_DUMP}, + + /* PKT log dump block */ + {DUMP_FILENAME_ATTR_PKTLOG_DUMP, 0, DUMP_TYPE_PKTLOG_DUMP}, + {DUMP_LEN_ATTR_PKTLOG, DUMP_BUF_ATTR_PKTLOG, DUMP_TYPE_PKTLOG_DUMP}, + {DUMP_FILENAME_ATTR_PKTLOG_DEBUG_DUMP, 0, DUMP_TYPE_PKTLOG_DEBUG_DUMP}, + {DUMP_LEN_ATTR_PKTLOG_DEBUG, DUMP_BUF_ATTR_PKTLOG_DEBUG, DUMP_TYPE_PKTLOG_DEBUG_DUMP}, + /* AXI error log dump block */ + {DUMP_FILENAME_ATTR_AXI_ERROR_DUMP, 0, DUMP_TYPE_AXI_ERROR_DUMP}, + {DUMP_LEN_ATTR_AXI_ERROR, DUMP_BUF_ATTR_AXI_ERROR, DUMP_TYPE_AXI_ERROR_DUMP}, + /* SDTC etb log dump block */ + {DUMP_FILENAME_ATTR_SDTC_ETB_DUMP, 0, DUMP_TYPE_SDTC_ETB_DUMP}, + {DUMP_LEN_ATTR_SDTC_ETB_DUMP, DUMP_BUF_ATTR_SDTC_ETB_DUMP, DUMP_TYPE_SDTC_ETB_DUMP}, + {DUMP_EVENT_ATTR_MAX, 0, 0}, +}; + +static const char *EWP_EventAttrToString(int len_attr) +{ + switch (len_attr) { + C2S(DUMP_LEN_ATTR_MEMDUMP) + C2S(DUMP_LEN_ATTR_SSSR_C0_BEFORE) + C2S(DUMP_LEN_ATTR_SSSR_C0_AFTER) + C2S(DUMP_LEN_ATTR_SSSR_C1_BEFORE) + C2S(DUMP_LEN_ATTR_SSSR_C1_AFTER) + C2S(DUMP_LEN_ATTR_SSSR_C2_BEFORE) + C2S(DUMP_LEN_ATTR_SSSR_C2_AFTER) + C2S(DUMP_LEN_ATTR_SSSR_DIG_BEFORE) + C2S(DUMP_LEN_ATTR_SSSR_DIG_AFTER) + C2S(DUMP_LEN_ATTR_TIMESTAMP) + C2S(DUMP_LEN_ATTR_GENERAL_LOG) + C2S(DUMP_LEN_ATTR_ECNTRS) + C2S(DUMP_LEN_ATTR_SPECIAL_LOG) + C2S(DUMP_LEN_ATTR_DHD_DUMP) + C2S(DUMP_LEN_ATTR_EXT_TRAP) + C2S(DUMP_LEN_ATTR_HEALTH_CHK) + C2S(DUMP_LEN_ATTR_PRESERVE_LOG) + C2S(DUMP_LEN_ATTR_COOKIE) + C2S(DUMP_LEN_ATTR_FLOWRING_DUMP) + C2S(DUMP_LEN_ATTR_PKTLOG) + C2S(DUMP_LEN_ATTR_PKTLOG_DEBUG) + C2S(DUMP_LEN_ATTR_STATUS_LOG) + C2S(DUMP_FILENAME_ATTR_DEBUG_DUMP) + C2S(DUMP_FILENAME_ATTR_MEM_DUMP) + C2S(DUMP_FILENAME_ATTR_SSSR_CORE_0_BEFORE_DUMP) + C2S(DUMP_FILENAME_ATTR_SSSR_CORE_0_AFTER_DUMP) + C2S(DUMP_FILENAME_ATTR_SSSR_CORE_1_BEFORE_DUMP) + C2S(DUMP_FILENAME_ATTR_SSSR_CORE_1_AFTER_DUMP) + C2S(DUMP_FILENAME_ATTR_SSSR_CORE_2_BEFORE_DUMP) + C2S(DUMP_FILENAME_ATTR_SSSR_CORE_2_AFTER_DUMP) + C2S(DUMP_FILENAME_ATTR_SSSR_DIG_BEFORE_DUMP) + C2S(DUMP_FILENAME_ATTR_SSSR_DIG_AFTER_DUMP) + C2S(DUMP_FILENAME_ATTR_PKTLOG_DUMP) + C2S(DUMP_FILENAME_ATTR_PKTLOG_DEBUG_DUMP) + C2S(DUMP_LEN_ATTR_AXI_ERROR) + C2S(DUMP_FILENAME_ATTR_AXI_ERROR_DUMP) + C2S(DUMP_LEN_ATTR_RTT_LOG) + C2S(DUMP_FILENAME_ATTR_SDTC_ETB_DUMP) + C2S(DUMP_LEN_ATTR_SDTC_ETB_DUMP) + C2S(DUMP_LEN_ATTR_PKTID_MAP_LOG) + C2S(DUMP_LEN_ATTR_PKTID_UNMAP_LOG) + C2S(DUMP_LEN_ATTR_EWP_HW_INIT_LOG) + C2S(DUMP_LEN_ATTR_EWP_HW_MOD_DUMP) + C2S(DUMP_LEN_ATTR_EWP_HW_REG_DUMP) + default: + return "DUMP_LEN_ATTR_INVALID"; + } +} + +static const char *EWP_CmdAttrToString(int attr) +{ + switch (attr) { + C2S(DUMP_BUF_ATTR_MEMDUMP) + C2S(DUMP_BUF_ATTR_SSSR_C0_BEFORE) + C2S(DUMP_BUF_ATTR_SSSR_C0_AFTER) + C2S(DUMP_BUF_ATTR_SSSR_C1_BEFORE) + C2S(DUMP_BUF_ATTR_SSSR_C1_AFTER) + C2S(DUMP_BUF_ATTR_SSSR_C2_BEFORE) + C2S(DUMP_BUF_ATTR_SSSR_C2_AFTER) + C2S(DUMP_BUF_ATTR_SSSR_DIG_BEFORE) + C2S(DUMP_BUF_ATTR_SSSR_DIG_AFTER) + C2S(DUMP_BUF_ATTR_TIMESTAMP) + C2S(DUMP_BUF_ATTR_GENERAL_LOG) + C2S(DUMP_BUF_ATTR_ECNTRS) + C2S(DUMP_BUF_ATTR_SPECIAL_LOG) + C2S(DUMP_BUF_ATTR_DHD_DUMP) + C2S(DUMP_BUF_ATTR_EXT_TRAP) + C2S(DUMP_BUF_ATTR_HEALTH_CHK) + C2S(DUMP_BUF_ATTR_PRESERVE_LOG) + C2S(DUMP_BUF_ATTR_COOKIE) + C2S(DUMP_BUF_ATTR_FLOWRING_DUMP) + C2S(DUMP_BUF_ATTR_PKTLOG) + C2S(DUMP_BUF_ATTR_PKTLOG_DEBUG) + C2S(DUMP_BUF_ATTR_STATUS_LOG) + C2S(DUMP_BUF_ATTR_AXI_ERROR) + C2S(DUMP_BUF_ATTR_RTT_LOG) + C2S(DUMP_BUF_ATTR_SDTC_ETB_DUMP) + C2S(DUMP_BUF_ATTR_PKTID_MAP_LOG) + C2S(DUMP_BUF_ATTR_PKTID_UNMAP_LOG) + C2S(DUMP_BUF_ATTR_EWP_HW_INIT_LOG) + C2S(DUMP_BUF_ATTR_EWP_HW_MOD_DUMP) + C2S(DUMP_BUF_ATTR_EWP_HW_REG_DUMP) + default: + return "DUMP_BUF_ATTR_INVALID"; + } +} + +/* Return index for matching buffer attribute */ +static int logger_attr_buffer_lookup(u8 attr) { + for (u8 i = 0; i < ARRAYSIZE(attr_lookup_tbl); i++) { + if (attr == attr_lookup_tbl[i].buf_attr) { + return i; + } + } + ALOGE("Lookup for buf attr = %s failed\n", + EWP_CmdAttrToString(attr)); + return -1; +} + +/* Return index matching the length attribute */ +static int logger_attr_lookup(u8 attr) { + for (u8 i = 0; i < ARRAYSIZE(attr_lookup_tbl); i++) { + if (attr == attr_lookup_tbl[i].attr_type) { + return i; + } + } + ALOGE("Lookup for len attr = %s failed\n", + EWP_EventAttrToString(attr)); + return -1; +} +#endif /* RING_DUMP */ + +#define DBGRING_NAME_MAX 32 //Copy from legacy hal +typedef struct wifi_buf_ring_map_entry { + uint32_t type; + uint32_t ring_id; + char ring_name[DBGRING_NAME_MAX]; +} wifi_buf_ring_map_entry_t; + +typedef struct { + char hw_id[PROPERTY_VALUE_MAX]; + char sku[MAX_SKU_NAME_LEN]; +} sku_info_t; + +sku_info_t sku_table[] = { + { {"G9S9B"}, {"MMW"} }, + { {"G8V0U"}, {"MMW"} }, + { {"GFQM1"}, {"MMW"} }, + { {"GB62Z"}, {"MMW"} }, + { {"GB7N6"}, {"ROW"} }, + { {"GLU0G"}, {"ROW"} }, + { {"GNA8F"}, {"ROW"} }, + { {"GX7AS"}, {"ROW"} }, + { {"GR1YH"}, {"JPN"} }, + { {"GF5KQ"}, {"JPN"} }, + { {"GPQ72"}, {"JPN"} }, + { {"GB17L"}, {"JPN"} }, + { {"G1AZG"}, {"EU"} } +}; +/////////////////////////////////////////////////////////////////////////////// +class DebugCommand : public WifiCommand +{ + char *mBuff; + int *mBuffSize; + u32 *mNumRings; + wifi_ring_buffer_status *mStatus; + u32 *mNumMaps; + wifi_buf_ring_map_entry_t *mMaps; + unsigned int *mSupport; + u32 mVerboseLevel; + u32 mFlags; + u32 mMaxIntervalSec; + u32 mMinDataSize; + char *mRingName; + GetCmdType mType; + +public: + + // constructor for get version + DebugCommand(wifi_interface_handle iface, char *buffer, int *buffer_size, + GetCmdType cmdType) + : WifiCommand("DebugCommand", iface, 0), mBuff(buffer), mBuffSize(buffer_size), mType + (cmdType) + { + mNumRings = NULL; + mStatus = NULL; + mSupport = NULL; + mVerboseLevel = 0; + mFlags = 0; + mMaxIntervalSec = 0; + mMinDataSize = 0; + mRingName = NULL; + memset(mBuff, 0, *mBuffSize); + } + + // constructor for ring data + DebugCommand(wifi_interface_handle iface, char *ring_name, GetCmdType cmdType) + : WifiCommand("DebugCommand", iface, 0), mRingName(ring_name), mType(cmdType) + { + mBuff = NULL; + mBuffSize = NULL; + mNumRings = NULL; + mStatus = NULL; + mSupport = NULL; + mVerboseLevel = 0; + mFlags = 0; + mMaxIntervalSec = 0; + mMinDataSize = 0; + } + + // constructor for ring status + DebugCommand(wifi_interface_handle iface, u32 *num_rings, + wifi_ring_buffer_status *status, GetCmdType cmdType) + : WifiCommand("DebugCommand", iface, 0), mNumRings(num_rings), mStatus(status), mType(cmdType) + { + mBuff = NULL; + mBuffSize = NULL; + mSupport = NULL; + mVerboseLevel = 0; + mFlags = 0; + mMaxIntervalSec = 0; + mMinDataSize = 0; + mRingName = NULL; + memset(mStatus, 0, sizeof(wifi_ring_buffer_status) * (*mNumRings)); + } + + // constructor for feature set + DebugCommand(wifi_interface_handle iface, unsigned int *support, GetCmdType cmdType) + : WifiCommand("DebugCommand", iface, 0), mSupport(support), mType(cmdType) + { + mBuff = NULL; + mBuffSize = NULL; + mNumRings = NULL; + mStatus = NULL; + mVerboseLevel = 0; + mFlags = 0; + mMaxIntervalSec = 0; + mMinDataSize = 0; + mRingName = NULL; + } + + // constructor for buf ring map + DebugCommand(wifi_interface_handle iface, u32 *num_maps, + wifi_buf_ring_map_entry_t *map, GetCmdType cmdType) + : WifiCommand("DebugCommand", iface, 0), mNumMaps(num_maps), mMaps(map), mType(cmdType) + { + memset(mMaps, 0, sizeof(wifi_buf_ring_map_entry_t) * (*mNumMaps)); + } + + // constructor for ring params + DebugCommand(wifi_interface_handle iface, u32 verbose_level, u32 flags, + u32 max_interval_sec, u32 min_data_size, char *ring_name, GetCmdType cmdType) + : WifiCommand("DebugCommand", iface, 0), mVerboseLevel(verbose_level), mFlags(flags), + mMaxIntervalSec(max_interval_sec), mMinDataSize(min_data_size), + mRingName(ring_name), mType(cmdType) + { + mBuff = NULL; + mBuffSize = NULL; + mNumRings = NULL; + mStatus = NULL; + mSupport = NULL; + } + + int createRingRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, LOGGER_START_LOGGING); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to create start ring logger request; result = %d", result); + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + result = request.put_u32(LOGGER_ATTRIBUTE_LOG_LEVEL, mVerboseLevel); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put log level; result = %d", result); + return result; + } + result = request.put_u32(LOGGER_ATTRIBUTE_RING_FLAGS, mFlags); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put ring flags; result = %d", result); + return result; + } + result = request.put_u32(LOGGER_ATTRIBUTE_LOG_TIME_INTVAL, mMaxIntervalSec); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put log time interval; result = %d", result); + return result; + } + result = request.put_u32(LOGGER_ATTRIBUTE_LOG_MIN_DATA_SIZE, mMinDataSize); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put min data size; result = %d", result); + return result; + } + result = request.put_string(LOGGER_ATTRIBUTE_RING_NAME, mRingName); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put ringbuffer name; result = %d", result); + return result; + } + request.attr_end(data); + + return WIFI_SUCCESS; + } + + int createRequest(WifiRequest &request) { + int result; + + switch (mType) { + case GET_FW_VER: + { + result = request.create(GOOGLE_OUI, LOGGER_GET_VER); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to create get fw version request; result = %d", result); + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + // Driver expecting only attribute type, passing mbuff as data with + // length 0 to avoid undefined state + result = request.put(LOGGER_ATTRIBUTE_FW_VER, mBuff, 0); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put get fw version request; result = %d", result); + return result; + } + request.attr_end(data); + break; + } + + case GET_DRV_VER: + { + result = request.create(GOOGLE_OUI, LOGGER_GET_VER); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to create get drv version request; result = %d", result); + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + // Driver expecting only attribute type, passing mbuff as data with + // length 0 to avoid undefined state + result = request.put(LOGGER_ATTRIBUTE_DRIVER_VER, mBuff, 0); + + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put get drv version request; result = %d", result); + return result; + } + request.attr_end(data); + break; + } + + case GET_RING_DATA: + { + result = request.create(GOOGLE_OUI, LOGGER_GET_RING_DATA); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to create get ring data request; result = %d", result); + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_string(LOGGER_ATTRIBUTE_RING_NAME, mRingName); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put ring data request; result = %d", result); + return result; + } + request.attr_end(data); + break; + } + + case GET_RING_STATUS: + { + result = request.create(GOOGLE_OUI, LOGGER_GET_RING_STATUS); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to create get ring status request; result = %d", result); + return result; + } + break; + } + + case GET_FEATURE: + { + result = request.create(GOOGLE_OUI, LOGGER_GET_FEATURE); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to create get feature request; result = %d", result); + return result; + } + break; + } + + case GET_BUF_RING_MAP: + { + result = request.create(GOOGLE_OUI, LOGGER_GET_BUF_RING_MAP); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to create get ring status request; result = %d", result); + return result; + } + break; + } + + case START_RING_LOG: + result = createRingRequest(request); + break; + + default: + ALOGE("Unknown Debug command"); + result = WIFI_ERROR_UNKNOWN; + } + return result; + } + + int start() { + ALOGD("Start debug command"); + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to create debug request; result = %d", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to register debug response; result = %d", result); + } + return result; + } + + virtual int handleResponse(WifiEvent& reply) { + ALOGD("In DebugCommand::handleResponse, mType:%d\n", mType); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + switch (mType) { + case GET_DRV_VER: + case GET_FW_VER: + { + void *data = reply.get_vendor_data(); + int len = reply.get_vendor_data_len(); + + ALOGD("len = %d, expected len = %d", len, *mBuffSize); + memcpy(mBuff, data, min(len, *mBuffSize)); + if (*mBuffSize < len) + return NL_SKIP; + *mBuffSize = len; + break; + } + + case START_RING_LOG: + case GET_RING_DATA: + break; + + case GET_RING_STATUS: + { + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + wifi_ring_buffer_status *status(mStatus); + + if (vendor_data == NULL || len == 0) { + ALOGE("No Debug data found"); + return NL_SKIP; + } + + nl_iterator it(vendor_data); + if (it.get_type() == LOGGER_ATTRIBUTE_RING_NUM) { + unsigned int num_rings = it.get_u32(); + if (*mNumRings < num_rings) { + ALOGE("Not enough status buffers provided, available: %d required: %d", + *mNumRings, num_rings); + } else { + *mNumRings = num_rings; + } + } else { + ALOGE("Unknown attribute: %d expecting %d", + it.get_type(), LOGGER_ATTRIBUTE_RING_NUM); + return NL_SKIP; + } + + it.next(); + for (unsigned int i = 0; it.has_next() && i < *mNumRings; it.next()) { + if (it.get_type() == LOGGER_ATTRIBUTE_RING_STATUS) { + if (it.get_len() > sizeof(wifi_ring_buffer_status)) { + ALOGE("ring status unexpected len = %d, dest len = %lu", + it.get_len(), sizeof(wifi_ring_buffer_status)); + return NL_SKIP; + } else { + memcpy(status, it.get_data(), sizeof(wifi_ring_buffer_status)); + i++; + status++; + } + } else { + ALOGW("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + break; + } + + case GET_FEATURE: + { + void *data = reply.get_vendor_data(); + int len = reply.get_vendor_data_len(); + + ALOGD("len = %d, expected len = %lu", len, sizeof(unsigned int)); + memcpy(mSupport, data, sizeof(unsigned int)); + break; + } + + case GET_BUF_RING_MAP: + { + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + wifi_buf_ring_map_entry_t *map(mMaps); + + if (vendor_data == NULL || len == 0) { + ALOGE("No Debug data found"); + return NL_SKIP; + } + + nl_iterator it(vendor_data); + if (it.get_type() == LOGGER_ATTRIBUTE_BUF_RING_NUM) { + unsigned int num_maps = it.get_u32(); + if (*mNumMaps < num_maps) { + ALOGE("Not enough status buffers provided, available: %d required: %d", + *mNumMaps, num_maps); + } else { + *mNumMaps = num_maps; + } + } else { + ALOGE("Unknown attribute: %d expecting %d", + it.get_type(), LOGGER_ATTRIBUTE_BUF_RING_NUM); + return NL_SKIP; + } + + it.next(); + for (unsigned int i = 0; it.has_next() && i < *mNumMaps; it.next()) { + if (it.get_type() == LOGGER_ATTRIBUTE_BUF_RING_MAP) { + if (it.get_len() > sizeof(wifi_buf_ring_map_entry_t)) { + ALOGE("GET_BUF_RING_MAP: unexpected len = %d, dest len = %lu", + it.get_len(), sizeof(wifi_buf_ring_map_entry_t)); + return NL_SKIP; + } else { + memcpy(map, it.get_data(), sizeof(wifi_buf_ring_map_entry_t)); + } + i++; + map++; + } else { + ALOGW("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + break; + } + + default: + ALOGW("Unknown Debug command"); + } + return NL_OK; + } + + virtual int handleEvent(WifiEvent& event) { + /* NO events! */ + return NL_SKIP; + } +}; + +/* API to collect a firmware version string */ +wifi_error wifi_get_firmware_version(wifi_interface_handle iface, char *buffer, + int buffer_size) +{ + if (buffer && (buffer_size > 0)) { + DebugCommand *cmd = new DebugCommand(iface, buffer, &buffer_size, GET_FW_VER); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; + } else { + ALOGE("FW version buffer NULL"); + return WIFI_ERROR_INVALID_ARGS; + } +} + +/* API to collect a driver version string */ +wifi_error wifi_get_driver_version(wifi_interface_handle iface, char *buffer, int buffer_size) +{ + if (buffer && (buffer_size > 0)) { + DebugCommand *cmd = new DebugCommand(iface, buffer, &buffer_size, GET_DRV_VER); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; + } else { + ALOGE("Driver version buffer NULL"); + return WIFI_ERROR_INVALID_ARGS; + } +} + +/* API to collect driver records */ +wifi_error wifi_get_ring_data(wifi_interface_handle iface, char *ring_name) +{ + DebugCommand *cmd = new DebugCommand(iface, ring_name, GET_RING_DATA); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; +} + +/* API to get the status of all ring buffers supported by driver */ +wifi_error wifi_get_ring_buffers_status(wifi_interface_handle iface, + u32 *num_rings, wifi_ring_buffer_status *status) +{ + if (status && num_rings) { + DebugCommand *cmd = new DebugCommand(iface, num_rings, status, GET_RING_STATUS); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; + } else { + ALOGE("Ring status buffer NULL"); + return WIFI_ERROR_INVALID_ARGS; + } +} + +/* API to get supportable feature */ +wifi_error wifi_get_logger_supported_feature_set(wifi_interface_handle iface, + unsigned int *support) +{ + if (support) { + DebugCommand *cmd = new DebugCommand(iface, support, GET_FEATURE); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; + } else { + ALOGE("Get support buffer NULL"); + return WIFI_ERROR_INVALID_ARGS; + } +} + +wifi_error wifi_start_logging(wifi_interface_handle iface, u32 verbose_level, + u32 flags, u32 max_interval_sec, u32 min_data_size, char *ring_name) +{ + if (ring_name) { + ALOGE("Ring name: level:%d sec:%d ring_name:%s", + verbose_level, max_interval_sec, ring_name); + DebugCommand *cmd = new DebugCommand(iface, verbose_level, flags, max_interval_sec, + min_data_size, ring_name, START_RING_LOG); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; + } else { + ALOGE("Ring name NULL"); + return WIFI_ERROR_INVALID_ARGS; + } +} + + +/////////////////////////////////////////////////////////////////////////////// +class SetLogHandler : public WifiCommand +{ + wifi_ring_buffer_data_handler mHandler; + +public: + SetLogHandler(wifi_interface_handle iface, int id, wifi_ring_buffer_data_handler handler) + : WifiCommand("SetLogHandler", iface, id), mHandler(handler) + { } + + int start() { + ALOGV("Register loghandler"); + int result; + uint32_t event_sock_pid = getpid() + (WIFI_HAL_EVENT_SOCK_PORT << 22); + + WifiRequest request(familyId(), ifaceId()); + + /* set hal event socket port to driver */ + result = request.create(GOOGLE_OUI, LOGGER_SET_HAL_PID); + if (result != WIFI_SUCCESS) { + ALOGV("Failed to set Hal preInit; result = %d", result); + return result; + } + registerVendorHandler(GOOGLE_OUI, GOOGLE_DEBUG_RING_EVENT); + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(SET_HAL_START_ATTRIBUTE_EVENT_SOCK_PID, event_sock_pid); + if (result != WIFI_SUCCESS) { + unregisterVendorHandler(GOOGLE_OUI, GOOGLE_DEBUG_RING_EVENT); + ALOGV("Hal preInit Failed to put pic = %d", result); + return result; + } + + if (result != WIFI_SUCCESS) { + unregisterVendorHandler(GOOGLE_OUI, GOOGLE_DEBUG_RING_EVENT); + ALOGV("Hal preInit Failed to put pid= %d", result); + return result; + } + + request.attr_end(data); + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + unregisterVendorHandler(GOOGLE_OUI, GOOGLE_DEBUG_RING_EVENT); + ALOGE("Failed to register set Hal preInit; result = %d", result); + return result; + } + return result; + } + + virtual int cancel() { + /* Send a command to driver to stop generating logging events */ + ALOGV("Clear loghandler"); + + /* unregister event handler */ + unregisterVendorHandler(GOOGLE_OUI, GOOGLE_DEBUG_RING_EVENT); + wifi_unregister_cmd(wifiHandle(), id()); + + WifiRequest request(familyId(), ifaceId()); + int result = request.create(GOOGLE_OUI, LOGGER_RESET_LOGGING); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create reset request; result = %d", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to request reset; result = %d", result); + return result; + } + + ALOGD("Success to clear loghandler"); + return WIFI_SUCCESS; + } + + virtual int handleEvent(WifiEvent& event) { + char *buffer = NULL; + int buffer_size = 0; + + // ALOGD("In SetLogHandler::handleEvent"); + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = event.get_vendor_data_len(); + int event_id = event.get_vendor_subcmd(); + // ALOGI("Got Logger event: %d", event_id); + + if (vendor_data == NULL || len == 0) { + ALOGE("No Debug data found"); + return NL_SKIP; + } + + if (event_id == GOOGLE_DEBUG_RING_EVENT) { + wifi_ring_buffer_status status; + memset(&status, 0, sizeof(status)); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == LOGGER_ATTRIBUTE_RING_STATUS) { + if (it.get_len() > sizeof(wifi_ring_buffer_status)) { + ALOGE("SetLogHandler: ring status unexpected len = %d, dest len = %lu", + it.get_len(), sizeof(wifi_ring_buffer_status)); + return NL_SKIP; + } else { + memcpy(&status, it.get_data(), sizeof(wifi_ring_buffer_status)); + } + } else if (it.get_type() == LOGGER_ATTRIBUTE_RING_DATA) { + buffer_size = it.get_len(); + buffer = (char *)it.get_data(); + ALOGV("SetLogHandler: ring data size = %d", buffer_size); + } else { + ALOGW("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + + // ALOGI("Retrieved Debug data"); + if (mHandler.on_ring_buffer_data) { + /* Skip msg header. Retrieved log */ + char *pBuff; + wifi_ring_buffer_entry *buffer_entry = + (wifi_ring_buffer_entry *) buffer; + pBuff = (char *) (buffer_entry + 1); + (*mHandler.on_ring_buffer_data)((char *)status.name, pBuff, + buffer_entry->entry_size, &status); + } + } else { + ALOGE("Unknown Event"); + return NL_SKIP; + } + return NL_OK; + } +}; + +wifi_error wifi_set_log_handler(wifi_request_id id, wifi_interface_handle iface, + wifi_ring_buffer_data_handler handler) +{ + wifi_handle handle = getWifiHandle(iface); + ALOGE("Loghandler start, handle = %p", handle); + + SetLogHandler *cmd = new SetLogHandler(iface, id, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + +#ifdef RING_DUMP + wifi_start_ring_dump(iface, handler); +#endif /* RING_DUMP */ + return result; +} + +wifi_error wifi_reset_log_handler(wifi_request_id id, wifi_interface_handle iface) +{ + wifi_handle handle = getWifiHandle(iface); + ALOGE("Loghandler reset, wifi_request_id = %d, handle = %p", id, handle); + +#ifdef RING_DUMP + wifi_stop_ring_dump(iface); +#endif /* RING_DUMP */ + + if (id == -1) { + wifi_ring_buffer_data_handler handler; + memset(&handler, 0, sizeof(handler)); + + SetLogHandler *cmd = new SetLogHandler(iface, id, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + cmd->cancel(); + cmd->releaseRef(); + + return WIFI_SUCCESS; + } + + return wifi_get_cancel_cmd(id, iface); +} + +/////////////////////////////////////////////////////////////////////////////// +class SetAlertHandler : public WifiCommand +{ + wifi_alert_handler mHandler; + int mBuffSize; + char *mBuff; + int mErrCode; + +public: + SetAlertHandler(wifi_interface_handle iface, int id, wifi_alert_handler handler) + : WifiCommand("SetAlertHandler", iface, id), mHandler(handler), mBuffSize(0), mBuff(NULL), + mErrCode(0) + { } + + int start() { + ALOGV("Start Alerting"); + registerVendorHandler(GOOGLE_OUI, GOOGLE_DEBUG_MEM_DUMP_EVENT); + return WIFI_SUCCESS; + } + + virtual int cancel() { + ALOGV("Clear alerthandler"); + + /* unregister alert handler */ + unregisterVendorHandler(GOOGLE_OUI, GOOGLE_DEBUG_MEM_DUMP_EVENT); + wifi_unregister_cmd(wifiHandle(), id()); + ALOGD("Success to clear alerthandler"); + return WIFI_SUCCESS; + } + + virtual int handleResponse(WifiEvent& reply) { + ALOGD("In SetAlertHandler::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + ALOGD("len = %d", len); + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in memory dump response; ignoring it"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == LOGGER_ATTRIBUTE_FW_DUMP_DATA) { + ALOGI("Initiating alert callback"); + if (mHandler.on_alert) { + (*mHandler.on_alert)(id(), mBuff, mBuffSize, mErrCode); + } + if (mBuff) { + free(mBuff); + mBuff = NULL; + } + } + } + return NL_OK; + } + + virtual int handleEvent(WifiEvent& event) { + char *buffer = NULL; + int buffer_size = 0; + bool is_err_alert = false; + + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = event.get_vendor_data_len(); + int event_id = event.get_vendor_subcmd(); + ALOGI("Got event: %d", event_id); + + if (vendor_data == NULL || len == 0) { + ALOGE("No Debug data found"); + return NL_SKIP; + } + + if (event_id == GOOGLE_DEBUG_MEM_DUMP_EVENT) { + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == LOGGER_ATTRIBUTE_FW_DUMP_LEN) { + mBuffSize = it.get_u32(); + } else if (it.get_type() == LOGGER_ATTRIBUTE_RING_DATA) { + buffer_size = it.get_len(); + buffer = (char *)it.get_data(); + } else if (it.get_type() == LOGGER_ATTRIBUTE_FW_ERR_CODE) { + /* Error code is for error alert event only */ + mErrCode = it.get_u32(); + is_err_alert = true; + } else { + ALOGW("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + + if (is_err_alert) { + mBuffSize = sizeof(mErrCode); + if (mBuff) free(mBuff); + mBuff = (char *)malloc(mBuffSize); + if (!mBuff) { + ALOGE("Buffer allocation failed"); + return NL_SKIP; + } + memcpy(mBuff, (char *)&mErrCode, mBuffSize); + ALOGI("Initiating alert callback"); + if (mHandler.on_alert) { + (*mHandler.on_alert)(id(), mBuff, mBuffSize, mErrCode); + } + if (mBuff) { + free(mBuff); + mBuff = NULL; + } + mBuffSize = 0; + return NL_OK; + } + + if (mBuffSize) { + ALOGD("dump size: %d meta data size: %d", mBuffSize, buffer_size); + if (mBuff) free(mBuff); + mBuff = (char *)malloc(mBuffSize + buffer_size); + if (!mBuff) { + ALOGE("Buffer allocation failed"); + return NL_SKIP; + } + memcpy(mBuff, buffer, buffer_size); + + WifiRequest request(familyId(), ifaceId()); + int result = request.create(GOOGLE_OUI, LOGGER_GET_MEM_DUMP); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to create get memory dump request; result = %d", result); + free(mBuff); + return NL_SKIP; + } + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(LOGGER_ATTRIBUTE_FW_DUMP_LEN, mBuffSize); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put get memory dump request; result = %d", result); + return result; + } + + result = request.put_u64(LOGGER_ATTRIBUTE_FW_DUMP_DATA, + (uint64_t)(mBuff+buffer_size)); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put get memory dump request; result = %d", result); + return result; + } + + request.attr_end(data); + mBuffSize += buffer_size; + + result = requestResponse(request); + + if (result != WIFI_SUCCESS) { + ALOGE("Failed to register get momory dump response; result = %d", result); + } + } else { + ALOGE("dump event missing dump length attribute"); + return NL_SKIP; + } + } + return NL_OK; + } +}; + +class SetRestartHandler : public WifiCommand +{ + wifi_subsystem_restart_handler mHandler; + char *mBuff; +public: + SetRestartHandler(wifi_handle handle, wifi_request_id id, wifi_subsystem_restart_handler handler) + : WifiCommand("SetRestartHandler", handle, id), mHandler(handler), mBuff(NULL) + { } + int start() { + ALOGI("Start Restart Handler handler"); + registerVendorHandler(BRCM_OUI, BRCM_VENDOR_EVENT_HANGED); + return WIFI_SUCCESS; + } + virtual int cancel() { + ALOGI("Clear Restart Handler"); + + /* unregister alert handler */ + unregisterVendorHandler(BRCM_OUI, BRCM_VENDOR_EVENT_HANGED); + wifi_unregister_cmd(wifiHandle(), id()); + ALOGI("Success to clear restarthandler"); + return WIFI_SUCCESS; + } + + virtual int handleResponse(WifiEvent& reply) { + /* Nothing to do on response! */ + return NL_OK; + } + + virtual int handleEvent(WifiEvent& event) { + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = event.get_vendor_data_len(); + int event_id = event.get_vendor_subcmd(); + ALOGI("Got event: %d", event_id); + + if (vendor_data == NULL || len == 0) { + ALOGE("No Debug data found"); + return NL_SKIP; + } + if (event_id == BRCM_VENDOR_EVENT_HANGED) { + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == LOGGER_ATTRIBUTE_HANG_REASON) { + mBuff = (char *)it.get_data(); + } else { + ALOGI("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + + if (*mHandler.on_subsystem_restart) { + (*mHandler.on_subsystem_restart)(mBuff); + ALOGI("Hang event received. Trigger SSR handler:%p", + mHandler.on_subsystem_restart); + } else { + ALOGI("No Restart handler registered"); + } + } + return NL_OK; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +class SubSystemRestart : public WifiCommand +{ + public: + SubSystemRestart(wifi_interface_handle iface) + : WifiCommand("SubSystemRestart", iface, 0) + { } + + int createRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, WIFI_SUBCMD_TRIGGER_SSR); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + request.attr_end(data); + return WIFI_SUCCESS; + } + + int create() { + WifiRequest request(familyId(), ifaceId()); + + int result = createRequest(request); + if (result < 0) { + ALOGE("Failed to create ssr request result = %d\n", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to register ssr response; result = %d\n", result); + } + return result; + } + + protected: + int handleResponse(WifiEvent& reply) { + /* Nothing to do on response! */ + return NL_OK; + } + + int handleEvent(WifiEvent& event) { + /* NO events to handle here! */ + return NL_SKIP; + } + +}; +/////////////////////////////////////////////////////////////////////////////// +class HalInit : public WifiCommand +{ + int mErrCode; + + public: + HalInit(wifi_interface_handle iface, int id) + : WifiCommand("HalInit", iface, id), mErrCode(0) + { } + + int start() { + ALOGE("Start Set Hal"); + WifiRequest request(familyId(), ifaceId()); + + int result = request.create(GOOGLE_OUI, LOGGER_SET_HAL_START); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to set hal start; result = %d", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to register set hal start response; result = %d", result); + } + return result; + } + + + virtual int cancel() { + ALOGE("Cancel: Stop Hal"); + WifiRequest request(familyId(), ifaceId()); + + int result = request.create(GOOGLE_OUI, LOGGER_HAL_STOP); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to stop hal ; result = %d", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to register set hal start response; result = %d", result); + } + wifi_unregister_cmd(wifiHandle(), id()); + ALOGV("Stop HAL Successfully Completed, mErrCode = %d\n", mErrCode); + return result; + } + + int preInit() { + ALOGE("Hal preInit"); + WifiRequest request(familyId(), ifaceId()); + + int result = request.create(GOOGLE_OUI, LOGGER_SET_HAL_START); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to set Hal preInit; result = %d", result); + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_string(SET_HAL_START_ATTRIBUTE_PRE_INIT, (char *)HAL_VERSION); + if (result != WIFI_SUCCESS) { + ALOGE("Hal preInit Failed to put data= %d", result); + return result; + } + request.attr_end(data); + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to register set Hal preInit; result = %d", result); + } + return result; + } + + virtual int handleResponse(WifiEvent& reply) { + ALOGE("In SetHalStarted::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + return NL_OK; + } + + virtual int handleEvent(WifiEvent& event) { + /* NO events! */ + return NL_SKIP; + } +}; + +#ifdef RING_DUMP +/////////////////////////////////////////////////////////////////////////////// +class RingDump : public WifiCommand +{ + int mLargestBuffSize; + char *mBuff; + int mErrCode; + int mNumMaps; + wifi_buf_ring_map_entry_t *mMap; + int attr_type_len[DUMP_EVENT_ATTR_MAX]; + char *ring_name[DUMP_BUF_ATTR_MAX]; + wifi_ring_buffer_data_handler mHandle; + +public: + RingDump(wifi_interface_handle iface, int id, int num_maps, wifi_buf_ring_map_entry_t *map, + wifi_ring_buffer_data_handler ring_handle) + : WifiCommand("RingDump", iface, id), mLargestBuffSize(0), mBuff(NULL), + mErrCode(0), mNumMaps(num_maps), mMap(map), mHandle(ring_handle) + { + memset(attr_type_len, 0, sizeof(attr_type_len)); + for (int i = 0; i < DUMP_BUF_ATTR_MAX; i++) { + ring_name[i] = NULL; + } + } + RingDump(wifi_interface_handle iface, int id) + : WifiCommand("RingDump", iface, id), mLargestBuffSize(0), mBuff(NULL), + mErrCode(0) + { + } + + int start() { + DUMP_INFO(("Start Ring Dump Map_cnt:%d\n", mNumMaps)); + registerVendorHandler(GOOGLE_OUI, GOOGLE_FILE_DUMP_EVENT); + + //Set ringname to buf hashmap + for (int i = 0; i < mNumMaps; i++) { + int type = mMap[i].type; + ring_name[type] = (char *)malloc(DBGRING_NAME_MAX); + memset(ring_name[type], 0, DBGRING_NAME_MAX); + memcpy(ring_name[type], mMap[i].ring_name, strlen(mMap[i].ring_name)); + DUMP_DEBUG(("Set ringname Buf:%s Ringname:%s len:%lu", + EWP_CmdAttrToString(type), ring_name[type], strlen(mMap[i].ring_name))); + } + return WIFI_SUCCESS; + } + + virtual int freeup() { + DUMP_DEBUG(("freeup:Enter\n")); + if (mBuff) { + free(mBuff); + mBuff = NULL; + DUMP_INFO(("freed allocated memory\n")); + } + return WIFI_SUCCESS; + } + + virtual int cancel() { + /* unregister file dump handler */ + unregisterVendorHandler(GOOGLE_OUI, GOOGLE_FILE_DUMP_EVENT); + wifi_unregister_cmd(wifiHandle(), id()); + + /* Free up the ring names allocated */ + for (u8 i = 0; i < DUMP_BUF_ATTR_MAX; i++) { + if (ring_name[i]) { + free(ring_name[i]); + ring_name[i] = NULL; + } + } + if (mBuff) { + free(mBuff); + } + + DUMP_INFO(("Stop Ring Dump Successfully Completed, mErrCode = %d\n", mErrCode)); + return WIFI_SUCCESS; + } + + virtual int handleResponse(WifiEvent& reply) { + DUMP_DEBUG(("RingDump::handleResponse\n")); + int buf_attr = DUMP_BUF_ATTR_INVALID; + int len_attr = DUMP_LEN_ATTR_INVALID; + int index = -1; + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in memory dump response; ignoring it"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + buf_attr = it.get_type(); + switch (buf_attr) { + case DUMP_BUF_ATTR_MEMDUMP: + case DUMP_BUF_ATTR_TIMESTAMP: + case DUMP_BUF_ATTR_ECNTRS: + case DUMP_BUF_ATTR_DHD_DUMP: + case DUMP_BUF_ATTR_EXT_TRAP: + case DUMP_BUF_ATTR_HEALTH_CHK: + case DUMP_BUF_ATTR_COOKIE: + case DUMP_BUF_ATTR_FLOWRING_DUMP: + case DUMP_BUF_ATTR_STATUS_LOG: + case DUMP_BUF_ATTR_RTT_LOG: { + if (it.get_u32()) { + ALOGE("Copying data to userspace failed, status = %d\n", it.get_u32()); + return WIFI_ERROR_UNKNOWN; + } + index = logger_attr_buffer_lookup(buf_attr); + if (index == -1) { + ALOGE("Invalid index. buf attr = %s\n", EWP_CmdAttrToString(buf_attr)); + return WIFI_ERROR_UNKNOWN; + } + len_attr = attr_lookup_tbl[index].attr_type; + if (len_attr == DUMP_EVENT_ATTR_MAX) { + ALOGE("Invalid len attr = %s\n", EWP_EventAttrToString(len_attr)); + return WIFI_ERROR_UNKNOWN; + } + if (!mBuff || attr_type_len[len_attr] <= 0) { + return WIFI_ERROR_UNKNOWN; + } + + if (!ring_name[buf_attr]) { + ALOGE("Not allocated buf attr = %s\n", EWP_CmdAttrToString(buf_attr)); + return WIFI_ERROR_UNKNOWN; + } + DUMP_INFO(("RingDump:: buf_attr:%s size = %d ring_name:%s\n", + EWP_CmdAttrToString(buf_attr), attr_type_len[len_attr], + ring_name[buf_attr])); + if (mHandle.on_ring_buffer_data) { + /* on_ring_buffer_data callback requires status memory + * so should pass status memory */ + wifi_ring_buffer_status status; + memset(&status, 0, sizeof(status)); + /* Skip msg header. Retrieved log */ + (*mHandle.on_ring_buffer_data)(ring_name[buf_attr], mBuff, + attr_type_len[len_attr], &status); + } + if (mBuff) { + memset(mBuff, 0, mLargestBuffSize); + } + break; + } + default: { + DUMP_DEBUG(("Ignoring invalid attribute buf_attr = %d, size = %d", + buf_attr, it.get_len())); + break; + } + } + } + return NL_OK; + } + + virtual int request_logger_dump(WifiRequest& request, + buf_data_t *buf, int len_attr) { + + int result = 0; + int buf_attr = DUMP_BUF_ATTR_INVALID; + int index = -1; + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + index = logger_attr_lookup(len_attr); + if (index == -1) { + ALOGE("Invalid index\n"); + return WIFI_ERROR_UNKNOWN; + } + buf_attr = attr_lookup_tbl[index].buf_attr; + + if (buf_attr != DUMP_BUF_ATTR_INVALID) { + result = request.put(buf_attr, buf, sizeof(buf_data_t)); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put get memory dump request; result = %d", result); + return result; + } + } else { + ALOGE("Invalid buf attr = %s, index = %d\n", + EWP_CmdAttrToString(buf_attr), index); + return WIFI_ERROR_UNKNOWN; + } + DUMP_INFO(("Trigger get dump for buf attr = %s\n", + EWP_CmdAttrToString(buf_attr))); + + request.attr_end(data); + return result; + } + + virtual int handleEvent(WifiEvent& event) { + mLargestBuffSize = 0; + mBuff = NULL; + memset(attr_type_len, 0, sizeof(attr_type_len)); + u8 i = 0; + int result = 0; + int mActualBuffSize = 0; + int index = -1; + + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = event.get_vendor_data_len(); + int event_id = event.get_vendor_subcmd(); + int req_attr_cnt = 0; + int req_attr[DUMP_EVENT_ATTR_MAX]; + int buf_attr = DUMP_BUF_ATTR_INVALID; + + if (vendor_data == NULL || len == 0) { + ALOGE("No Debug data found"); + return NL_SKIP; + } + DUMP_INFO(("Ring Dump handler. Got event: %d", event_id)); + + buf_data_t buf; + + memset(&buf, 0, sizeof(buf_data_t)); + buf.ver = 0; + buf.buf_threshold = 0; + + if (event_id == GOOGLE_FILE_DUMP_EVENT) { + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + int attr = it.get_type(); + switch (attr) { + case DUMP_LEN_ATTR_MEMDUMP: + case DUMP_LEN_ATTR_TIMESTAMP: + case DUMP_LEN_ATTR_ECNTRS: + case DUMP_LEN_ATTR_DHD_DUMP: + case DUMP_LEN_ATTR_EXT_TRAP: + case DUMP_LEN_ATTR_HEALTH_CHK: + case DUMP_LEN_ATTR_COOKIE: + case DUMP_LEN_ATTR_FLOWRING_DUMP: + case DUMP_LEN_ATTR_STATUS_LOG: + case DUMP_LEN_ATTR_RTT_LOG: { + mActualBuffSize = it.get_u32(); + DUMP_DEBUG(("len attr %s, len %d\n", + EWP_EventAttrToString(attr), mActualBuffSize)); + if (mActualBuffSize > mLargestBuffSize) + mLargestBuffSize = mActualBuffSize; + attr_type_len[attr] = mActualBuffSize; + + /* Store the order in which attributes are received + * so that file dump can be done in the same order + */ + req_attr[req_attr_cnt++] = attr; + break; + } + default: { + ALOGE("Ignoring invalid attribute type = %d, size = %d", + attr, it.get_len()); + break; + } + } + } + /* Allocation for the largest buffer size to use it recursively for other buf attr. */ + if (mLargestBuffSize) { + DUMP_INFO(("Max dump size: %d", mLargestBuffSize)); + mBuff = (char *)malloc(mLargestBuffSize); + if (!mBuff) { + ALOGE("Buffer allocation failed"); + return NL_SKIP; + } + memset(mBuff, 0, mLargestBuffSize); + } + + WifiRequest request(familyId(), ifaceId()); + result = request.create(GOOGLE_OUI, LOGGER_DEBUG_GET_DUMP); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to create get memory dump request; result = %d", result); + freeup(); + goto exit; + } + + /* Requesting logger dump for each received attr */ + for (i = 0; i < req_attr_cnt; i++) { + int attr = req_attr[i]; + + if (attr_type_len[attr] == 0) { + continue; + } + + index = logger_attr_lookup(attr); + buf_attr = attr_lookup_tbl[index].buf_attr; + if (!ring_name[buf_attr]) { + ALOGE("Failed to find ringname index:%d buf_attr:%d", index, buf_attr); + continue; + } + + buf.len = attr_type_len[attr]; + buf.data_buf[0] = mBuff; + DUMP_DEBUG(("buf len = %d, buf ptr= %p for attr = %s\n", + buf.len, buf.data_buf[0], EWP_EventAttrToString(attr))); + result = request_logger_dump(request, &buf, attr); + if (result != WIFI_SUCCESS) { + /* Proceeding further for other attributes */ + ALOGE("Failed to request the logger dump for attr = %s; result = %d", + EWP_EventAttrToString(attr), result); + continue; + } + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to register get memory dump response for attr = %s; result = %d", + EWP_EventAttrToString(attr), result); + /* Proceeding further for other attributes */ + continue; + } + } + + WifiRequest request2(familyId(), ifaceId()); + result = request2.create(GOOGLE_OUI, LOGGER_FILE_DUMP_DONE_IND); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to trigger dev close; result = %d", result); + freeup(); + goto exit; + } + requestResponse(request2); + freeup(); + } else { + ALOGE("dump event missing dump length attribute"); + return NL_SKIP; + } + exit: + if (result != WIFI_SUCCESS) { + return NL_SKIP; + } + return NL_OK; + } +}; +/////////////////////////////////////////////////////////////////////////////// +#endif /* RING_DUMP */ + +wifi_error wifi_start_hal(wifi_interface_handle iface) +{ + wifi_handle handle = getWifiHandle(iface); + ALOGV("HAL INIT start, handle = %p", handle); + + HalInit *cmd = new HalInit(iface, HAL_START_REQUEST_ID); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, HAL_START_REQUEST_ID, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle,HAL_START_REQUEST_ID); + cmd->releaseRef(); + return result; + } + return result; +} + +wifi_error wifi_hal_preInit(wifi_interface_handle iface) +{ + wifi_handle handle = getWifiHandle(iface); + ALOGV("wifi_hal_preInit, handle = %p", handle); + + HalInit *cmd = new HalInit(iface, HAL_START_REQUEST_ID); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, HAL_START_REQUEST_ID, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->preInit(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle,HAL_START_REQUEST_ID); + cmd->releaseRef(); + return result; + } + return result; +} + +#ifdef RING_DUMP +wifi_error wifi_start_ring_dump(wifi_interface_handle iface, + wifi_ring_buffer_data_handler ring_handle) +{ + wifi_handle handle = getWifiHandle(iface); + DUMP_INFO(("start ring dump, handle = %p", handle)); + wifi_buf_ring_map_entry_t map[DUMP_BUF_ATTR_MAX]; + unsigned int num_maps = DUMP_BUF_ATTR_MAX; + wifi_error result; + + /* Get mapping table from driver */ + DebugCommand *debug_cmd = new DebugCommand(iface, &num_maps, map, GET_BUF_RING_MAP); + NULL_CHECK_RETURN(debug_cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + result = (wifi_error)debug_cmd->start(); + debug_cmd->releaseRef(); + + /* Set ringname to corresponding buf attr */ + RingDump *cmd = new RingDump(iface, FILE_DUMP_REQUEST_ID, num_maps, map, ring_handle); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + result = wifi_register_cmd(handle, FILE_DUMP_REQUEST_ID, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, FILE_DUMP_REQUEST_ID); + cmd->releaseRef(); + return result; + } + return result; +} + +wifi_error wifi_stop_ring_dump(wifi_interface_handle iface) +{ + RingDump *cmd = new RingDump(iface, FILE_DUMP_REQUEST_ID); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + DUMP_INFO(("stop ring dump")); + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; +} +#endif /* RING_DUMP */ + +wifi_error wifi_stop_hal(wifi_interface_handle iface) +{ + HalInit *cmd = new HalInit(iface, HAL_START_REQUEST_ID); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; +} + + +wifi_error wifi_set_subsystem_restart_handler(wifi_handle handle, + wifi_subsystem_restart_handler handler) +{ + hal_info *info = NULL; + + info = (hal_info *)handle; + if (info == NULL) { + ALOGE("Could not find hal info\n"); + return WIFI_ERROR_UNKNOWN; + } + + SetRestartHandler *cmd = new SetRestartHandler(handle, HAL_RESTART_ID, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, HAL_RESTART_ID, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, HAL_RESTART_ID); + cmd->releaseRef(); + return result; + } + + /* Cache the handler to use it for trigger subsystem restart */ + ALOGI("Register SSR handler:%p", handler); + info->restart_handler = handler; + return result; +} + +wifi_error wifi_trigger_subsystem_restart(wifi_handle handle) +{ + wifi_error result = WIFI_SUCCESS; + hal_info *info = NULL; + char error_str[20]; + SubSystemRestart *cmd = NULL; + wifi_interface_handle *ifaceHandles = NULL; + wifi_interface_handle wlan0Handle; + int numIfaceHandles = 0; + + info = (hal_info *)handle; + if (handle == NULL || info == NULL) { + ALOGE("Could not find hal info\n"); + result = WIFI_ERROR_UNKNOWN; + goto exit; + } + + ALOGI("Trigger subsystem restart\n"); + + wlan0Handle = wifi_get_wlan_interface((wifi_handle)handle, ifaceHandles, numIfaceHandles); + + cmd = new SubSystemRestart(wlan0Handle); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + + result = (wifi_error)cmd->create(); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + strncpy(error_str, "WIFI_ERROR_UNKNOWN", sizeof(error_str)); + ALOGE("Failed to create SSR"); + goto exit; + } + + strncpy(error_str, "WIFI_SUCCESS", sizeof(error_str)); + +exit: + if (result == WIFI_SUCCESS) { + if (info->restart_handler.on_subsystem_restart) { + ALOGI("Trigger ssr handler registered handler:%p", + info->restart_handler.on_subsystem_restart); + (info->restart_handler.on_subsystem_restart)(error_str); + } else { + ALOGI("No trigger ssr handler registered"); + } + } + + return result; +} + +wifi_error wifi_set_alert_handler(wifi_request_id id, wifi_interface_handle iface, + wifi_alert_handler handler) +{ + wifi_handle handle = getWifiHandle(iface); + ALOGV("Alerthandler start, handle = %p", handle); + + SetAlertHandler *cmd = new SetAlertHandler(iface, id, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + +wifi_error wifi_reset_alert_handler(wifi_request_id id, wifi_interface_handle iface) +{ + wifi_handle handle = getWifiHandle(iface); + ALOGV("Alerthandler reset, wifi_request_id = %d, handle = %p", id, handle); + + if (id == -1) { + wifi_alert_handler handler; + memset(&handler, 0, sizeof(handler)); + + SetAlertHandler *cmd = new SetAlertHandler(iface, id, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + + return wifi_get_cancel_cmd(id, iface); +} + +/////////////////////////////////////////////////////////////////////////////// +class MemoryDumpCommand: public WifiCommand +{ + wifi_firmware_memory_dump_handler mHandler; + int mBuffSize; + char *mBuff; + +public: + MemoryDumpCommand(wifi_interface_handle iface, wifi_firmware_memory_dump_handler handler) + : WifiCommand("MemoryDumpCommand", iface, 0), mHandler(handler), mBuffSize(0), mBuff(NULL) + { } + + int start() { + ALOGD("Start memory dump command"); + WifiRequest request(familyId(), ifaceId()); + + int result = request.create(GOOGLE_OUI, LOGGER_TRIGGER_MEM_DUMP); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to create trigger fw memory dump request; result = %d", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to register trigger memory dump response; result = %d", result); + } + return result; + } + + virtual int handleResponse(WifiEvent& reply) { + ALOGD("In MemoryDumpCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + ALOGD("len = %d", len); + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in memory dump response; ignoring it"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == LOGGER_ATTRIBUTE_FW_DUMP_LEN) { + mBuffSize = it.get_u32(); + + if (mBuff) + free(mBuff); + mBuff = (char *)malloc(mBuffSize); + if (!mBuff) { + ALOGE("Buffer allocation failed"); + return NL_SKIP; + } + WifiRequest request(familyId(), ifaceId()); + int result = request.create(GOOGLE_OUI, LOGGER_GET_MEM_DUMP); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to create get memory dump request; result = %d", result); + free(mBuff); + return NL_SKIP; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(LOGGER_ATTRIBUTE_FW_DUMP_LEN, mBuffSize); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put get memory dump request; result = %d", result); + return result; + } + + result = request.put_u64(LOGGER_ATTRIBUTE_FW_DUMP_DATA, (uint64_t)mBuff); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put get memory dump request; result = %d", result); + return result; + } + request.attr_end(data); + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to register get momory dump response; result = %d", result); + } + } else if (it.get_type() == LOGGER_ATTRIBUTE_FW_DUMP_DATA) { + ALOGI("Initiating memory dump callback"); + if (mHandler.on_firmware_memory_dump) { + (*mHandler.on_firmware_memory_dump)(mBuff, mBuffSize); + } + if (mBuff) { + free(mBuff); + mBuff = NULL; + } + } else { + ALOGW("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + return NL_OK; + } + + virtual int handleEvent(WifiEvent& event) { + /* NO events! */ + return NL_SKIP; + } +}; + +/* API to collect a firmware memory dump for a given iface */ +wifi_error wifi_get_firmware_memory_dump( wifi_interface_handle iface, + wifi_firmware_memory_dump_handler handler) +{ + MemoryDumpCommand *cmd = new MemoryDumpCommand(iface, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; +} + +class PacketFateCommand: public WifiCommand +{ + void *mReportBufs; + size_t mNoReqFates; + size_t *mNoProvidedFates; + PktFateReqType mReqType; + +public: + PacketFateCommand(wifi_interface_handle handle) + : WifiCommand("PacketFateCommand", handle, 0), mReqType(PACKET_MONITOR_START) + { + mReportBufs = NULL; + mNoReqFates = 0; + mNoProvidedFates = NULL; + } + + PacketFateCommand(wifi_interface_handle handle, wifi_tx_report *tx_report_bufs, + size_t n_requested_fates, size_t *n_provided_fates) + : WifiCommand("PacketFateCommand", handle, 0), mReportBufs(tx_report_bufs), + mNoReqFates(n_requested_fates), mNoProvidedFates(n_provided_fates), + mReqType(TX_PACKET_FATE) + { } + + PacketFateCommand(wifi_interface_handle handle, wifi_rx_report *rx_report_bufs, + size_t n_requested_fates, size_t *n_provided_fates) + : WifiCommand("PacketFateCommand", handle, 0), mReportBufs(rx_report_bufs), + mNoReqFates(n_requested_fates), mNoProvidedFates(n_provided_fates), + mReqType(RX_PACKET_FATE) + { } + + int createRequest(WifiRequest& request) { + if (mReqType == TX_PACKET_FATE) { + ALOGD("%s Get Tx packet fate request\n", __FUNCTION__); + return createTxPktFateRequest(request); + } else if (mReqType == RX_PACKET_FATE) { + ALOGD("%s Get Rx packet fate request\n", __FUNCTION__); + return createRxPktFateRequest(request); + } else if (mReqType == PACKET_MONITOR_START) { + ALOGD("%s Monitor packet fate request\n", __FUNCTION__); + return createMonitorPktFateRequest(request); + } else { + ALOGE("%s Unknown packet fate request\n", __FUNCTION__); + return WIFI_ERROR_NOT_SUPPORTED; + } + return WIFI_SUCCESS; + } + + int createMonitorPktFateRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, LOGGER_START_PKT_FATE_MONITORING); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + request.attr_end(data); + return result; + } + + int createTxPktFateRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, LOGGER_GET_TX_PKT_FATES); + if (result < 0) { + return result; + } + + memset(mReportBufs, 0, (mNoReqFates * sizeof(wifi_tx_report))); + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(LOGGER_ATTRIBUTE_PKT_FATE_NUM, mNoReqFates); + if (result < 0) { + return result; + } + result = request.put_u64(LOGGER_ATTRIBUTE_PKT_FATE_DATA, (uint64_t)mReportBufs); + if (result < 0) { + return result; + } + request.attr_end(data); + return result; + } + + int createRxPktFateRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, LOGGER_GET_RX_PKT_FATES); + if (result < 0) { + return result; + } + + memset(mReportBufs, 0, (mNoReqFates * sizeof(wifi_rx_report))); + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(LOGGER_ATTRIBUTE_PKT_FATE_NUM, mNoReqFates); + if (result < 0) { + return result; + } + result = request.put_u64(LOGGER_ATTRIBUTE_PKT_FATE_DATA, (uint64_t)mReportBufs); + if (result < 0) { + return result; + } + request.attr_end(data); + return result; + } + + int start() { + ALOGD("Start get packet fate command\n"); + WifiRequest request(familyId(), ifaceId()); + + int result = createRequest(request); + if (result < 0) { + ALOGE("Failed to create get pkt fate request; result = %d\n", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to register get pkt fate response; result = %d\n", result); + } + return result; + } + + int handleResponse(WifiEvent& reply) { + ALOGD("In GetPktFateCommand::handleResponse\n"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGI("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + ALOGI("Id = %0x, subcmd = %d, len = %d", id, subcmd, len); + + if (mReqType == TX_PACKET_FATE) { + ALOGI("Response received for get TX pkt fate command\n"); + } else if (mReqType == RX_PACKET_FATE) { + ALOGI("Response received for get RX pkt fate command\n"); + } else if (mReqType == PACKET_MONITOR_START) { + ALOGI("Response received for monitor pkt fate command\n"); + return NL_OK; + } else { + ALOGE("Response received for unknown pkt fate command\n"); + return NL_SKIP; + } + + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in GetPktFateCommand response; ignoring it\n"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == LOGGER_ATTRIBUTE_PKT_FATE_NUM) { + *mNoProvidedFates = it.get_u32(); + ALOGI("No: of pkt fates provided is %zu\n", *mNoProvidedFates); + } else { + ALOGE("Ignoring invalid attribute type = %d, size = %d\n", + it.get_type(), it.get_len()); + } + } + + return NL_OK; + } + + int handleEvent(WifiEvent& event) { + /* NO events to handle here! */ + return NL_SKIP; + } +}; + +class GetWakeReasonCountCommand : public WifiCommand { + WLAN_DRIVER_WAKE_REASON_CNT *mWakeReasonCnt; + void *mCmdEventWakeCount; + public: + GetWakeReasonCountCommand(wifi_interface_handle handle, + WLAN_DRIVER_WAKE_REASON_CNT *wlanDriverWakeReasonCount) : + WifiCommand("GetWakeReasonCountCommand", handle, 0), + mWakeReasonCnt(wlanDriverWakeReasonCount) + { + mCmdEventWakeCount = mWakeReasonCnt->cmd_event_wake_cnt; + } + + int createRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, LOGGER_GET_WAKE_REASON_STATS); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + request.attr_end(data); + return WIFI_SUCCESS; + } + + int start() { + ALOGD("Start get wake stats command\n"); + WifiRequest request(familyId(), ifaceId()); + + int result = createRequest(request); + if (result < 0) { + ALOGE("Failed to create request result = %d\n", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to register wake stats response; result = %d\n", result); + } + return result; + } + + protected: + int handleResponse(WifiEvent& reply) { + ALOGE("In GetWakeReasonCountCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + ALOGV("Id = %0x, subcmd = %d, len = %d", id, subcmd, len); + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in GetGetWakeReasonCountCommand response; ignoring it"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + switch (it.get_type()) { + case WAKE_STAT_ATTRIBUTE_TOTAL_DRIVER_FW: + mWakeReasonCnt->total_driver_fw_local_wake = + it.get_u32(); + break; + case WAKE_STAT_ATTRIBUTE_TOTAL: + mWakeReasonCnt->total_cmd_event_wake = + it.get_u32(); + break; + case WAKE_STAT_ATTRIBUTE_CMD_COUNT_USED: + mWakeReasonCnt->cmd_event_wake_cnt_used = + it.get_u32(); + break; + case WAKE_STAT_ATTRIBUTE_WAKE: + memcpy(mCmdEventWakeCount, it.get_data(), + (mWakeReasonCnt->cmd_event_wake_cnt_used * sizeof(int))); + break; + case WAKE_STAT_ATTRIBUTE_TOTAL_RX_DATA_WAKE: + mWakeReasonCnt->total_rx_data_wake = + it.get_u32(); + break; + case WAKE_STAT_ATTRIBUTE_RX_UNICAST_COUNT: + mWakeReasonCnt->rx_wake_details.rx_unicast_cnt = + it.get_u32(); + break; + case WAKE_STAT_ATTRIBUTE_RX_MULTICAST_COUNT: + mWakeReasonCnt->rx_wake_details.rx_multicast_cnt = + it.get_u32(); + break; + case WAKE_STAT_ATTRIBUTE_RX_BROADCAST_COUNT: + mWakeReasonCnt->rx_wake_details.rx_broadcast_cnt = + it.get_u32(); + break; + case WAKE_STAT_ATTRIBUTE_RX_ICMP_PKT: + mWakeReasonCnt->rx_wake_pkt_classification_info.icmp_pkt = + it.get_u32(); + break; + case WAKE_STAT_ATTRIBUTE_RX_ICMP6_PKT: + mWakeReasonCnt->rx_wake_pkt_classification_info.icmp6_pkt = + it.get_u32(); + break; + case WAKE_STAT_ATTRIBUTE_RX_ICMP6_RA: + mWakeReasonCnt->rx_wake_pkt_classification_info.icmp6_ra = + it.get_u32(); + break; + case WAKE_STAT_ATTRIBUTE_RX_ICMP6_NA: + mWakeReasonCnt->rx_wake_pkt_classification_info.icmp6_na = + it.get_u32(); + break; + case WAKE_STAT_ATTRIBUTE_RX_ICMP6_NS: + mWakeReasonCnt->rx_wake_pkt_classification_info.icmp6_ns = + it.get_u32(); + break; + case WAKE_STAT_ATTRIBUTE_IPV4_RX_MULTICAST_ADD_CNT: + mWakeReasonCnt->rx_multicast_wake_pkt_info.ipv4_rx_multicast_addr_cnt = + it.get_u32(); + break; + case WAKE_STAT_ATTRIBUTE_IPV6_RX_MULTICAST_ADD_CNT: + mWakeReasonCnt->rx_multicast_wake_pkt_info.ipv6_rx_multicast_addr_cnt = + it.get_u32(); + break; + case WAKE_STAT_ATTRIBUTE_OTHER_RX_MULTICAST_ADD_CNT: + mWakeReasonCnt->rx_multicast_wake_pkt_info.other_rx_multicast_addr_cnt = + it.get_u32(); + break; + default: + break; + } + + } + return NL_OK; + } +}; + +wifi_error wifi_start_pkt_fate_monitoring(wifi_interface_handle handle) +{ + PacketFateCommand *cmd = new PacketFateCommand(handle); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; +} + +wifi_error wifi_get_tx_pkt_fates(wifi_interface_handle handle, + wifi_tx_report *tx_report_bufs, size_t n_requested_fates, + size_t *n_provided_fates) +{ + PacketFateCommand *cmd = new PacketFateCommand(handle, tx_report_bufs, + n_requested_fates, n_provided_fates); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; +} + +wifi_error wifi_get_rx_pkt_fates(wifi_interface_handle handle, + wifi_rx_report *rx_report_bufs, size_t n_requested_fates, + size_t *n_provided_fates) +{ + PacketFateCommand *cmd = new PacketFateCommand(handle, rx_report_bufs, + n_requested_fates, n_provided_fates); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; +} + +wifi_error wifi_get_wake_reason_stats(wifi_interface_handle handle, + WLAN_DRIVER_WAKE_REASON_CNT *wifi_wake_reason_cnt) +{ + GetWakeReasonCountCommand *cmd = + new GetWakeReasonCountCommand(handle, wifi_wake_reason_cnt); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +class OtaUpdateCommand : public WifiCommand +{ + + public: + OtaUpdateCommand(wifi_interface_handle iface) + : WifiCommand("OtaUpdateCommand", iface, 0) + { } + + int start() { + ALOGE("Start OtaUpdateCommand"); + WifiRequest request(familyId(), ifaceId()); + + int result = request.create(GOOGLE_OUI, WIFI_SUBCMD_GET_OTA_CURRUNT_INFO); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to set hal start; result = %d", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to register set hal start response; result = %d", result); + } + return result; + } + + int otaDownload(ota_info_buf_t* buf, uint32_t ota_version) { + u32 force_reg_on = false; + WifiRequest request(familyId(), ifaceId()); + int result = request.create(GOOGLE_OUI, WIFI_SUBCMD_OTA_UPDATE); + + ALOGE("Download the OTA configuration"); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to set Hal preInit; result = %d", result); + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + result = request.put_u32(OTA_DOWNLOAD_CLM_LENGTH_ATTR, buf->ota_clm_len); + if (result != WIFI_SUCCESS) { + ALOGE("otaDownload Failed to put data= %d", result); + return result; + } + + result = request.put(OTA_DOWNLOAD_CLM_ATTR, buf->ota_clm_buf, sizeof(*buf->ota_clm_buf)); + if (result != WIFI_SUCCESS) { + ALOGE("otaDownload Failed to put data= %d", result); + return result; + } + + result = request.put_u32(OTA_DOWNLOAD_NVRAM_LENGTH_ATTR, buf->ota_nvram_len); + if (result != WIFI_SUCCESS) { + ALOGE("otaDownload Failed to put data= %d", result); + return result; + } + + result = request.put(OTA_DOWNLOAD_NVRAM_ATTR, + buf->ota_nvram_buf, sizeof(*buf->ota_nvram_buf)); + if (result != WIFI_SUCCESS) { + ALOGE("otaDownload Failed to put data= %d", result); + return result; + } + + if (applied_ota_version != ota_version) { + force_reg_on = true; + applied_ota_version = ota_version; + } + result = request.put_u32(OTA_SET_FORCE_REG_ON, force_reg_on); + if (result != WIFI_SUCCESS) { + ALOGE("otaDownload Failed to put data= %d", result); + return result; + } + + request.attr_end(data); + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to register set otaDownload; result = %d", result); + } + + return result; + } + + virtual int handleResponse(WifiEvent& reply) { + ALOGD("In OtaUpdateCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + ALOGI("Id = %0x, subcmd = %d, len = %d", id, subcmd, len); + + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in GetPktFateCommand response; ignoring it\n"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + switch (it.get_type()) { + case OTA_CUR_NVRAM_EXT_ATTR: + strncpy(ota_nvram_ext, (char*)it.get_string(), it.get_len()); + ALOGI("Current Nvram ext [%s]\n", ota_nvram_ext); + break; + default: + ALOGE("Ignoring invalid attribute type = %d, size = %d\n", + it.get_type(), it.get_len()); + break; + } + } + return NL_OK; + } + + virtual int handleEvent(WifiEvent& event) { + /* NO events! */ + return NL_SKIP; + } +}; + +wifi_error read_ota_file(char* file, char** buffer, uint32_t* size) +{ + FILE* fp = NULL; + int file_size; + char* buf; + fp = fopen(file, "r"); + + if (fp == NULL) { + ALOGI("File [%s] doesn't exist.", file); + return WIFI_ERROR_NOT_AVAILABLE; + } + + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + + buf = (char *)malloc(file_size + 1); + if (buf == NULL) { + fclose(fp); + return WIFI_ERROR_UNKNOWN; + } + memset(buf, 0, file_size + 1); + fseek(fp, 0, SEEK_SET); + fread(buf, file_size, 1, fp); + + *buffer = (char*) buf; + *size = file_size; + fclose(fp); + return WIFI_SUCCESS; +} + +wifi_error check_multiple_nvram_clm(uint32_t type, char* hw_revision, char* hw_sku, + char** buffer, uint32_t* buffer_len) +{ + char file_name[MAX_NV_FILE][FILE_NAME_LEN]; + char nvram_clmblob_default_file[FILE_NAME_LEN] = {0,}; + wifi_error result = WIFI_SUCCESS; + + if (type == CLM_BLOB) { + sprintf(nvram_clmblob_default_file, "%s%s", OTA_PATH, OTA_CLM_FILE); + } + else if (type == NVRAM) { + sprintf(nvram_clmblob_default_file, "%s%s", OTA_PATH, OTA_NVRAM_FILE); + } + for (unsigned int i = 0; i < MAX_NV_FILE; i++) { + memset(file_name[i], 0, FILE_NAME_LEN); + } + + sprintf(file_name[0], "%s_%s_%s", nvram_clmblob_default_file, hw_revision, hw_sku); + sprintf(file_name[1], "%s_%s", nvram_clmblob_default_file, hw_revision); + sprintf(file_name[2], "%s_%s", nvram_clmblob_default_file, hw_sku); + sprintf(file_name[3], "%s", nvram_clmblob_default_file); + + for (unsigned int i = 0; i < MAX_NV_FILE; i++) { + result = read_ota_file(file_name[i], buffer, buffer_len); + if (result == WIFI_SUCCESS) { + ALOGI("[OTA] %s PATH %s", type == NVRAM ? "NVRAM" : "CLM", file_name[i]); + break; + } + } + return result; +} + +wifi_error wifi_hal_ota_update(wifi_interface_handle iface, uint32_t ota_version) +{ + wifi_handle handle = getWifiHandle(iface); + wifi_error result = WIFI_SUCCESS; + ota_info_buf_t buf; + char *buffer_nvram = NULL; + char *buffer_clm = NULL; + char prop_revision_buf[PROPERTY_VALUE_MAX] = {0,}; + char prop_sku_buf[PROPERTY_VALUE_MAX] = {0,}; + char sku_name[MAX_SKU_NAME_LEN] = {0,}; + + OtaUpdateCommand *cmd = new OtaUpdateCommand(iface); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + + ALOGD("wifi_hal_ota_update, handle = %p, ota_version %d\n", handle, ota_version); + + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + + property_get(HW_DEV_PROP, prop_revision_buf, NULL); + property_get(HW_SKU_PROP, prop_sku_buf, NULL); + + strncpy(sku_name, "NA", MAX_SKU_NAME_LEN); + for (int i = 0; i < ARRAYSIZE(sku_table); i ++) { + if (strcmp(prop_sku_buf, sku_table[i].hw_id) == 0) { + strncpy(sku_name, sku_table[i].sku, MAX_SKU_NAME_LEN); + break; + } + } + ALOGD("prop_sku_buf is %s, sku_name is %s", prop_sku_buf, sku_name); + + check_multiple_nvram_clm(CLM_BLOB, prop_revision_buf, sku_name, &buffer_clm, &buf.ota_clm_len); + if (buffer_clm == NULL) { + ALOGE("buffer_clm is null"); + goto exit; + } + buf.ota_clm_buf[0] = buffer_clm; + + check_multiple_nvram_clm(NVRAM, prop_revision_buf, sku_name, + &buffer_nvram, &buf.ota_nvram_len); + if (buffer_nvram == NULL) { + ALOGE("buffer_nvram is null"); + goto exit; + } + buf.ota_nvram_buf[0] = buffer_nvram; + cmd->otaDownload(&buf, ota_version); + +exit: + if (buffer_clm != NULL) { + free(buffer_clm); + } + if (buffer_nvram != NULL) { + free(buffer_nvram); + } + + cmd->releaseRef(); + + return result; +} diff --git a/synadhd/wifi_hal/wifi_offload.cpp b/synadhd/wifi_hal/wifi_offload.cpp new file mode 100644 index 0000000..1702ffd --- /dev/null +++ b/synadhd/wifi_hal/wifi_offload.cpp @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * 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 <stdint.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/errqueue.h> + +#include <linux/pkt_sched.h> +#include <netlink/object-api.h> +#include <netlink/netlink.h> +#include <netlink/socket.h> +#include <netlink-private/object-api.h> +#include <netlink-private/types.h> + + +#include "nl80211_copy.h" +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include <log/log.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +typedef enum { + WIFI_OFFLOAD_START_MKEEP_ALIVE = ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START, + WIFI_OFFLOAD_STOP_MKEEP_ALIVE, +} WIFI_OFFLOAD_SUB_COMMAND; + +typedef enum { + MKEEP_ALIVE_ATTRIBUTE_INVALID = 0, + MKEEP_ALIVE_ATTRIBUTE_ID = 1, + MKEEP_ALIVE_ATTRIBUTE_IP_PKT = 2, + MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN = 3, + MKEEP_ALIVE_ATTRIBUTE_SRC_MAC_ADDR = 4, + MKEEP_ALIVE_ATTRIBUTE_DST_MAC_ADDR = 5, + MKEEP_ALIVE_ATTRIBUTE_PERIOD_MSEC = 6, + MKEEP_ALIVE_ATTRIBUTE_ETHER_TYPE = 7, + /* Add new attributes just above this */ + MKEEP_ALIVE_ATTRIBUTE_MAX +} WIFI_MKEEP_ALIVE_ATTRIBUTE; + +typedef enum { + START_MKEEP_ALIVE, + STOP_MKEEP_ALIVE, +} GetCmdType; + +/////////////////////////////////////////////////////////////////////////////// +class MKeepAliveCommand : public WifiCommand +{ + u8 mIndex; + u8 *mIpPkt; + u16 mIpPktLen; + u8 *mSrcMacAddr; + u8 *mDstMacAddr; + u32 mPeriodMsec; + GetCmdType mType; + u16 mEther_type; + +public: + + // constructor for start sending + MKeepAliveCommand(wifi_interface_handle iface, u8 index, u16 ether_type, u8 *ip_packet, u16 ip_packet_len, + u8 *src_mac_addr, u8 *dst_mac_addr, u32 period_msec, GetCmdType cmdType) + : WifiCommand("MKeepAliveCommand", iface, 0), mIndex(index), mEther_type(ether_type), mIpPkt(ip_packet), + mIpPktLen(ip_packet_len), mSrcMacAddr(src_mac_addr), mDstMacAddr(dst_mac_addr), + mPeriodMsec(period_msec), mType(cmdType) + { } + + // constructor for stop sending + MKeepAliveCommand(wifi_interface_handle iface, u8 index, GetCmdType cmdType) + : WifiCommand("MKeepAliveCommand", iface, 0), mIndex(index), mType(cmdType) + { + mIpPkt = NULL; + mIpPktLen = 0; + mSrcMacAddr = NULL; + mDstMacAddr = NULL; + mPeriodMsec = 0; + mEther_type = 0; + } + + int createRequest(WifiRequest &request) { + int result = WIFI_SUCCESS; + + switch (mType) { + case START_MKEEP_ALIVE: + { + result = request.create(GOOGLE_OUI, WIFI_OFFLOAD_START_MKEEP_ALIVE); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to create start keep alive request; result = %d", result); + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + result = request.put_u8(MKEEP_ALIVE_ATTRIBUTE_ID, mIndex); + if (result < 0) { + ALOGE("Failed to put id request; result = %d", result); + return result; + } + + result = request.put_u16(MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN, mIpPktLen); + if (result < 0) { + ALOGE("Failed to put ip pkt len request; result = %d", result); + return result; + } + + result = request.put(MKEEP_ALIVE_ATTRIBUTE_IP_PKT, (u8*)mIpPkt, mIpPktLen); + if (result < 0) { + ALOGE("Failed to put ip pkt request; result = %d", result); + return result; + } + + result = request.put_addr(MKEEP_ALIVE_ATTRIBUTE_SRC_MAC_ADDR, mSrcMacAddr); + if (result < 0) { + ALOGE("Failed to put src mac address request; result = %d", result); + return result; + } + + result = request.put_addr(MKEEP_ALIVE_ATTRIBUTE_DST_MAC_ADDR, mDstMacAddr); + if (result < 0) { + ALOGE("Failed to put dst mac address request; result = %d", result); + return result; + } + + result = request.put_u32(MKEEP_ALIVE_ATTRIBUTE_PERIOD_MSEC, mPeriodMsec); + if (result < 0) { + ALOGE("Failed to put period request; result = %d", result); + return result; + } + result = request.put_u16(MKEEP_ALIVE_ATTRIBUTE_ETHER_TYPE, mEther_type); + if (result < 0) { + ALOGE("Failed to put ether type; result = %d", result); + return result; + } + + request.attr_end(data); + break; + } + + case STOP_MKEEP_ALIVE: + { + result = request.create(GOOGLE_OUI, WIFI_OFFLOAD_STOP_MKEEP_ALIVE); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to create stop keep alive request; result = %d", result); + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + result = request.put_u8(MKEEP_ALIVE_ATTRIBUTE_ID, mIndex); + if (result < 0) { + ALOGE("Failed to put id request; result = %d", result); + return result; + } + + request.attr_end(data); + break; + } + + default: + ALOGE("Unknown wifi keep alive command"); + result = WIFI_ERROR_UNKNOWN; + } + return result; + } + + int start() { + ALOGD("Start mkeep_alive command"); + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to create keep alive request; result = %d", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to register keep alive response; result = %d", result); + } + return result; + } + + virtual int handleResponse(WifiEvent& reply) { + ALOGD("In MKeepAliveCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + switch (mType) { + case START_MKEEP_ALIVE: + case STOP_MKEEP_ALIVE: + break; + + default: + ALOGW("Unknown mkeep_alive command"); + } + return NL_OK; + } + + virtual int handleEvent(WifiEvent& event) { + /* NO events! */ + return NL_SKIP; + } +}; + + +/* API to send specified mkeep_alive packet periodically. */ +wifi_error wifi_start_sending_offloaded_packet(wifi_request_id index, wifi_interface_handle iface, + u16 ether_type, u8 *ip_packet, u16 ip_packet_len, u8 *src_mac_addr, u8 *dst_mac_addr, + u32 period_msec) +{ + if ((index > 0 && index <= N_AVAIL_ID) && (ip_packet != NULL) && (src_mac_addr != NULL) + && (dst_mac_addr != NULL) && (period_msec > 0) + && (ip_packet_len <= MKEEP_ALIVE_IP_PKT_MAX) && ((ether_type == ETHERTYPE_IP) || + (ether_type == ETHERTYPE_IPV6))) { + MKeepAliveCommand *cmd = new MKeepAliveCommand(iface, index, ether_type, ip_packet, ip_packet_len, + src_mac_addr, dst_mac_addr, period_msec, START_MKEEP_ALIVE); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; + } else { + ALOGE("Invalid mkeep_alive parameters"); + return WIFI_ERROR_INVALID_ARGS; + } +} + +/* API to stop sending mkeep_alive packet. */ +wifi_error wifi_stop_sending_offloaded_packet(wifi_request_id index, wifi_interface_handle iface) +{ + if (index > 0 && index <= N_AVAIL_ID) { + MKeepAliveCommand *cmd = new MKeepAliveCommand(iface, index, STOP_MKEEP_ALIVE); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; + } else { + ALOGE("Invalid mkeep_alive parameters"); + return WIFI_ERROR_INVALID_ARGS; + } +} diff --git a/synadhd/wpa_supplicant_8_lib/Android.mk b/synadhd/wpa_supplicant_8_lib/Android.mk new file mode 100644 index 0000000..d1f8e55 --- /dev/null +++ b/synadhd/wpa_supplicant_8_lib/Android.mk @@ -0,0 +1,81 @@ +# +# Copyright (C) 2008 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. +# +LOCAL_PATH := $(call my-dir) + +ifeq ($(WPA_SUPPLICANT_VERSION),VER_0_8_X) + +ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),) + CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y +endif + +WPA_SUPPL_DIR = external/wpa_supplicant_8 +WPA_SRC_FILE := + +include $(WPA_SUPPL_DIR)/wpa_supplicant/android.config + +WPA_SUPPL_DIR_INCLUDE = $(WPA_SUPPL_DIR)/src \ + $(WPA_SUPPL_DIR)/src/common \ + $(WPA_SUPPL_DIR)/src/drivers \ + $(WPA_SUPPL_DIR)/src/l2_packet \ + $(WPA_SUPPL_DIR)/src/utils \ + $(WPA_SUPPL_DIR)/src/wps \ + $(WPA_SUPPL_DIR)/wpa_supplicant + +ifdef CONFIG_DRIVER_NL80211 +WPA_SUPPL_DIR_INCLUDE += external/libnl/include +WPA_SRC_FILE += driver_cmd_nl80211.c +endif + +ifdef CONFIG_DRIVER_WEXT +WPA_SRC_FILE += driver_cmd_wext.c +endif + +ifeq ($(TARGET_ARCH),arm) +# To force sizeof(enum) = 4 +L_CFLAGS += -mabi=aapcs-linux +endif + +ifdef CONFIG_ANDROID_LOG +L_CFLAGS += -DCONFIG_ANDROID_LOG +endif + +ifdef CONFIG_P2P +L_CFLAGS += -DCONFIG_P2P +endif + +ifeq ($(TARGET_USES_64_BIT_BCMDHD),true) +L_CFLAGS += -DBCMDHD_64_BIT_IPC +endif + +L_CFLAGS += -Wall -Werror -Wno-unused-parameter -Wno-macro-redefined + +######################## + +include $(CLEAR_VARS) +LOCAL_MODULE := lib_driver_cmd_synadhd +LOCAL_LICENSE_KINDS := SPDX-license-identifier-BSD +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE +LOCAL_SHARED_LIBRARIES := libc libcutils +LOCAL_CFLAGS := $(L_CFLAGS) +LOCAL_SRC_FILES := $(WPA_SRC_FILE) +LOCAL_C_INCLUDES := $(WPA_SUPPL_DIR_INCLUDE) +LOCAL_VENDOR_MODULE := true +include $(BUILD_STATIC_LIBRARY) + +######################## + +endif diff --git a/synadhd/wpa_supplicant_8_lib/MODULE_LICENSE_BSD b/synadhd/wpa_supplicant_8_lib/MODULE_LICENSE_BSD new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/synadhd/wpa_supplicant_8_lib/MODULE_LICENSE_BSD diff --git a/synadhd/wpa_supplicant_8_lib/NOTICE b/synadhd/wpa_supplicant_8_lib/NOTICE new file mode 100644 index 0000000..f9d25ea --- /dev/null +++ b/synadhd/wpa_supplicant_8_lib/NOTICE @@ -0,0 +1,43 @@ + +Copyright (c) 2005-2010, The Android Open Source Project +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 Android Open Source Project 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE 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. + + * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net> + * Copyright (c) 2009-2010, Atheros Communications + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. diff --git a/synadhd/wpa_supplicant_8_lib/driver_cmd_nl80211.c b/synadhd/wpa_supplicant_8_lib/driver_cmd_nl80211.c new file mode 100644 index 0000000..f9dbb95 --- /dev/null +++ b/synadhd/wpa_supplicant_8_lib/driver_cmd_nl80211.c @@ -0,0 +1,215 @@ +/* + * Driver interaction with extended Linux CFG8021 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + */ + +#include "includes.h" +#include <sys/types.h> +#include <fcntl.h> +#include <net/if.h> + +#include "common.h" +#include "linux_ioctl.h" +#include "driver_nl80211.h" +#include "wpa_supplicant_i.h" +#include "config.h" +#ifdef ANDROID +#include "android_drv.h" +#endif + +typedef struct android_wifi_priv_cmd { +#ifdef BCMDHD_64_BIT_IPC + u64 bufaddr; +#else + char *bufaddr; +#endif + int used_len; + int total_len; +} android_wifi_priv_cmd; + +static int drv_errors = 0; + +static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv) +{ + drv_errors++; + if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { + drv_errors = 0; + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); + } +} + +static void wpa_driver_notify_country_change(void *ctx, char *cmd) +{ + if ((os_strncasecmp(cmd, "COUNTRY", 7) == 0) || + (os_strncasecmp(cmd, "SETBAND", 7) == 0)) { + union wpa_event_data event; + + os_memset(&event, 0, sizeof(event)); + event.channel_list_changed.initiator = REGDOM_SET_BY_USER; + if (os_strncasecmp(cmd, "COUNTRY", 7) == 0) { + event.channel_list_changed.type = REGDOM_TYPE_COUNTRY; + if (os_strlen(cmd) > 9) { + event.channel_list_changed.alpha2[0] = cmd[8]; + event.channel_list_changed.alpha2[1] = cmd[9]; + } + } else { + event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN; + } + wpa_supplicant_event(ctx, EVENT_CHANNEL_LIST_CHANGED, &event); + } +} + +int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf, + size_t buf_len ) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ifreq ifr; + android_wifi_priv_cmd priv_cmd; + int ret = 0; + + if (bss->ifindex <= 0 && bss->wdev_id > 0) { + /* DRIVER CMD received on the DEDICATED P2P Interface which doesn't + * have an NETDEVICE associated with it. So we have to re-route the + * command to the parent NETDEVICE + */ + struct wpa_supplicant *wpa_s = (struct wpa_supplicant *)(drv->ctx); + + wpa_printf(MSG_DEBUG, "Re-routing DRIVER cmd to parent iface"); + if (wpa_s && wpa_s->parent) { + /* Update the nl80211 pointers corresponding to parent iface */ + bss = wpa_s->parent->drv_priv; + drv = bss->drv; + wpa_printf(MSG_DEBUG, "Re-routing command to iface: %s" + " cmd (%s)", bss->ifname, cmd); + } + } + + if (os_strcasecmp(cmd, "STOP") == 0) { + linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0); + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED"); + } else if (os_strcasecmp(cmd, "START") == 0) { + linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1); + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED"); + } else if (os_strcasecmp(cmd, "MACADDR") == 0) { + u8 macaddr[ETH_ALEN] = {}; + + ret = linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, macaddr); + if (!ret) + ret = os_snprintf(buf, buf_len, + "Macaddr = " MACSTR "\n", MAC2STR(macaddr)); + } else { /* Use private command */ + os_memcpy(buf, cmd, strlen(cmd) + 1); + memset(&ifr, 0, sizeof(ifr)); + memset(&priv_cmd, 0, sizeof(priv_cmd)); + os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); + +#ifdef BCMDHD_64_BIT_IPC + priv_cmd.bufaddr = (u64)(uintptr_t)buf; +#else + priv_cmd.bufaddr = buf; +#endif + priv_cmd.used_len = buf_len; + priv_cmd.total_len = buf_len; + ifr.ifr_data = &priv_cmd; + + if ((ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr)) < 0) { + wpa_printf(MSG_ERROR, "%s: failed to issue private command: %s", __func__, cmd); + wpa_driver_send_hang_msg(drv); + } else { + drv_errors = 0; + ret = 0; + if ((os_strcasecmp(cmd, "LINKSPEED") == 0) || + (os_strcasecmp(cmd, "RSSI") == 0) || + (os_strcasecmp(cmd, "GETBAND") == 0) || + (os_strncasecmp(cmd, "WLS_BATCHING", 12) == 0)) + ret = strlen(buf); + wpa_driver_notify_country_change(drv->ctx, cmd); + wpa_printf(MSG_DEBUG, "%s %s len = %d, %zu", __func__, buf, ret, strlen(buf)); + } + } + return ret; +} + +int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration) +{ + char buf[MAX_DRV_CMD_SIZE]; + + memset(buf, 0, sizeof(buf)); + wpa_printf(MSG_DEBUG, "%s: Entry", __func__); + snprintf(buf, sizeof(buf), "P2P_SET_NOA %d %d %d", count, start, duration); + return wpa_driver_nl80211_driver_cmd(priv, buf, buf, strlen(buf)+1); +} + +int wpa_driver_get_p2p_noa(void *priv __unused, u8 *buf __unused, size_t len __unused) +{ + /* Return 0 till we handle p2p_presence request completely in the driver */ + return 0; +} + +int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow) +{ + char buf[MAX_DRV_CMD_SIZE]; + + memset(buf, 0, sizeof(buf)); + wpa_printf(MSG_DEBUG, "%s: Entry", __func__); + snprintf(buf, sizeof(buf), "P2P_SET_PS %d %d %d", legacy_ps, opp_ps, ctwindow); + return wpa_driver_nl80211_driver_cmd(priv, buf, buf, strlen(buf) + 1); +} + +int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) +{ + char *buf; + const struct wpabuf *ap_wps_p2p_ie = NULL; + + char *_cmd = "SET_AP_WPS_P2P_IE"; + char *pbuf; + int ret = 0; + int i, buf_len; + struct cmd_desc { + int cmd; + const struct wpabuf *src; + } cmd_arr[] = { + {0x1, beacon}, + {0x2, proberesp}, + {0x4, assocresp}, + {-1, NULL} + }; + + wpa_printf(MSG_DEBUG, "%s: Entry", __func__); + for (i = 0; cmd_arr[i].cmd != -1; i++) { + ap_wps_p2p_ie = cmd_arr[i].src; + if (ap_wps_p2p_ie) { + buf_len = strlen(_cmd) + 3 + wpabuf_len(ap_wps_p2p_ie); + buf = os_zalloc(buf_len); + if (NULL == buf) { + wpa_printf(MSG_ERROR, "%s: Out of memory", + __func__); + ret = -1; + break; + } + } else { + continue; + } + pbuf = buf; + pbuf += snprintf(pbuf, buf_len - wpabuf_len(ap_wps_p2p_ie), + "%s %d",_cmd, cmd_arr[i].cmd); + *pbuf++ = '\0'; + os_memcpy(pbuf, wpabuf_head(ap_wps_p2p_ie), wpabuf_len(ap_wps_p2p_ie)); + ret = wpa_driver_nl80211_driver_cmd(priv, buf, buf, buf_len); + os_free(buf); + if (ret < 0) + break; + } + + return ret; +} diff --git a/synadhd/wpa_supplicant_8_lib/driver_cmd_wext.c b/synadhd/wpa_supplicant_8_lib/driver_cmd_wext.c new file mode 100644 index 0000000..3734cb0 --- /dev/null +++ b/synadhd/wpa_supplicant_8_lib/driver_cmd_wext.c @@ -0,0 +1,396 @@ +/* + * Driver interaction with extended Linux Wireless Extensions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <net/if_arp.h> +#include <net/if.h> + +#include "linux_wext.h" +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "priv_netlink.h" +#include "driver_wext.h" +#include "ieee802_11_defs.h" +#include "wpa_common.h" +#include "wpa_ctrl.h" +#include "wpa_supplicant_i.h" +#include "config.h" +#include "linux_ioctl.h" +#include "scan.h" + +#include "driver_cmd_wext.h" +#ifdef ANDROID +#include "android_drv.h" +#endif /* ANDROID */ + +#define RSSI_CMD "RSSI" +#define LINKSPEED_CMD "LINKSPEED" + +/** + * wpa_driver_wext_set_scan_timeout - Set scan timeout to report scan completion + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * + * This function can be used to set registered timeout when starting a scan to + * generate a scan completed event if the driver does not report this. + */ +static void wpa_driver_wext_set_scan_timeout(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + int timeout = 10; /* In case scan A and B bands it can be long */ + + /* Not all drivers generate "scan completed" wireless event, so try to + * read results after a timeout. */ + if (drv->scan_complete_events) { + /* + * The driver seems to deliver SIOCGIWSCAN events to notify + * when scan is complete, so use longer timeout to avoid race + * conditions with scanning and following association request. + */ + timeout = 30; + } + wpa_printf(MSG_DEBUG, "Scan requested - scan timeout %d seconds", + timeout); + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx); + eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv, + drv->ctx); +} + +/** + * wpa_driver_wext_combo_scan - Request the driver to initiate combo scan + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @params: Scan parameters + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_combo_scan(void *priv, struct wpa_driver_scan_params *params) +{ + char buf[WEXT_CSCAN_BUF_LEN]; + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret, bp; + unsigned i; + + if (!drv->driver_is_started) { + wpa_printf(MSG_DEBUG, "%s: Driver stopped", __func__); + return 0; + } + + wpa_printf(MSG_DEBUG, "%s: Start", __func__); + + /* Set list of SSIDs */ + bp = WEXT_CSCAN_HEADER_SIZE; + os_memcpy(buf, WEXT_CSCAN_HEADER, bp); + for(i=0; i < params->num_ssids; i++) { + if ((bp + IW_ESSID_MAX_SIZE + 10) >= (int)sizeof(buf)) + break; + wpa_printf(MSG_DEBUG, "For Scan: %s", params->ssids[i].ssid); + buf[bp++] = WEXT_CSCAN_SSID_SECTION; + buf[bp++] = params->ssids[i].ssid_len; + os_memcpy(&buf[bp], params->ssids[i].ssid, params->ssids[i].ssid_len); + bp += params->ssids[i].ssid_len; + } + + /* Set list of channels */ + buf[bp++] = WEXT_CSCAN_CHANNEL_SECTION; + buf[bp++] = 0; + + /* Set passive dwell time (default is 250) */ + buf[bp++] = WEXT_CSCAN_PASV_DWELL_SECTION; + buf[bp++] = (u8)WEXT_CSCAN_PASV_DWELL_TIME; + buf[bp++] = (u8)(WEXT_CSCAN_PASV_DWELL_TIME >> 8); + + /* Set home dwell time (default is 40) */ + buf[bp++] = WEXT_CSCAN_HOME_DWELL_SECTION; + buf[bp++] = (u8)WEXT_CSCAN_HOME_DWELL_TIME; + buf[bp++] = (u8)(WEXT_CSCAN_HOME_DWELL_TIME >> 8); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = buf; + iwr.u.data.length = bp; + + if ((ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr)) < 0) { + if (!drv->bgscan_enabled) + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (cscan): %d", ret); + else + ret = 0; /* Hide error in case of bg scan */ + } + return ret; +} + +static int wpa_driver_wext_set_cscan_params(char *buf, size_t buf_len, char *cmd) +{ + char *pasv_ptr; + int bp, i; + u16 pasv_dwell = WEXT_CSCAN_PASV_DWELL_TIME_DEF; + u8 channel; + + wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd); + + /* Get command parameters */ + pasv_ptr = os_strstr(cmd, ",TIME="); + if (pasv_ptr) { + *pasv_ptr = '\0'; + pasv_ptr += 6; + pasv_dwell = (u16)atoi(pasv_ptr); + if (pasv_dwell == 0) + pasv_dwell = WEXT_CSCAN_PASV_DWELL_TIME_DEF; + } + channel = (u8)atoi(cmd + 5); + + bp = WEXT_CSCAN_HEADER_SIZE; + os_memcpy(buf, WEXT_CSCAN_HEADER, bp); + + /* Set list of channels */ + buf[bp++] = WEXT_CSCAN_CHANNEL_SECTION; + buf[bp++] = channel; + if (channel != 0) { + i = (pasv_dwell - 1) / WEXT_CSCAN_PASV_DWELL_TIME_DEF; + for (; i > 0; i--) { + if ((size_t)(bp + 12) >= buf_len) + break; + buf[bp++] = WEXT_CSCAN_CHANNEL_SECTION; + buf[bp++] = channel; + } + } else { + if (pasv_dwell > WEXT_CSCAN_PASV_DWELL_TIME_MAX) + pasv_dwell = WEXT_CSCAN_PASV_DWELL_TIME_MAX; + } + + /* Set passive dwell time (default is 250) */ + buf[bp++] = WEXT_CSCAN_PASV_DWELL_SECTION; + if (channel != 0) { + buf[bp++] = (u8)WEXT_CSCAN_PASV_DWELL_TIME_DEF; + buf[bp++] = (u8)(WEXT_CSCAN_PASV_DWELL_TIME_DEF >> 8); + } else { + buf[bp++] = (u8)pasv_dwell; + buf[bp++] = (u8)(pasv_dwell >> 8); + } + + /* Set home dwell time (default is 40) */ + buf[bp++] = WEXT_CSCAN_HOME_DWELL_SECTION; + buf[bp++] = (u8)WEXT_CSCAN_HOME_DWELL_TIME; + buf[bp++] = (u8)(WEXT_CSCAN_HOME_DWELL_TIME >> 8); + + /* Set cscan type */ + buf[bp++] = WEXT_CSCAN_TYPE_SECTION; + buf[bp++] = WEXT_CSCAN_TYPE_PASSIVE; + return bp; +} + +static char *wpa_driver_get_country_code(int channels) +{ + char *country = "US"; /* WEXT_NUMBER_SCAN_CHANNELS_FCC */ + + if (channels == WEXT_NUMBER_SCAN_CHANNELS_ETSI) + country = "EU"; + else if( channels == WEXT_NUMBER_SCAN_CHANNELS_MKK1) + country = "JP"; + return country; +} + +static int wpa_driver_set_backgroundscan_params(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + struct wpa_supplicant *wpa_s; + struct iwreq iwr; + int ret = 0, i = 0, bp; + char buf[WEXT_PNO_MAX_COMMAND_SIZE]; + struct wpa_ssid *ssid_conf; + + if (drv == NULL) { + wpa_printf(MSG_ERROR, "%s: drv is NULL. Exiting", __func__); + return -1; + } + if (drv->ctx == NULL) { + wpa_printf(MSG_ERROR, "%s: drv->ctx is NULL. Exiting", __func__); + return -1; + } + wpa_s = (struct wpa_supplicant *)(drv->ctx); + if (wpa_s->conf == NULL) { + wpa_printf(MSG_ERROR, "%s: wpa_s->conf is NULL. Exiting", __func__); + return -1; + } + ssid_conf = wpa_s->conf->ssid; + + bp = WEXT_PNOSETUP_HEADER_SIZE; + os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp); + buf[bp++] = WEXT_PNO_TLV_PREFIX; + buf[bp++] = WEXT_PNO_TLV_VERSION; + buf[bp++] = WEXT_PNO_TLV_SUBVERSION; + buf[bp++] = WEXT_PNO_TLV_RESERVED; + + while ((i < WEXT_PNO_AMOUNT) && (ssid_conf != NULL)) { + /* Check that there is enough space needed for 1 more SSID, the other sections and null termination */ + if ((bp + WEXT_PNO_SSID_HEADER_SIZE + IW_ESSID_MAX_SIZE + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int)sizeof(buf)) + break; + if ((!ssid_conf->disabled) && (ssid_conf->ssid_len <= IW_ESSID_MAX_SIZE)){ + wpa_printf(MSG_DEBUG, "For PNO Scan: %s", ssid_conf->ssid); + buf[bp++] = WEXT_PNO_SSID_SECTION; + buf[bp++] = ssid_conf->ssid_len; + os_memcpy(&buf[bp], ssid_conf->ssid, ssid_conf->ssid_len); + bp += ssid_conf->ssid_len; + i++; + } + ssid_conf = ssid_conf->next; + } + + buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x", WEXT_PNO_SCAN_INTERVAL); + bp += WEXT_PNO_SCAN_INTERVAL_LENGTH; + + buf[bp++] = WEXT_PNO_REPEAT_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x", WEXT_PNO_REPEAT); + bp += WEXT_PNO_REPEAT_LENGTH; + + buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x", WEXT_PNO_MAX_REPEAT); + bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = buf; + iwr.u.data.length = bp; + + ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr); + + if (ret < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d", ret); + drv->errors++; + if (drv->errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { + drv->errors = 0; + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); + } + } else { + drv->errors = 0; + } + return ret; + +} + +int wpa_driver_wext_driver_cmd( void *priv, char *cmd, char *buf, size_t buf_len ) +{ + struct wpa_driver_wext_data *drv = priv; + struct wpa_supplicant *wpa_s = (struct wpa_supplicant *)(drv->ctx); + struct iwreq iwr; + int ret = 0, flags; + + wpa_printf(MSG_DEBUG, "%s %s len = %d", __func__, cmd, buf_len); + + if (!drv->driver_is_started && (os_strcasecmp(cmd, "START") != 0)) { + wpa_printf(MSG_ERROR,"WEXT: Driver not initialized yet"); + return -1; + } + + if (os_strcasecmp(cmd, "RSSI-APPROX") == 0) { + os_strlcpy(cmd, RSSI_CMD, MAX_DRV_CMD_SIZE); + } else if( os_strncasecmp(cmd, "SCAN-CHANNELS", 13) == 0 ) { + int no_of_chan; + + no_of_chan = atoi(cmd + 13); + os_snprintf(cmd, MAX_DRV_CMD_SIZE, "COUNTRY %s", + wpa_driver_get_country_code(no_of_chan)); + } else if (os_strcasecmp(cmd, "STOP") == 0) { + linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0); + } else if( os_strcasecmp(cmd, "RELOAD") == 0 ) { + wpa_printf(MSG_DEBUG,"Reload command"); + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); + return ret; + } else if( os_strcasecmp(cmd, "BGSCAN-START") == 0 ) { + ret = wpa_driver_set_backgroundscan_params(priv); + if (ret < 0) { + return ret; + } + os_strlcpy(cmd, "PNOFORCE 1", MAX_DRV_CMD_SIZE); + drv->bgscan_enabled = 1; + } else if( os_strcasecmp(cmd, "BGSCAN-STOP") == 0 ) { + os_strlcpy(cmd, "PNOFORCE 0", MAX_DRV_CMD_SIZE); + drv->bgscan_enabled = 0; + } + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + os_memcpy(buf, cmd, strlen(cmd) + 1); + iwr.u.data.pointer = buf; + iwr.u.data.length = buf_len; + + if( os_strncasecmp(cmd, "CSCAN", 5) == 0 ) { + if (!wpa_s->scanning && ((wpa_s->wpa_state <= WPA_SCANNING) || + (wpa_s->wpa_state >= WPA_COMPLETED))) { + iwr.u.data.length = wpa_driver_wext_set_cscan_params(buf, buf_len, cmd); + } else { + wpa_printf(MSG_ERROR, "Ongoing Scan action..."); + return ret; + } + } + + ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr); + + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s failed (%d): %s", __func__, ret, cmd); + drv->errors++; + if (drv->errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { + drv->errors = 0; + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); + } + } else { + drv->errors = 0; + ret = 0; + if ((os_strcasecmp(cmd, RSSI_CMD) == 0) || + (os_strcasecmp(cmd, LINKSPEED_CMD) == 0) || + (os_strcasecmp(cmd, "MACADDR") == 0) || + (os_strcasecmp(cmd, "GETPOWER") == 0) || + (os_strcasecmp(cmd, "GETBAND") == 0)) { + ret = strlen(buf); + } else if (os_strcasecmp(cmd, "START") == 0) { + drv->driver_is_started = TRUE; + linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1); + /* os_sleep(0, WPA_DRIVER_WEXT_WAIT_US); + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED"); */ + } else if (os_strcasecmp(cmd, "STOP") == 0) { + drv->driver_is_started = FALSE; + /* wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED"); */ + } else if (os_strncasecmp(cmd, "CSCAN", 5) == 0) { + wpa_driver_wext_set_scan_timeout(priv); + wpa_supplicant_notify_scanning(wpa_s, 1); + } + wpa_printf(MSG_DEBUG, "%s %s len = %d, %d", __func__, buf, ret, strlen(buf)); + } + return ret; +} + +int wpa_driver_signal_poll(void *priv, struct wpa_signal_info *si) +{ + char buf[MAX_DRV_CMD_SIZE]; + struct wpa_driver_wext_data *drv = priv; + char *prssi; + int res; + + os_memset(si, 0, sizeof(*si)); + res = wpa_driver_wext_driver_cmd(priv, RSSI_CMD, buf, sizeof(buf)); + /* Answer: SSID rssi -Val */ + if (res < 0) + return res; + prssi = strcasestr(buf, RSSI_CMD); + if (!prssi) + return -1; + si->current_signal = atoi(prssi + strlen(RSSI_CMD) + 1); + + res = wpa_driver_wext_driver_cmd(priv, LINKSPEED_CMD, buf, sizeof(buf)); + /* Answer: LinkSpeed Val */ + if (res < 0) + return res; + si->current_txrate = atoi(buf + strlen(LINKSPEED_CMD) + 1) * 1000; + + return 0; +} diff --git a/synadhd/wpa_supplicant_8_lib/driver_cmd_wext.h b/synadhd/wpa_supplicant_8_lib/driver_cmd_wext.h new file mode 100644 index 0000000..56d54fc --- /dev/null +++ b/synadhd/wpa_supplicant_8_lib/driver_cmd_wext.h @@ -0,0 +1,37 @@ +/* + * Driver interaction with extended Linux Wireless Extensions + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + */ +#ifndef DRIVER_CMD_WEXT_H +#define DRIVER_CMD_WEXT_H + +#define WEXT_NUMBER_SCAN_CHANNELS_FCC 11 +#define WEXT_NUMBER_SCAN_CHANNELS_ETSI 13 +#define WEXT_NUMBER_SCAN_CHANNELS_MKK1 14 + +#define WPA_DRIVER_WEXT_WAIT_US 400000 +#define WEXT_CSCAN_BUF_LEN 360 +#define WEXT_CSCAN_HEADER "CSCAN S\x01\x00\x00S\x00" +#define WEXT_CSCAN_HEADER_SIZE 12 +#define WEXT_CSCAN_SSID_SECTION 'S' +#define WEXT_CSCAN_CHANNEL_SECTION 'C' +#define WEXT_CSCAN_NPROBE_SECTION 'N' +#define WEXT_CSCAN_ACTV_DWELL_SECTION 'A' +#define WEXT_CSCAN_PASV_DWELL_SECTION 'P' +#define WEXT_CSCAN_HOME_DWELL_SECTION 'H' +#define WEXT_CSCAN_TYPE_SECTION 'T' +#define WEXT_CSCAN_TYPE_DEFAULT 0 +#define WEXT_CSCAN_TYPE_PASSIVE 1 +#define WEXT_CSCAN_PASV_DWELL_TIME 130 +#define WEXT_CSCAN_PASV_DWELL_TIME_DEF 250 +#define WEXT_CSCAN_PASV_DWELL_TIME_MAX 3000 +#define WEXT_CSCAN_HOME_DWELL_TIME 130 + +#endif /* DRIVER_CMD_WEXT_H */ |