From 816121673881080cd9eddac67c254c9794224b56 Mon Sep 17 00:00:00 2001 From: Yuri Konotopov Date: Fri, 8 Dec 2017 19:23:24 +0000 Subject: [PATCH] mtk: added wifi HAL Signed-off-by: Moyster --- mtk/wifi_hal/Android.mk | 48 + mtk/wifi_hal/common.cpp | 246 ++++ mtk/wifi_hal/common.h | 195 +++ mtk/wifi_hal/cpp_bindings.cpp | 734 +++++++++++ mtk/wifi_hal/cpp_bindings.h | 346 ++++++ mtk/wifi_hal/gscan.cpp | 1882 +++++++++++++++++++++++++++++ mtk/wifi_hal/link_layer_stats.cpp | 109 ++ mtk/wifi_hal/rtt.cpp | 690 +++++++++++ mtk/wifi_hal/sync.h | 54 + mtk/wifi_hal/wifi_hal.cpp | 1181 ++++++++++++++++++ mtk/wifi_hal/wifi_logger.cpp | 812 +++++++++++++ mtk/wifi_hal/wifi_offload.cpp | 229 ++++ 12 files changed, 6526 insertions(+) create mode 100644 mtk/wifi_hal/Android.mk create mode 100644 mtk/wifi_hal/common.cpp create mode 100644 mtk/wifi_hal/common.h create mode 100644 mtk/wifi_hal/cpp_bindings.cpp create mode 100644 mtk/wifi_hal/cpp_bindings.h create mode 100644 mtk/wifi_hal/gscan.cpp create mode 100644 mtk/wifi_hal/link_layer_stats.cpp create mode 100644 mtk/wifi_hal/rtt.cpp create mode 100644 mtk/wifi_hal/sync.h create mode 100644 mtk/wifi_hal/wifi_hal.cpp create mode 100644 mtk/wifi_hal/wifi_logger.cpp create mode 100644 mtk/wifi_hal/wifi_offload.cpp diff --git a/mtk/wifi_hal/Android.mk b/mtk/wifi_hal/Android.mk new file mode 100644 index 0000000..14bb8fd --- /dev/null +++ b/mtk/wifi_hal/Android.mk @@ -0,0 +1,48 @@ +# Copyright (C) 2016 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_REQUIRED_MODULES := + +LOCAL_CFLAGS += -Wno-unused-parameter -Wno-int-to-pointer-cast +LOCAL_CFLAGS += -Wno-maybe-uninitialized -Wno-parentheses +LOCAL_CPPFLAGS += -Wno-conversion-null + +ifeq ($(MTK_TC7_FEATURE), yes) +LOCAL_CFLAGS += -DCONFIG_PNO_SUPPORT +endif + +LOCAL_C_INCLUDES += \ + external/libnl/include \ + $(call include-path-for, libhardware_legacy)/hardware_legacy \ + external/wpa_supplicant_8/src/drivers + +LOCAL_SRC_FILES := \ + wifi_hal.cpp \ + rtt.cpp \ + common.cpp \ + cpp_bindings.cpp \ + gscan.cpp \ + link_layer_stats.cpp \ + wifi_offload.cpp + +LOCAL_MODULE := libwifi-hal-mt66xx + +include $(BUILD_STATIC_LIBRARY) + diff --git a/mtk/wifi_hal/common.cpp b/mtk/wifi_hal/common.cpp new file mode 100644 index 0000000..1c50eb7 --- /dev/null +++ b/mtk/wifi_hal/common.cpp @@ -0,0 +1,246 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +interface_info *getIfaceInfo(wifi_interface_handle handle) +{ + return (interface_info *)handle; +} + +wifi_handle getWifiHandle(wifi_interface_handle handle) +{ + return getIfaceInfo(handle)->handle; +} + +hal_info *getHalInfo(wifi_handle handle) +{ + return (hal_info *)handle; +} + +hal_info *getHalInfo(wifi_interface_handle handle) +{ + return getHalInfo(getWifiHandle(handle)); +} + +wifi_handle getWifiHandle(hal_info *info) +{ + return (wifi_handle)info; +} + +wifi_interface_handle getIfaceHandle(interface_info *info) +{ + return (wifi_interface_handle)info; +} + +wifi_error wifi_register_handler(wifi_handle handle, int cmd, nl_recvmsg_msg_cb_t func, void *arg) +{ + hal_info *info = (hal_info *)handle; + + /* 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) { + 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; + ALOGV("Added event handler %p:%p for vendor 0x%0x and subcmd 0x%0x at %d", + arg, func, id, subcmd, info->num_event_cb); + 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(wifi_handle handle, uint32_t id, int subcmd) +{ + hal_info *info = (hal_info *)handle; + + pthread_mutex_lock(&info->cb_lock); + + for (int i = 0; i < info->num_event_cb; i++) { + + if (info->event_cb[i].nl_cmd == NL80211_CMD_VENDOR + && info->event_cb[i].vendor_id == id + && info->event_cb[i].vendor_subcmd == subcmd) { + ALOGV("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; + } + } + + 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) * sizeof(cmd_info)); + info->num_cmd--; + ALOGV("Successfully removed command %d: %p from %d", id, cmd, i); + break; + } + } + + if (!cmd) { + ALOGE("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) * 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; +} + diff --git a/mtk/wifi_hal/common.h b/mtk/wifi_hal/common.h new file mode 100644 index 0000000..81ae161 --- /dev/null +++ b/mtk/wifi_hal/common.h @@ -0,0 +1,195 @@ +#include "wifi_hal.h" + +#ifndef __WIFI_HAL_COMMON_H__ +#define __WIFI_HAL_COMMON_H__ + +#define LOG_TAG "WifiHAL" + +#include +#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 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; +/* TODO: define vendor OUI here */ + +#ifndef MAC2STR +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif + +/* + 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 enum { + /* Don't use 0 as a valid subcommand */ + ANDROID_NL80211_SUBCMD_UNSPECIFIED, + + /* Define all vendor startup commands between 0x0 and 0x0FFF */ + ANDROID_NL80211_SUBCMD_WIFI_RANGE_START = 0x0001, + ANDROID_NL80211_SUBCMD_WIFI_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 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, + + /* This is reserved for future usage */ + +} ANDROID_VENDOR_SUB_COMMAND; + +typedef enum { + GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS, + GSCAN_EVENT_HOTLIST_RESULTS_FOUND, + GSCAN_EVENT_SCAN_RESULTS_AVAILABLE, + GSCAN_EVENT_FULL_SCAN_RESULTS, + RTT_EVENT_COMPLETE, + GSCAN_EVENT_COMPLETE_SCAN, + GSCAN_EVENT_HOTLIST_RESULTS_LOST, + WIFI_EVENT_RSSI_MONITOR, + GSCAN_EVENT_EPNO_EVENT, + GSCAN_EVENT_ANQPO_HOTSPOT_MATCH, +} WIFI_VENDOR_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 +} 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 clean up the socket + + 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 + + + // 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 ie_length; + char ie_data[1]; +} 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; + +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(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); + +// some common macros + +#define min(x, y) ((x) < (y) ? (x) : (y)) +#define max(x, y) ((x) > (y) ? (x) : (y)) + +#endif + diff --git a/mtk/wifi_hal/cpp_bindings.cpp b/mtk/wifi_hal/cpp_bindings.cpp new file mode 100644 index 0000000..82a1314 --- /dev/null +++ b/mtk/wifi_hal/cpp_bindings.cpp @@ -0,0 +1,734 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +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) { + 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; + } +} + +int WifiRequest::create(uint32_t id, int subcmd) { + int res = create(NL80211_CMD_VENDOR); + if (res < 0) { + return res; + } + + res = put_u32(NL80211_ATTR_VENDOR_ID, id); + if (res < 0) { + return res; + } + + res = put_u32(NL80211_ATTR_VENDOR_SUBCMD, subcmd); + if (res < 0) { + return res; + } + + if (mIface != -1) { + res = set_iface_id(mIface); + ALOGD("WifiRequest::create vendor command to iface %d, vendor_id=0x%x, subcmd=0x%04x, res=%d", + mIface, id, subcmd, res); + } + + return res; +} + + +static int no_seq_check(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + +int WifiCommand::requestResponse() { + int err = create(); + if (err < 0) { + return err; + } + + return requestResponse(mMsg); +} + +int WifiCommand::requestResponse(WifiRequest& request) { + int err = 0; + + struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!cb) + goto out; + + /* send message */ + err = nl_send_auto_complete(mInfo->cmd_sock, request.getMessage()); + 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); + + /* wait for reply */ + while (err > 0) { + int res = nl_recvmsgs(mInfo->cmd_sock, cb); + if (res) { + ALOGE("WifiCommand::requestResponse nl_recvmsgs failed: %d", res); + } + } + if (err) + ALOGD("WifiCommand::requestResponse err=%d", err); +out: + nl_cb_put(cb); + return err; +} + +int WifiCommand::requestEvent(int cmd) { + + ALOGD("WifiCommand::requestEvent for cmd %d", cmd); + + int res = wifi_register_handler(wifiHandle(), cmd, event_handler, this); + if (res < 0) { + return res; + } + + res = create(); + if (res < 0) + goto out; + + res = nl_send_auto_complete(mInfo->cmd_sock, mMsg.getMessage()); + if (res < 0) + goto out; + + ALOGD("WifiCommand::requestEvent waiting for event"); + res = mCondition.wait(); + if (res < 0) + goto out; + +out: + wifi_unregister_handler(wifiHandle(), cmd); + return 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(); + if (res < 0) + goto out; + + res = nl_send_auto_complete(mInfo->cmd_sock, mMsg.getMessage()); + if (res < 0) + goto out; + + res = mCondition.wait(); + if (res < 0) + goto out; + +out: + wifi_unregister_vendor_handler(wifiHandle(), id, subcmd); + return 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; +} + +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 called, err->error=%d", err->error); + return NL_SKIP; +} diff --git a/mtk/wifi_hal/cpp_bindings.h b/mtk/wifi_hal/cpp_bindings.h new file mode 100644 index 0000000..be7441e --- /dev/null +++ b/mtk/wifi_hal/cpp_bindings.h @@ -0,0 +1,346 @@ + +#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; + } + +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 pos->nla_type; + } + 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); + } +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_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: + hal_info *mInfo; + WifiRequest mMsg; + Condition mCondition; + wifi_request_id mId; + interface_info *mIfaceInfo; + int mRefs; +public: + WifiCommand(wifi_handle handle, wifi_request_id id) + : 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(wifi_interface_handle iface, wifi_request_id id) + : mMsg(getHalInfo(iface)->nl80211_family_id, getIfaceInfo(iface)->id), mId(id), mRefs(1) + { + mIfaceInfo = getIfaceInfo(iface); + mInfo = getHalInfo(iface); + // ALOGD("WifiCommand2 %p created, mInfo=%p, mIfaceInfo=%p, id=%d", this, mInfo, mIfaceInfo, mIfaceInfo->id); + } + + virtual ~WifiCommand() { + // ALOGD("WifiCommand %p destroyed", this); + } + + wifi_request_id id() { + return mId; + } + + virtual void addRef() { + int refs = __sync_add_and_fetch(&mRefs, 1); + // ALOGD("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 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))) + diff --git a/mtk/wifi_hal/gscan.cpp b/mtk/wifi_hal/gscan.cpp new file mode 100644 index 0000000..bb19457 --- /dev/null +++ b/mtk/wifi_hal/gscan.cpp @@ -0,0 +1,1882 @@ + +//#define LOG_NDEBUG 0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +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_SET_EPNO_SSID = 0x100F, /* 0x100F */ + + GSCAN_SUBCMD_SET_SSID_WHITE_LIST, /* 0x1010 */ + GSCAN_SUBCMD_SET_ROAM_PARAMS, /* 0x1011 */ + GSCAN_SUBCMD_ENABLE_LAZY_ROAM, /* 0x1012 */ + GSCAN_SUBCMD_SET_BSSID_PREF, /* 0x1013 */ + GSCAN_SUBCMD_SET_BSSID_BLACKLIST, /* 0x1014 */ + + GSCAN_SUBCMD_ANQPO_CONFIG, /* 0x1015 */ + /* Add more sub commands here */ + +} GSCAN_SUB_COMMAND; + +typedef enum { + GSCAN_ATTRIBUTE_CAPABILITIES = 1, + + 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_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, + /* Adaptive scan attributes */ + GSCAN_ATTRIBUTE_BUCKET_STEP_COUNT, + GSCAN_ATTRIBUTE_BUCKET_MAX_PERIOD, + + 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_CH_BUCKET_BITMASK, + + GSCAN_ATTRIBUTE_SSID = 40, + GSCAN_ATTRIBUTE_BSSID, + GSCAN_ATTRIBUTE_CHANNEL, + GSCAN_ATTRIBUTE_RSSI, + GSCAN_ATTRIBUTE_TIMESTAMP, + GSCAN_ATTRIBUTE_RTT, + GSCAN_ATTRIBUTE_RTTSD, + + GSCAN_ATTRIBUTE_HOTLIST_BSSIDS = 50, + GSCAN_ATTRIBUTE_RSSI_LOW, + GSCAN_ATTRIBUTE_RSSI_HIGH, + GSCAN_ATTRIBUTE_HOTLIST_ELEM, + GSCAN_ATTRIBUTE_HOTLIST_FLUSH, + + GSCAN_ATTRIBUTE_RSSI_SAMPLE_SIZE = 60, + GSCAN_ATTRIBUTE_LOST_AP_SAMPLE_SIZE, + GSCAN_ATTRIBUTE_MIN_BREACHING, + GSCAN_ATTRIBUTE_NUM_AP, /* TBD */ + 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, + + 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, + + 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, + + /* 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, + + GSCAN_ATTRIBUTE_MAX + +} GSCAN_ATTRIBUTE; + + +// 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 +{ + wifi_gscan_capabilities *mCapabilities; +public: + GetCapabilitiesCommand(wifi_interface_handle iface, wifi_gscan_capabilities *capabitlites) + : WifiCommand(iface, 0), mCapabilities(capabitlites) + { + memset(mCapabilities, 0, sizeof(*mCapabilities)); + } + + virtual int create() { + ALOGD("[WIFI HAL]Creating message to get gscan capablities; handle=%p, iface=%d, ifname=%s", + mIfaceInfo->handle, mIfaceInfo->id, mIfaceInfo->name); + + int ret = mMsg.create(GOOGLE_OUI, GSCAN_SUBCMD_GET_CAPABILITIES); + if (ret < 0) { + return ret; + } + + return ret; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + + ALOGV("In GetCapabilities::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGE("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + int wiphy_id = reply.get_u32(NL80211_ATTR_WIPHY); + int if_id = reply.get_u32(NL80211_ATTR_IFINDEX); + + struct nlattr *vendor_data = (struct nlattr *)reply.get_vendor_data(); + int len = reply.get_vendor_data_len(); + void *payload = NULL; + if (vendor_data->nla_type == GSCAN_ATTRIBUTE_CAPABILITIES) { + payload = nla_data(vendor_data); + len -= NLA_HDRLEN; + } + + ALOGD("wiphy_id=%d, if_id=%d, Id=%0x, subcmd=%d, len=%d, expected len=%lu", + wiphy_id, if_id, id, subcmd, len, sizeof(*mCapabilities)); + if (payload) + memcpy(mCapabilities, payload, min(len, (int) sizeof(*mCapabilities))); + + ALOGI("max_scan_cache_size=%d, %d, %d, %d, %d, %d, %d, %d, max_bssid_history_entries=%d", + mCapabilities->max_scan_cache_size, mCapabilities->max_scan_buckets, + mCapabilities->max_ap_cache_per_scan, + mCapabilities->max_rssi_sample_size, + mCapabilities->max_scan_reporting_threshold, + mCapabilities->max_hotlist_bssids, + mCapabilities->max_hotlist_ssids, + mCapabilities->max_significant_wifi_change_aps, + mCapabilities->max_bssid_history_entries); + + /* this case wifi driver don't support GScan */ + if (!(mCapabilities->max_scan_cache_size || mCapabilities->max_scan_buckets + || mCapabilities->max_ap_cache_per_scan)) + return NL_STOP; + + return NL_OK; + } +}; + +wifi_error wifi_get_gscan_capabilities(wifi_interface_handle handle, + wifi_gscan_capabilities *capabilities) +{ + GetCapabilitiesCommand command(handle, capabilities); + return (wifi_error) command.requestResponse(); +} + +///////////////////////////////////////////////////////////////////////////// + +/* helper functions */ + +static int parseScanResults(wifi_scan_result *results, int num, nlattr *attr) +{ + memset(results, 0, sizeof(wifi_scan_result) * num); + + int i = 0; + for (nl_iterator it(attr); it.has_next() && i < num; it.next(), i++) { + + int index = it.get_type(); + ALOGI("retrieved scan result %d", index); + nlattr *sc_data = (nlattr *) it.get_data(); + wifi_scan_result *result = results + i; + + for (nl_iterator it2(sc_data); it2.has_next(); it2.next()) { + int type = it2.get_type(); + if (type == GSCAN_ATTRIBUTE_SSID) { + strncpy(result->ssid, (char *) it2.get_data(), it2.get_len()); + result->ssid[it2.get_len()] = 0; + } else if (type == GSCAN_ATTRIBUTE_BSSID) { + memcpy(result->bssid, (byte *) it2.get_data(), sizeof(mac_addr)); + } else if (type == GSCAN_ATTRIBUTE_TIMESTAMP) { + result->ts = it2.get_u64(); + } else if (type == GSCAN_ATTRIBUTE_CHANNEL) { + result->ts = it2.get_u16(); + } else if (type == GSCAN_ATTRIBUTE_RSSI) { + result->rssi = it2.get_u8(); + } else if (type == GSCAN_ATTRIBUTE_RTT) { + result->rtt = it2.get_u64(); + } else if (type == GSCAN_ATTRIBUTE_RTTSD) { + result->rtt_sd = it2.get_u64(); + } + } + + } + + if (i >= num) { + ALOGE("Got too many results; skipping some"); + } + + return i; +} + +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(iface, id), mParams(params), mHandler(handler) + { } + + 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() { + 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) { + ALOGE("failed to enable full scan results; result = %d", result); + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_FULL_SCAN_RESULTS); + return result; + } + + return result; + } + + virtual int cancel() { + ALOGD("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) { +#if 1 + ALOGV("Full scan results: Got an event"); + return wifi_handle_full_scan_event(id(), event, mHandler); +#else + ALOGD("[WIFI HAL]Full scan results: Got an event"); + + // event.log(); + + struct nlattr *vendor_data = (struct nlattr *)event.get_vendor_data(); + unsigned int len = event.get_vendor_data_len(); + + if (vendor_data == NULL || len < sizeof(wifi_scan_result)) { + ALOGE("No scan results found"); + return NL_SKIP; + } + + ALOGD("vendor_data->nla_type=%d nla_len=%d, len=%d", + vendor_data->nla_type, vendor_data->nla_len, len); + + wifi_scan_result *result = NULL; + if (vendor_data->nla_type == GSCAN_EVENT_FULL_SCAN_RESULTS) + result = (wifi_scan_result *)nla_data(vendor_data); + + if (*mHandler.on_full_scan_result) + (*mHandler.on_full_scan_result)(id(), result); + + if (result) { + ALOGI("Full scan result: %-32s %02x:%02x:%02x:%02x:%02x:%02x %d %d %lld %lld %lld\n", + result->ssid, result->bssid[0], result->bssid[1], result->bssid[2], result->bssid[3], + result->bssid[4], result->bssid[5], result->rssi, result->channel, result->ts, + result->rtt, result->rtt_sd); + } + + return NL_SKIP; +#endif + } + +}; +///////////////////////////////////////////////////////////////////////////// + +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(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; + } + + /* parameter validity check */ + if (mParams->buckets[i].band < WIFI_BAND_UNSPECIFIED + || mParams->buckets[i].band > WIFI_BAND_ABG_WITH_DFS) + mParams->buckets[i].band = WIFI_BAND_ABG; /* default 2.4G + 5G */ + 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; + } + + /* parameter validity check */ + if (mParams->report_threshold_percent < 0 || mParams->report_threshold_percent > 100) + mParams->report_threshold_percent = 100; + 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); + } + + int start() { + ALOGI("1) GScan Setting configuration: "); + WifiRequest request(familyId(), ifaceId()); + int result = createSetupRequest(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create setup request; result = %d", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to configure setup; result = %d", result); + return result; + } + + request.destroy(); + + result = createScanConfigRequest(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create scan config request; result = %d", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to configure scan; result = %d", result); + return result; + } + + ALOGI("2) Enable GScan: "); + + result = createStartRequest(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create start 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) { + ALOGE("failed to start scan; result = %d", result); + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_COMPLETE_SCAN); + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SCAN_RESULTS_AVAILABLE); + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_FULL_SCAN_RESULTS); + return result; + } + return result; + } + + virtual int cancel() { + ALOGI("Stopping GScan"); + + 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); + } + } + + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_COMPLETE_SCAN); + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SCAN_RESULTS_AVAILABLE); + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_FULL_SCAN_RESULTS); + return WIFI_SUCCESS; + } + + virtual int handleResponse(WifiEvent& reply) { + /* Nothing to do on response! */ + return NL_SKIP; + } + + virtual int handleEvent(WifiEvent& event) { + ALOGV("[WIFI HAL]Got a GScan results event"); + + // event.log(); + + struct nlattr *vendor_data = (struct nlattr *)event.get_vendor_data(); + int len = event.get_vendor_data_len(); + int event_id = event.get_vendor_subcmd(); + + ALOGV("vendor_data->nla_type=%d nla_len=%d, len=%d, event_id=%d", + vendor_data->nla_type, vendor_data->nla_len, len, event_id); + + if ((event_id == GSCAN_EVENT_COMPLETE_SCAN) || + (event_id == GSCAN_EVENT_SCAN_RESULTS_AVAILABLE)) { + if (vendor_data == NULL || vendor_data->nla_len != 8) { + ALOGE("Bad event data!"); + return NL_SKIP; + } + wifi_scan_event evt_type; + evt_type = (wifi_scan_event) nla_get_u32(vendor_data); + ALOGI("Received event_id=%d, event type=%d", event_id, 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); + + ALOGD("[WIFI HAL]Starting GScan, halHandle = %p", handle); + + ScanCommand *cmd = new ScanCommand(iface, id, ¶ms, handler); + wifi_register_cmd(handle, id, cmd); + wifi_error result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + } + return result; +} + +wifi_error wifi_stop_gscan(wifi_request_id id, wifi_interface_handle iface) +{ + ALOGD("[WIFI HAL]Stopping GScan"); + wifi_handle handle = getWifiHandle(iface); + + 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); + 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; + ALOGD("[WIFI HAL]Enabling full scan results, halHandle = %p", handle); + + FullScanResultsCommand *cmd = new FullScanResultsCommand(iface, id, ¶ms_dummy, handler); + wifi_register_cmd(handle, id, cmd); + + wifi_error result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + } + return result; +} + +wifi_error wifi_disable_full_scan_results(wifi_request_id id, wifi_interface_handle iface) +{ + ALOGD("[WIFI HAL]Disabling full scan results"); + wifi_handle handle = getWifiHandle(iface); + + if(id == -1) { + wifi_scan_result_handler handler; + wifi_handle handle = getWifiHandle(iface); + int params_dummy; + + memset(&handler, 0, sizeof(handler)); + FullScanResultsCommand *cmd = new FullScanResultsCommand(iface, 0, ¶ms_dummy, handler); + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + + WifiCommand *cmd = wifi_unregister_cmd(handle, id); + if (cmd) { + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + + return WIFI_ERROR_INVALID_ARGS; +} + +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); + struct nlattr *vendor_data = (struct nlattr *)event.get_vendor_data(); + unsigned int len = event.get_vendor_data_len(); + int event_id = event.get_vendor_subcmd(); + + if (vendor_data == NULL || len < sizeof(wifi_gscan_full_result_t)) { + ALOGE("Full scan results: No scan results found"); + return NL_SKIP; + } + + wifi_gscan_full_result_t *drv_res = NULL; + if (event_id == GSCAN_EVENT_FULL_SCAN_RESULTS) + drv_res = (wifi_gscan_full_result_t *)nla_data(vendor_data); + if (!drv_res) { + ALOGE("cannot get vendor_data of GSCAN_EVENT_FULL_SCAN_RESULTS\n"); + return NL_SKIP; + } + /* 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); + + ALOGI("Full scan result: %-32s %02x:%02x:%02x:%02x:%02x:%02x %d %d %lu %lu %lu 0x%x %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, fixed->capability, drv_res->scan_ch_bucket, drv_res->ie_length); + free(full_scan_result); + return NL_SKIP; +} + + +///////////////////////////////////////////////////////////////////////////// + +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(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()); + ALOGD("retrieving mMax=%d scan results", mMax); + + for (int i = 0; i < 10 && mRetrieved < mMax; i++) { + int num_to_retrieve = mMax - mRetrieved; + ALOGI("retrieving %d:%d cached gscan results in one shot", mRetrieved, 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; + } + + ALOGD("mRetrieved=%d, prev_retrieved=%d, mCompleted=%d", mRetrieved, prev_retrieved, mCompleted); + + if (mRetrieved == prev_retrieved || mCompleted) { + /* no more items left to retrieve */ + break; + } + + request.destroy(); + } + + ALOGI("GetScanResults total 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) { + ALOGE("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + + /* + if (subcmd != GSCAN_SUBCMD_SCAN_RESULTS) { + ALOGE("Invalid response to GetScanResultsCommand; ignoring it"); + return NL_SKIP; + } + */ + + //nlattr *vendor = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + struct nlattr *vendor_data = (struct nlattr *)reply.get_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; + } + ALOGD("Id = %0x, subcmd = %d, vendor=%p, get_vendor_data()=%p vendor->nla_type=%d len=%d", + id, subcmd, vendor_data, reply.get_vendor_data(), vendor_data->nla_type, len); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == GSCAN_ATTRIBUTE_SCAN_RESULTS_COMPLETE) { + mCompleted = it.get_u8(); + ALOGI("retrieved mCompleted flag : %d", mCompleted); + } else if (it.get_type() >= GSCAN_ATTRIBUTE_NUM_OF_RESULTS + && it.get_type() <= GSCAN_ATTRIBUTE_CH_BUCKET_BITMASK) { + 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(); + ALOGD("retrieved scan_id : %d", scan_id); + } else if (it2.get_type() == GSCAN_ATTRIBUTE_SCAN_FLAGS) { + flags = it2.get_u8(); + ALOGD("retrieved scan_flags : 0x%0x", flags); + } else if (it2.get_type() == GSCAN_ATTRIBUTE_NUM_OF_RESULTS) { + num = it2.get_u32(); + ALOGD("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: 0x%0x", 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_scan_result))); + num = min(num, (int)MAX_AP_CACHE_PER_SCAN); + ALOGI("Copying %d scan results, mRetrieved=%d, scan_id=%d, flag=0x%x, bktMask=0x%04x", + num, mRetrieved, scan_id, flags, scan_ch_bucket_mask); + 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 = result->ie_length; + //mScanResults[i].ie_data = result->ie_data; + ALOGD("%02d %-32s " MACSTR " %ddB channel=%d, capa=0x%04x, ie_len=%d", i, + result->ssid, MAC2STR(result->bssid), + result->rssi, result->channel, mScanResults[i].capability, mScanResults[i].ie_length); + } + 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()); + } + } + ALOGD("GetScanResults read %dth 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) { + + ALOGI("[WIFI HAL]Getting cached gscan results, flush=%d, max=%d, num=%d", flush, max, *num); + + GetScanResultsCommand *cmd = new GetScanResultsCommand(iface, flush, results, max, num); + 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(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_u16(GSCAN_ATTRIBUTE_NUM_AP, 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_u32(GSCAN_ATTRIBUTE_RSSI_HIGH, mParams.ap[i].high); + if (result < 0) { + return result; + } + result = request.put_u32(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; + } + + int start() { + ALOGD("[WIFI HAL]Executing hotlist setup request, num = %d", mParams.num_bssid); + WifiRequest request(familyId(), ifaceId()); + int result = createSetupRequest(request); + if (result < 0) { + return result; + } + + result = requestResponse(request); + if (result < 0) { + ALOGI("Failed to execute hotlist setup request, result = %d", result); + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_HOTLIST_RESULTS_FOUND); + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_HOTLIST_RESULTS_LOST); + return result; + } + + ALOGI("Successfully set %d APs in the hotlist", mParams.num_bssid); + result = createFeatureRequest(request, GSCAN_SUBCMD_ENABLE_GSCAN, 1); + 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) { + ALOGE("failed to start scan; result = %d", result); + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_HOTLIST_RESULTS_FOUND); + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_HOTLIST_RESULTS_LOST); + return result; + } + + ALOGI("successfully restarted the scan"); + return result; + } + + virtual int cancel() { + /* unregister event handler */ + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_HOTLIST_RESULTS_FOUND); + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_HOTLIST_RESULTS_LOST); + /* 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) { + ALOGD("[WIFI HAL]Hotlist AP event"); + int event_id = event.get_vendor_subcmd(); + // event.log(); + + struct nlattr *vendor_data = (struct nlattr *)event.get_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_scan_result); + num = min(MAX_RESULTS, num); + ALOGD("hotlist APs num=%d, vendor len=%d, nla_len=%d nla_type=%d", + num, len, vendor_data->nla_len, vendor_data->nla_type); + if(vendor_data->nla_type == GSCAN_EVENT_HOTLIST_RESULTS_LOST + || vendor_data->nla_type == GSCAN_EVENT_HOTLIST_RESULTS_FOUND) + memcpy(mResults, nla_data(vendor_data), num * sizeof(wifi_scan_result)); + + 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(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) { + 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; + } + result = request.put(GSCAN_ATTRIBUTE_EPNO_SSID, ssid_list[i].ssid, DOT11_MAX_SSID_LEN); + ALOGI("PNO network: SSID %s flags %x auth %x", ssid_list[i].ssid, + 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(ssid_list[i].ssid)); + 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; + } + + result = requestResponse(request); + if (result < 0) { + ALOGI("Failed to execute ePNO setup request, result = %d", result); + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_EPNO_EVENT); + return result; + } + + ALOGI("Successfully set %d SSIDs for ePNO", epno_params.num_networks); + registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_EPNO_EVENT); + 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"); + 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_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); + wifi_register_cmd(handle, id, cmd); + wifi_error result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + } + 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(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_AP, mParams.num_bssid); + if (result < 0) { + return result; + } + + struct nlattr * attr = request.attr_start(GSCAN_ATTRIBUTE_SIGNIFICANT_CHANGE_BSSIDS); + + 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_u32(GSCAN_ATTRIBUTE_RSSI_HIGH, mParams.ap[i].high); + if (result < 0) { + return result; + } + result = request.put_u32(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() { + ALOGD("[WIFI HAL]Set significant wifi change config"); + WifiRequest request(familyId(), ifaceId()); + + int result = createSetupRequest(request); + if (result < 0) { + return result; + } + + result = requestResponse(request); + if (result < 0) { + ALOGE("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) { + return result; + } + + registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS); + + result = requestResponse(request); + if (result < 0) { + ALOGE("failed to start scan; result = %d", result); + 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) { + ALOGD("[WIFI HAL]Got a significant wifi change event"); + + struct nlattr *vendor_data = (struct nlattr *)event.get_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; + if (vendor_data->nla_type == GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS) + ci = (ChangeInfo *)nla_data(vendor_data); + else + return NL_SKIP; + + 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(&(mResultsBuffer[i])); + } + + ALOGI("Retrieved %d scan results, vendor len=%d nla_type=%d", num, len, vendor_data->nla_type); + + 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); + wifi_register_cmd(handle, id, cmd); + wifi_error result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + } + 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; + wifi_handle handle = getWifiHandle(iface); + + memset(&handler, 0, sizeof(handler)); + ePNOCommand *cmd = new ePNOCommand(iface, id, NULL, handler); + 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); + + ePNOCommand *cmd = new ePNOCommand(iface, id, params, handler); + wifi_register_cmd(handle, id, cmd); + wifi_error result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + } + return result; +} + +class BssidBlacklistCommand : public WifiCommand +{ +private: + wifi_bssid_params *mParams; +public: + BssidBlacklistCommand(wifi_interface_handle handle, int id, + wifi_bssid_params *params) + : WifiCommand(handle, id), mParams(params) + { } + int createRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_SET_BSSID_BLACKLIST); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(GSCAN_ATTRIBUTE_NUM_BSSID, mParams->num_bssid); + if (result < 0) { + return result; + } + if (!mParams->num_bssid) { + result = request.put_u32(GSCAN_ATTRIBUTE_BSSID_BLACKLIST_FLUSH, 1); + 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() { + ALOGI("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); + if (result < 0) { + return result; + } + 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) +{ + wifi_handle handle = getWifiHandle(iface); + + BssidBlacklistCommand *cmd = new BssidBlacklistCommand(iface, id, ¶ms); + wifi_register_cmd(handle, id, cmd); + return (wifi_error)cmd->start(); +} + +//////////////////////////////////////////////////////////////////////////////// + +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(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) { + ALOGE("failed to set ANQPO networks; result = %d", result); + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_ANQPO_HOTSPOT_MATCH); + 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("%d\t", mResult->rssi); + ALOGI("%d\t", mResult->channel); + ALOGI("%lld\t", mResult->ts); + ALOGI("%lld\t", mResult->rtt); + ALOGI("%lld\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); + wifi_register_cmd(handle, id, cmd); + wifi_error result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + } + 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/mtk/wifi_hal/link_layer_stats.cpp b/mtk/wifi_hal/link_layer_stats.cpp new file mode 100644 index 0000000..192ba92 --- /dev/null +++ b/mtk/wifi_hal/link_layer_stats.cpp @@ -0,0 +1,109 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + + +typedef enum { + LSTATS_SUBCMD_GET_INFO = ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START, +} LSTATS_SUB_COMMAND; + +typedef enum { + LSTATS_ATTRIBUTE_STATS = 2, +} LSTATS_ATTRIBUTE; + +/////////////////////////////////////////////////////////////////////////////////// +class GetLinkStatsCommand : public WifiCommand +{ + wifi_stats_result_handler mHandler; +public: + GetLinkStatsCommand(wifi_interface_handle iface, wifi_stats_result_handler handler) + : WifiCommand(iface, 0), mHandler(handler) + { } + + virtual int create() { + ALOGD("[WIFI HAL]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) { + ALOGD("[WIFI HAL]In GetLinkStatsCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGE("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + + // ALOGI("Id = %0x, subcmd = %d", id, subcmd); + + struct nlattr *vendor_data = (struct nlattr *)reply.get_vendor_data(); + int len = reply.get_vendor_data_len(); + wifi_radio_stat *data; + + if(vendor_data->nla_type == LSTATS_ATTRIBUTE_STATS) + data = (wifi_radio_stat *)nla_data(vendor_data); + else + return NL_SKIP; + int num_chan = data->num_channels; + if (num_chan > 32) { + ALOGE("Incorrect number of channels = %d", num_chan); + return NL_SKIP; + } + +/* + (*mHandler.on_link_stats_results)(id, + (wifi_iface_stat *)((char *)&(data)->channels + + num_chan*sizeof(wifi_channel_stat)), + 1, data); +*/ + + return NL_OK; + } +}; + +wifi_error wifi_get_link_stats(wifi_request_id id, + wifi_interface_handle iface, wifi_stats_result_handler handler) +{ +#if 0 + GetLinkStatsCommand command(iface, handler); + return (wifi_error) command.requestResponse(); +#else + ALOGD("[WIFI HAL]don't support wifi_get_link_stats"); + return WIFI_ERROR_NOT_SUPPORTED; +#endif +} + diff --git a/mtk/wifi_hal/rtt.cpp b/mtk/wifi_hal/rtt.cpp new file mode 100644 index 0000000..9e06ba2 --- /dev/null +++ b/mtk/wifi_hal/rtt.cpp @@ -0,0 +1,690 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "nl80211_copy.h" + +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include +#include + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +using namespace android; +#define RTT_RESULT_SIZE (sizeof(wifi_rtt_result)); + +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_CAPABILITIES = 1, + + RTT_ATTRIBUTE_TARGET_CNT = 10, + RTT_ATTRIBUTE_TARGET_INFO, + RTT_ATTRIBUTE_TARGET_MAC, + RTT_ATTRIBUTE_TARGET_TYPE, + RTT_ATTRIBUTE_TARGET_PEER, + RTT_ATTRIBUTE_TARGET_CHAN, + RTT_ATTRIBUTE_TARGET_PERIOD, + RTT_ATTRIBUTE_TARGET_NUM_BURST, + RTT_ATTRIBUTE_TARGET_NUM_FTM_BURST, + RTT_ATTRIBUTE_TARGET_NUM_RETRY_FTM, + RTT_ATTRIBUTE_TARGET_NUM_RETRY_FTMR, + RTT_ATTRIBUTE_TARGET_LCI, + RTT_ATTRIBUTE_TARGET_LCR, + RTT_ATTRIBUTE_TARGET_BURST_DURATION, + RTT_ATTRIBUTE_TARGET_PREAMBLE, + RTT_ATTRIBUTE_TARGET_BW, + RTT_ATTRIBUTE_RESULTS_COMPLETE = 30, + RTT_ATTRIBUTE_RESULTS_PER_TARGET, + RTT_ATTRIBUTE_RESULT_CNT, + RTT_ATTRIBUTE_RESULT +} 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; +#define DOT11_HDR_LEN 2 +#define DOT11_RM_IE_LEN 5 +#define DOT11_MNG_MEASURE_REQUEST_ID 38 /* 11H MeasurementRequest */ +#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 reponse")}, + {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(iface, 0), mCapabilities(capabitlites) + { + memset(mCapabilities, 0, sizeof(*mCapabilities)); + } + + virtual int create() { + ALOGD("[WIFI HAL]Creating message to get Rtt capablities; 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(); + int wiphy_id = reply.get_u32(NL80211_ATTR_WIPHY); + int if_id = reply.get_u32(NL80211_ATTR_IFINDEX); + + struct nlattr *vendor_data = (struct nlattr *)reply.get_vendor_data(); + int len = reply.get_vendor_data_len(); + void *payload = NULL; + if(vendor_data->nla_type == RTT_ATTRIBUTE_CAPABILITIES) { + payload = nla_data(vendor_data); + len -= NLA_HDRLEN; + } + + ALOGD("wiphy_id=%d, if_id=%d, Id=%0x, subcmd=%d, len=%d, expected len=%d", wiphy_id, if_id, id, subcmd, len, + sizeof(*mCapabilities)); + if (payload) + memcpy(mCapabilities, payload, min(len, (int) sizeof(*mCapabilities))); + + ALOGI("RTT capability: %d, %d, %d, %d, %d, %d", + mCapabilities->rtt_one_sided_supported, mCapabilities->rtt_ftm_supported, + mCapabilities->lci_support, mCapabilities->lcr_support, + mCapabilities->preamble_support, mCapabilities->bw_support); + + return NL_OK; + } +}; + + +class GetRttAvailableChannelCommand : public WifiCommand +{ + wifi_channel_info* mChannelInfo; +public: + GetRttAvailableChannelCommand(wifi_interface_handle iface, wifi_channel_info *channel) + : WifiCommand(iface, 0), mChannelInfo(channel) + { + memset(mChannelInfo, 0 , sizeof(*mChannelInfo)); + + } + + virtual int create() { + ALOGD("Creating message to get available channel ; 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 GetRttAvailableChannelCommand::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(*mChannelInfo)); + + memcpy(mChannelInfo, data, min(len, (int) sizeof(*mChannelInfo))); + + return NL_OK; + } +}; + + +class EnableResponderCommand : public WifiCommand +{ + wifi_channel_info mChannelInfo; + wifi_channel_info* mChannelUsed; + unsigned m_max_duration_sec; +public: + EnableResponderCommand(wifi_interface_handle iface, int id, wifi_channel_info channel_hint, + unsigned max_duration_seconds, wifi_channel_info *channel_used) + : WifiCommand(iface, 0), mChannelInfo(channel_hint), + m_max_duration_sec(max_duration_seconds), mChannelUsed(channel_used) + { + memset(mChannelUsed, 0 , sizeof(*mChannelUsed)); + + } + + 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(*mChannelUsed)); + + memcpy(mChannelUsed, data, min(len, (int) sizeof(*mChannelUsed))); + + return NL_OK; + } +}; + + +class CancelResponderCommand : public WifiCommand +{ + +public: + CancelResponderCommand(wifi_interface_handle iface, int id) + : WifiCommand(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 totalCnt; + static const int MAX_RESULTS = 1024; + wifi_rtt_result *rttResults[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(iface, id), numRttParams(num_rtt_config), rttParams(rtt_config), + rttHandler(handler) + { + memset(rttResults, 0, sizeof(rttResults)); + currentIdx = 0; + mCompleted = 0; + totalCnt = 0; + } + + RttCommand(wifi_interface_handle iface, int id) + : WifiCommand(iface, id) + { + currentIdx = 0; + mCompleted = 0; + totalCnt = 0; + numRttParams = 0; + } + + 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); + request.put_u8(RTT_ATTRIBUTE_TARGET_CNT, num_devices); + 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; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to configure RTT setup; result = %d", result); + return result; + } + + registerVendorHandler(GOOGLE_OUI, RTT_EVENT_COMPLETE); + 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(); + if (vendor_data == NULL || len == 0) { + ALOGI("No rtt results found"); + } + 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("retrived 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) { + int result_len = it2.get_len(); + rttResults[currentIdx] = (wifi_rtt_result *)malloc(it2.get_len()); + wifi_rtt_result *rtt_result = rttResults[currentIdx]; + if (rtt_result == NULL) { + mCompleted = 1; + ALOGE("failed to allocate the wifi_rtt_result\n"); + break; + } + memcpy(rtt_result, it2.get_data(), it2.get_len()); + result_len -= sizeof(wifi_rtt_result); + if (result_len > 0) { + result_len -= sizeof(wifi_rtt_result); + 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_REQUEST_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_REQUEST_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_REQUEST_ID) && + (ele_2->type == DOT11_MEASURE_TYPE_LCI)) { + rtt_result->LCI = (wifi_information_element *)ele_2; + } + } + } + } + } + totalCnt++; + ALOGI("retrived 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 : %llu ns, rtt_sd : %llu\n" + "\tdistance : %d, burst_duration : %d ms, negotiated_burst_num : %d\n", + rtt_result->burst_num, rtt_result->measurement_number, + rtt_result->success_number, rtt_result->number_per_burst_peer, + get_err_info(rtt_result->status), rtt_result->retry_after_duration, + rtt_result->rssi, rtt_result->rx_rate.bitrate * 100, + rtt_result->rtt/10, rtt_result->rtt_sd, rtt_result->distance_mm / 10, + rtt_result->burst_duration, rtt_result->negotiated_burst_num); + currentIdx++; + } + } + } + + } + if (mCompleted) { + unregisterVendorHandler(GOOGLE_OUI, RTT_EVENT_COMPLETE); + (*rttHandler.on_rtt_results)(id(), totalCnt, rttResults); + for (int i = 0; i < currentIdx; i++) { + free(rttResults[i]); + rttResults[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) +{ + wifi_handle handle = getWifiHandle(iface); + + RttCommand *cmd = new RttCommand(iface, id, num_rtt_config, rtt_config, handler); + wifi_register_cmd(handle, id, cmd); + return (wifi_error)cmd->start(); +} + +/* 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[]) +{ + wifi_handle handle = getWifiHandle(iface); + RttCommand *cmd = new RttCommand(iface, id); + if (cmd) { + cmd->cancel_specific(num_devices, addr); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + return WIFI_ERROR_INVALID_ARGS; +} + +/* API to get RTT capability */ +wifi_error wifi_get_rtt_capabilities(wifi_interface_handle iface, + wifi_rtt_capabilities *capabilities) +{ + GetRttCapabilitiesCommand command(iface, capabilities); + return (wifi_error) command.requestResponse(); +} + +/* API to get the channel */ +wifi_error wifi_rtt_get_available_channel(wifi_interface_handle iface, wifi_channel_info* channel) +{ + GetRttAvailableChannelCommand command(iface, channel); + 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. + * channel_used - channel used for RTT responder, NULL if responder is not enabled. + */ +wifi_error wifi_enable_responder(wifi_request_id id, wifi_interface_handle iface, + wifi_channel_info channel_hint, unsigned max_duration_seconds, + wifi_channel_info* channel_used ) +{ + EnableResponderCommand command(iface, id, channel_hint, max_duration_seconds, channel_used); + 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/mtk/wifi_hal/sync.h b/mtk/wifi_hal/sync.h new file mode 100644 index 0000000..cea2ea9 --- /dev/null +++ b/mtk/wifi_hal/sync.h @@ -0,0 +1,54 @@ + +#include + +#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 \ No newline at end of file diff --git a/mtk/wifi_hal/wifi_hal.cpp b/mtk/wifi_hal/wifi_hal.cpp new file mode 100644 index 0000000..8b746cc --- /dev/null +++ b/mtk/wifi_hal/wifi_hal.cpp @@ -0,0 +1,1181 @@ +//#define LOG_NDEBUG 0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" +#include "cutils/properties.h" + +/* + 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 FEATURE_SET 0 +#define FEATURE_SET_MATRIX 1 + +typedef enum { + WIFI_SUBCMD_GET_CHANNEL_LIST = ANDROID_NL80211_SUBCMD_WIFI_RANGE_START, + + WIFI_SUBCMD_GET_FEATURE_SET, /* 0x0002 */ + WIFI_SUBCMD_GET_FEATURE_SET_MATRIX, /* 0x0003 */ + WIFI_SUBCMD_SET_PNO_RANDOM_MAC_OUI, /* 0x0004 */ + WIFI_SUBCMD_NODFS_SET, /* 0x0005 */ + WIFI_SUBCMD_SET_COUNTRY_CODE, /* 0x0006 */ + + WIFI_SUBCMD_SET_RSSI_MONITOR, /* 0x0007 */ + /* Add more sub commands here */ + +} WIFI_SUB_COMMAND; + +typedef enum { + WIFI_ATTRIBUTE_BAND = 1, + WIFI_ATTRIBUTE_NUM_CHANNELS, + WIFI_ATTRIBUTE_CHANNEL_LIST, + + WIFI_ATTRIBUTE_NUM_FEATURE_SET, + WIFI_ATTRIBUTE_FEATURE_SET, + WIFI_ATTRIBUTE_PNO_RANDOM_MAC_OUI, + WIFI_ATTRIBUTE_NODFS_VALUE, + WIFI_ATTRIBUTE_COUNTRY_CODE, + + WIFI_ATTRIBUTE_MAX_RSSI, + WIFI_ATTRIBUTE_MIN_RSSI, + WIFI_ATTRIBUTE_RSSI_MONITOR_START + +} WIFI_ATTRIBUTE; + + +#define WIFI_HAL_CMD_SOCK_PORT 644 +#define WIFI_HAL_EVENT_SOCK_PORT 645 + +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 wifi_error wifi_init_interfaces(wifi_handle handle); +static void wifi_internal_cleanup(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); + +/***************************************************************************** +* socket pair to wake up wifi_event_loop from a blocking poll for termination +* - exit_sockets[0]: write socket, trigger from wifi_cleanup +* - exit_sockets[1]: read socket, monitored in poll of wifi_event_loop +*****************************************************************************/ +static int exit_sockets[2] = {-1, -1}; + + +///////////////////////////////////////////////////////////////////////// +/* Initialize vendor function pointer table with MTK HAL 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_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_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_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_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_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_set_bssid_blacklist = wifi_set_bssid_blacklist; + fn->wifi_start_rssi_monitoring = wifi_start_rssi_monitoring; + fn->wifi_stop_rssi_monitoring = wifi_stop_rssi_monitoring; + fn->wifi_start_sending_offloaded_packet = wifi_start_sending_offloaded_packet; + fn->wifi_stop_sending_offloaded_packet = wifi_stop_sending_offloaded_packet; + return WIFI_SUCCESS; +} + +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) +{ + struct nl_sock *sock; + + // ALOGD("Creating netlink socket, local port[%d]", port); + sock = nl_socket_alloc(); + if (sock == NULL) { + ALOGE("Could not create netlink socket: %s(%d)", strerror(errno), errno); + return NULL; + } + + wifi_socket_set_local_port(sock, port); + + // ALOGD("Connecting to socket"); + if (nl_connect(sock, NETLINK_GENERIC)) { + ALOGE("Could not connect to netlink socket: %s(%d)", strerror(errno), errno); + nl_socket_free(sock); + return NULL; + } + + return sock; +} + +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); + } + + ALOGD("Add membership for group %s successfully", group); + return ret; +} + +wifi_error wifi_initialize(wifi_handle *handle) +{ + ALOGI("Wifi HAL initializing"); + + hal_info *info = (hal_info *)malloc(sizeof(hal_info)); + if (info == NULL) { + ALOGE("Could not allocate hal_info"); + return WIFI_ERROR_OUT_OF_MEMORY; + } + + memset(info, 0, sizeof(*info)); + *handle = (wifi_handle)info; + + info->cmd_sock = wifi_create_nl_socket(WIFI_HAL_CMD_SOCK_PORT); + if (info->cmd_sock == NULL) { + ALOGE("Could not create command socket"); + wifi_internal_cleanup(*handle); + *handle = NULL; + return WIFI_ERROR_UNKNOWN; + } + + info->event_sock = wifi_create_nl_socket(WIFI_HAL_EVENT_SOCK_PORT); + if (info->event_sock == NULL) { + ALOGE("Could not create event socket"); + wifi_internal_cleanup(*handle); + *handle = NULL; + return WIFI_ERROR_UNKNOWN; + } + + struct nl_cb *cb = nl_socket_get_cb(info->event_sock); + // ALOGI("cb->refcnt = %d", cb->cb_refcnt); + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, internal_no_seq_check, info); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, internal_valid_message_handler, info); + nl_cb_put(cb); + + info->nl80211_family_id = genl_ctrl_resolve(info->cmd_sock, "nl80211"); + if (info->nl80211_family_id < 0) { + ALOGE("Could not resolve nl80211 family id"); + wifi_internal_cleanup(*handle); + *handle = NULL; + return WIFI_ERROR_UNKNOWN; + } + + info->clean_up = false; + info->in_event_loop = false; + + info->event_cb = (cb_info *)malloc(sizeof(cb_info) * DEFAULT_EVENT_CB_SIZE); + info->alloc_event_cb = DEFAULT_EVENT_CB_SIZE; + info->num_event_cb = 0; + if (info->event_cb == NULL) { + ALOGE("Could not allocate cb_info array"); + wifi_internal_cleanup(*handle); + *handle = NULL; + return WIFI_ERROR_OUT_OF_MEMORY; + } + + info->cmd = (cmd_info *)malloc(sizeof(cmd_info) * DEFAULT_CMD_SIZE); + info->alloc_cmd = DEFAULT_CMD_SIZE; + info->num_cmd = 0; + if (info->cmd == NULL) { + ALOGE("Could not allocate cmd_info array"); + wifi_internal_cleanup(*handle); + *handle = NULL; + return WIFI_ERROR_OUT_OF_MEMORY; + } + + pthread_mutex_init(&info->cb_lock, NULL); + + wifi_add_membership(*handle, "scan"); + wifi_add_membership(*handle, "mlme"); + wifi_add_membership(*handle, "regulatory"); + wifi_add_membership(*handle, "vendor"); + + wifi_init_interfaces(*handle); + ALOGD("Found %d interfaces", info->num_interfaces); + + ALOGD("Wifi HAL initialized successfully: nl80211_family_id=%d", info->nl80211_family_id); + return WIFI_SUCCESS; +} + +///////////////////////////////////////////////////////////////////////// + +static bool is_wifi_interface(const char *name) +{ + if (strncmp(name, "wlan", 4) != 0 && strncmp(name, "p2p", 3) != 0) { + /* Not a wifi interface; ignore it */ + return false; + } else { + return true; + } +} + +int get_interface(const char *name, interface_info *info) +{ + strcpy(info->name, name); + info->id = if_nametoindex(name); + ALOGD("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); + + d = opendir("/sys/class/net"); + if (d == 0) + return WIFI_ERROR_UNKNOWN; + + info->interfaces = (interface_info **)malloc(sizeof(interface_info *) * n); + if (!info->interfaces) { + 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) && i < n) { + interface_info *ifinfo = (interface_info *)malloc(sizeof(interface_info)); + if (!ifinfo) + continue; + if (get_interface(de->d_name, ifinfo) != WIFI_SUCCESS) { + free(ifinfo); + continue; + } + ifinfo->handle = handle; + info->interfaces[i] = ifinfo; + i++; + } + } + + closedir(d); + + info->num_interfaces = (i < n) ? i : 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_get_iface_name(wifi_interface_handle handle, char *name, size_t size) +{ + interface_info *info = (interface_info *)handle; + strcpy(name, info->name); + return WIFI_SUCCESS; +} + +///////////////////////////////////////////////////////////////////////// + +static void wifi_internal_cleanup(wifi_handle handle) +{ + hal_info *info = getHalInfo(handle); + if (info == NULL) + return; + + if (info->interfaces) { + int i = 0; + for (; i < info->num_interfaces; i++) + free(info->interfaces[i]); + free(info->interfaces); + } + + pthread_mutex_destroy(&info->cb_lock); + + if (info->event_cb) + free(info->event_cb); + + if (info->cmd) + free(info->cmd); + + if (info->cmd_sock) + nl_socket_free(info->cmd_sock); + + if (info->event_sock) + nl_socket_free(info->event_sock); + + if (info->cleaned_up_handler) + info->cleaned_up_handler(handle); + + free(info); + + if (exit_sockets[0] != -1) { + close(exit_sockets[0]); + exit_sockets[0] = -1; + } + if (exit_sockets[1] != -1) { + close(exit_sockets[1]); + exit_sockets[1] = -1; + } + + ALOGD("Internal cleanup completed"); + return; +} + +void wifi_cleanup(wifi_handle handle, wifi_cleaned_up_handler handler) +{ + hal_info *info = getHalInfo(handle); + info->cleaned_up_handler = handler; + info->clean_up = true; + + if (info->in_event_loop && exit_sockets[0] != -1) { + char sig = 'T'; + write(exit_sockets[0], &sig, sizeof(sig)); + ALOGD("Signal wifi_event_loop to exit"); + } else { + wifi_internal_cleanup(handle); + } +} + +///////////////////////////////////////////////////////////////////////// + +static int internal_event_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; +} + +void wifi_event_loop(wifi_handle handle) +{ + hal_info *info = getHalInfo(handle); + if (info->in_event_loop) + return; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, exit_sockets) < 0) { + ALOGE("Create socketpair failed, errno %d", errno); + return; + } + + info->in_event_loop = true; + + struct pollfd fds[2]; + /* TODO: Add support for timeout */ + int timeout = -1; /* Infinite timeout */ + + do { + fds[0].fd = exit_sockets[1]; + fds[0].events = POLLIN; + fds[0].revents = 0; + fds[1].fd = nl_socket_get_fd(info->event_sock); + fds[1].events = POLLIN; + fds[1].revents = 0; + + int result = poll(fds, 2, -1); + if (result < 0) { + ALOGE("wifi_event_loop: poll error result=%d, errno=%s(%d)", result, strerror(errno), errno); + if (errno == EINTR) /* ignore EINTR */ + continue; + break; + } else if (fds[0].revents & POLLIN) { + char sig; + int sz; + sz = read(fds[0].fd, &sig, sizeof(sig)); + if (sz == -1) { + ALOGE("read fail errno=%s(%d)\n", strerror(errno), errno); + break; + } + if (sig == 'T') { + ALOGD("Wifi HAL stopped!!!"); + break; + } + } else if (fds[1].revents & POLLIN) { + // ALOGI("Receive HAL events"); + internal_event_handler(handle); + } else { + ALOGE("Unknown returned event"); + } + } while (!info->clean_up); + + info->in_event_loop = false; + + if (info->clean_up) + wifi_internal_cleanup(handle); + + ALOGD("Leaving wifi_event_loop"); +} + +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) +{ + 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()); + } + + bool dispatched = false; + + 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); + + (*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(handle, 0) + { + mName = name; + mGroup = group; + mId = -1; + } + + int getId() { + return mId; + } + + virtual int create() { + int nlctrlFamily = genl_ctrl_resolve(mInfo->cmd_sock, "nlctrl"); + // ALOGD("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) { + + // ALOGD("handling reponse in %s", __func__); + + struct nlattr **tb = reply.attributes(); + struct genlmsghdr *gnlh = reply.header(); + struct nlattr *mcgrp = NULL; + int i; + + if (!tb[CTRL_ATTR_MCAST_GROUPS]) { + ALOGD("No multicast groups found"); + return NL_SKIP; + } else { + // ALOGD("Multicast groups attr size = %d", nla_len(tb[CTRL_ATTR_MCAST_GROUPS])); + } + + for_each_attr(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) { + + // ALOGD("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]); + + // ALOGD("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; + } +}; + +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(); +} + +class GetChannelListCommand : public WifiCommand +{ +private: + wifi_channel *mChannels; + int mMaxChannels; + int *mNumOfChannel; + int mBand; + +public: + GetChannelListCommand(wifi_interface_handle handle, int band, int max_channels, + wifi_channel *channels, int *num_channels) + : WifiCommand(handle, 0) + { + mBand = band; + mMaxChannels = max_channels; + mChannels = channels; + mNumOfChannel = num_channels; + memset(mChannels, 0, sizeof(wifi_channel) * mMaxChannels); + } + + virtual int create() { + int ret; + + ret = mMsg.create(GOOGLE_OUI, WIFI_SUBCMD_GET_CHANNEL_LIST); + if (ret < 0) { + return ret; + } + + ALOGI("In GetChannelList::mBand=%d", mBand); + + nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); + ret = mMsg.put_u32(WIFI_ATTRIBUTE_BAND, mBand); + if (ret < 0) { + return ret; + } + + mMsg.attr_end(data); + return WIFI_SUCCESS; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + + ALOGV("In GetChannelList::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGE("Ignore reply with cmd 0x%x", reply.get_cmd()); + return NL_SKIP; + } + + int vendor_id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + ALOGV("vendor_id = 0x%x, subcmd = 0x%x", vendor_id, subcmd); + + nlattr *vendor = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + if (vendor == NULL || len == 0) { + ALOGE("No vendor data in GetChannelList response, ignore it"); + return NL_SKIP; + } + + int num_channels = 0; + for (nl_iterator it(vendor); it.has_next(); it.next()) { + if (it.get_type() == WIFI_ATTRIBUTE_NUM_CHANNELS) { + num_channels = it.get_u32(); + ALOGI("Get channel list with %d channels", num_channels); + if (num_channels > mMaxChannels) + num_channels = mMaxChannels; + *mNumOfChannel = num_channels; + } else if (it.get_type() == WIFI_ATTRIBUTE_CHANNEL_LIST && num_channels) { + memcpy(mChannels, it.get_data(), sizeof(wifi_channel) * num_channels); + } else { + ALOGW("Ignore invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + + ALOGD("mChannels[0]=%d mChannels[1]=%d", *mChannels, *(mChannels + 1)); + + 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, band, max_channels, channels, num_channels); + return (wifi_error) command.requestResponse(); +} + +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(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 == FEATURE_SET) { + ret = mMsg.create(GOOGLE_OUI, WIFI_SUBCMD_GET_FEATURE_SET); + } else if (feature_type == FEATURE_SET_MATRIX) { + 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 subcmd message to driver, ret=%d", ret); + } + + return ret; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + + ALOGD("In GetFeatureSetCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignore reply with cmd 0x%x", reply.get_cmd()); + return NL_SKIP; + } + + int vendor_id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + ALOGD("vendor_id = 0x%x, subcmd = 0x%x", vendor_id, subcmd); + + 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 GetFeatureSetCommand response, ignore it"); + return NL_SKIP; + } + + if (feature_type == FEATURE_SET) { + void *data = reply.get_vendor_data(); + if (!fset) { + ALOGE("feature_set pointer is 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("feature_set pointer is not set"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == WIFI_ATTRIBUTE_NUM_FEATURE_SET) { + num_features_set = it.get_u32(); + ALOGI("Get 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() == WIFI_ATTRIBUTE_FEATURE_SET) && + i < num_features_set) { + feature_matrix[i] = it.get_u32(); + i++; + } else { + ALOGW("Ignore invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + } + + return NL_OK; + } +}; + +wifi_error wifi_get_supported_feature_set(wifi_interface_handle handle, feature_set *pset) +{ +#if 0 + GetFeatureSetCommand command(handle, FEATURE_SET, set, NULL, NULL, 1); + return (wifi_error)command.requestResponse(); +#else + feature_set set = 0; + char prop_buf[PROPERTY_VALUE_MAX]; + + property_get("ro.wlan.mtk.wifi.5g", prop_buf, NULL); + if (!strcmp(prop_buf, "1")) + set |= WIFI_FEATURE_INFRA_5G; + + set |= WIFI_FEATURE_P2P; + set |= WIFI_FEATURE_SOFT_AP; + set |= WIFI_FEATURE_TDLS; + +#ifdef CONFIG_PNO_SUPPORT + set |= WIFI_FEATURE_PNO; +#endif + + memcpy(pset, &set, sizeof(feature_set)); + + ALOGI("[WIFI HAL]wifi_get_supported_feature_set: handle=%p, feature_set=0x%x", handle, *pset); + return WIFI_SUCCESS; +#endif +} + +wifi_error wifi_get_concurrency_matrix(wifi_interface_handle handle, int set_size_max, + feature_set set[], int *set_size) +{ + GetFeatureSetCommand command(handle, FEATURE_SET_MATRIX, NULL, set, set_size, set_size_max); + return (wifi_error)command.requestResponse(); +} + +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(handle, 0) + { + mOui = scan_oui; + } + + 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(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("[WIFI HAL]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("[WIFI HAL]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; + } +}; + +wifi_error wifi_set_scanning_mac_oui(wifi_interface_handle handle, oui scan_oui) +{ +#if 0 + SetPnoMacAddrOuiCommand command(handle, scan_oui); + return (wifi_error)command.start(); +#else + return WIFI_ERROR_NOT_SUPPORTED; +#endif +} + +class SetNodfsCommand : public WifiCommand +{ +private: + u32 mNoDfs; + +public: + SetNodfsCommand(wifi_interface_handle handle, u32 nodfs) + : WifiCommand(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 subcmd message to driver, ret=%d", ret); + return ret; + } + + nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); + ret = mMsg.put_u32(WIFI_ATTRIBUTE_NODFS_VALUE, mNoDfs); + if (ret < 0) { + return ret; + } + + mMsg.attr_end(data); + return WIFI_SUCCESS; + } +}; + +wifi_error wifi_set_nodfs_flag(wifi_interface_handle handle, u32 nodfs) +{ +#if 0 + SetNodfsCommand command(handle, nodfs); + return (wifi_error)command.requestResponse(); +#else + return WIFI_ERROR_NOT_SUPPORTED; +#endif +} + +class SetCountryCodeCommand : public WifiCommand +{ +private: + const char *mCountryCode; + +public: + SetCountryCodeCommand(wifi_interface_handle handle, const char *country_code) + : WifiCommand(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 subcmd message to driver, ret=%d", ret); + return ret; + } + + nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); + ret = mMsg.put_string(WIFI_ATTRIBUTE_COUNTRY_CODE, mCountryCode); + if (ret < 0) { + return ret; + } + + mMsg.attr_end(data); + return WIFI_SUCCESS; + } +}; + +wifi_error wifi_set_country_code(wifi_interface_handle handle, const char *country_code) +{ + SetCountryCodeCommand command(handle, country_code); + return (wifi_error) command.requestResponse(); +} + +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(handle, id), mMax_rssi(max_rssi), mMin_rssi(min_rssi), mHandler(eh) + { + } + + int createRequest(WifiRequest& request, int enable) { + int result = request.create(GOOGLE_OUI, WIFI_SUBCMD_SET_RSSI_MONITOR); + if (result < 0) { + return result; + } + + ALOGI("set RSSI Monitor, mMax_rssi=%d, mMin_rssi=%d, enable=%d", mMax_rssi, mMin_rssi, enable); + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(WIFI_ATTRIBUTE_MAX_RSSI, (enable? mMax_rssi: 0)); + if (result < 0) { + return result; + } + result = request.put_u32(WIFI_ATTRIBUTE_MIN_RSSI, (enable? mMin_rssi: 0)); + if (result < 0) { + return result; + } + result = request.put_u32(WIFI_ATTRIBUTE_RSSI_MONITOR_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 < 0) { + return result; + } + + result = requestResponse(request); + if (result < 0) { + ALOGE("Failed to set RSSI Monitor, result=%d", result); + return result; + } + ALOGD("Successfully set RSSI monitoring"); + + registerVendorHandler(GOOGLE_OUI, WIFI_EVENT_RSSI_MONITOR); + + if (result < 0) { + unregisterVendorHandler(GOOGLE_OUI, WIFI_EVENT_RSSI_MONITOR); + 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 stop RSSI monitoring, result=%d", result); + } + } + unregisterVendorHandler(GOOGLE_OUI, WIFI_EVENT_RSSI_MONITOR); + return WIFI_SUCCESS; + } + + virtual int handleResponse(WifiEvent& reply) { + /* Nothing to do on response! */ + return NL_SKIP; + } + + virtual int handleEvent(WifiEvent& event) { + ALOGD("Got a RSSI monitor event"); + + //nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + struct nlattr *vendor_data = (struct nlattr *)event.get_vendor_data(); + int len = event.get_vendor_data_len(); + + if (vendor_data == NULL || len == 0) { + ALOGE("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 = NULL; + if (vendor_data->nla_type == WIFI_EVENT_RSSI_MONITOR) + data = (rssi_monitor_evt *)nla_data(vendor_data); + else + return NL_SKIP; + + ALOGI("data: version=%d, cur_rssi=%d BSSID=" MACSTR "\r\n", + data->version, data->cur_rssi, MAC2STR(data->BSSID)); + + if (data->version != RSSI_MONITOR_EVT_VERSION) { + ALOGE("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; + } +}; + +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) +{ + ALOGD("Start RSSI monitoring %d", id); + wifi_handle handle = getWifiHandle(iface); + SetRSSIMonitorCommand *cmd = new SetRSSIMonitorCommand(id, iface, max_rssi, min_rssi, eh); + wifi_register_cmd(handle, id, cmd); + return (wifi_error)cmd->start(); +} + +static wifi_error wifi_stop_rssi_monitoring(wifi_request_id id, wifi_interface_handle iface) +{ + ALOGD("Stopping RSSI monitoring"); + + if (id == -1) { + wifi_rssi_event_handler handler; + s8 max_rssi = 0, min_rssi = 0; + wifi_handle handle = getWifiHandle(iface); + memset(&handler, 0, sizeof(handler)); + SetRSSIMonitorCommand *cmd = new SetRSSIMonitorCommand(id, iface, max_rssi, min_rssi, handler); + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + + return wifi_cancel_cmd(id, iface); +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/mtk/wifi_hal/wifi_logger.cpp b/mtk/wifi_hal/wifi_logger.cpp new file mode 100644 index 0000000..23f097e --- /dev/null +++ b/mtk/wifi_hal/wifi_logger.cpp @@ -0,0 +1,812 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "nl80211_copy.h" +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +using namespace android; + +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, +} DEBUG_SUB_COMMAND; + +typedef enum { + LOGGER_ATTRIBUTE_DRIVER_VER, + LOGGER_ATTRIBUTE_FW_VER, + LOGGER_ATTRIBUTE_RING_ID, + LOGGER_ATTRIBUTE_RING_NAME, + LOGGER_ATTRIBUTE_RING_FLAGS, + LOGGER_ATTRIBUTE_LOG_LEVEL, + LOGGER_ATTRIBUTE_LOG_TIME_INTVAL, + LOGGER_ATTRIBUTE_LOG_MIN_DATA_SIZE, + LOGGER_ATTRIBUTE_FW_DUMP_LEN, + LOGGER_ATTRIBUTE_FW_DUMP_DATA, + // LOGGER_ATTRIBUTE_FW_ERR_CODE, + LOGGER_ATTRIBUTE_RING_DATA, + LOGGER_ATTRIBUTE_RING_STATUS, + LOGGER_ATTRIBUTE_RING_NUM, +} 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, +} GetCmdType; + + +/////////////////////////////////////////////////////////////////////////////// +class DebugCommand : public WifiCommand +{ + char *mBuff; + int *mBuffSize; + u32 *mNumRings; + wifi_ring_buffer_status *mStatus; + 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(iface, 0), mBuff(buffer), mBuffSize(buffer_size), mType(cmdType) + { + memset(mBuff, 0, *mBuffSize); + } + + // constructor for ring data + DebugCommand(wifi_interface_handle iface, char *ring_name, GetCmdType cmdType) + : WifiCommand(iface, 0), mRingName(ring_name), mType(cmdType) + { } + + // constructor for ring status + DebugCommand(wifi_interface_handle iface, u32 *num_rings, + wifi_ring_buffer_status *status, GetCmdType cmdType) + : WifiCommand(iface, 0), mNumRings(num_rings), mStatus(status), mType(cmdType) + { + memset(mStatus, 0, sizeof(wifi_ring_buffer_status) * (*mNumRings)); + } + + // constructor for feature set + DebugCommand(wifi_interface_handle iface, unsigned int *support, GetCmdType cmdType) + : WifiCommand(iface, 0), mSupport(support), mType(cmdType) + { } + + // 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(iface, 0), mVerboseLevel(verbose_level), mFlags(flags), + mMaxIntervalSec(max_interval_sec), mMinDataSize(min_data_size), + mRingName(ring_name), mType(cmdType) + { } + + 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 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"); + + 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) { + 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 = %d", len, sizeof(unsigned int)); + memcpy(mSupport, data, sizeof(unsigned int)); + 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); + return (wifi_error)cmd->start(); + } 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); + return (wifi_error)cmd->start(); + } 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); + return (wifi_error)cmd->start(); +} + +/* 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); + return (wifi_error)cmd->start(); + } 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); + return (wifi_error)cmd->start(); + } 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) { + DebugCommand *cmd = new DebugCommand(iface, verbose_level, flags, + max_interval_sec, min_data_size, ring_name, START_RING_LOG); + return (wifi_error)cmd->start(); + } 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(iface, id), mHandler(handler) + { } + SetLogHandler(wifi_interface_handle iface, int id) + : WifiCommand(iface, id) + { } + + int start() { + ALOGD("Register log handler"); + registerVendorHandler(GOOGLE_OUI, GOOGLE_DEBUG_RING_EVENT); + return WIFI_SUCCESS; + } + + virtual int cancel() { + /* Send a command to driver to stop generating logging events */ + ALOGD("Reset event handler"); + + 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; + } + + /* unregister event handler */ + unregisterVendorHandler(GOOGLE_OUI, GOOGLE_DEBUG_RING_EVENT); + ALOGD("Success to reset event handler"); + 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) { + memcpy(&status, it.get_data(), sizeof(status)); + } else if (it.get_type() == LOGGER_ATTRIBUTE_RING_DATA) { + buffer_size = it.get_len(); + buffer = (char *)it.get_data(); + } 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) { + (*mHandler.on_ring_buffer_data)((char *)status.name, buffer, buffer_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); + SetLogHandler *cmd = new SetLogHandler(iface, id, handler); + + ALOGI("Logger start, handle = %p", handle); + if (cmd) { + wifi_register_cmd(handle, id, cmd); + return (wifi_error)cmd->start(); + } else { + ALOGD("Out of memory"); + return WIFI_ERROR_OUT_OF_MEMORY; + } +} + +wifi_error wifi_reset_log_handler(wifi_request_id id, wifi_interface_handle iface) +{ + wifi_handle handle = getWifiHandle(iface); + SetLogHandler *cmd = new SetLogHandler(iface, id); + + ALOGI("Logger reset, handle = %p", handle); + if (cmd) { + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + return WIFI_ERROR_INVALID_ARGS; +} + +/////////////////////////////////////////////////////////////////////////////// +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(iface, id), mHandler(handler), mBuffSize(0), mBuff(NULL), mErrCode(0) + { } + + int start() { + ALOGD("Start Alerting"); + registerVendorHandler(GOOGLE_OUI, GOOGLE_DEBUG_MEM_DUMP_EVENT); + 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) { + wifi_ring_buffer_id ring_id; + char *buffer = NULL; + int buffer_size = 0; + + + 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) { + mErrCode = it.get_u32(); + */ + } else { + ALOGW("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + 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; + } +}; + +wifi_error wifi_set_alert_handler(wifi_request_id id, wifi_interface_handle iface, + wifi_alert_handler handler) +{ + wifi_handle handle = getWifiHandle(iface); + SetAlertHandler *cmd = new SetAlertHandler(iface, id, handler); + ALOGI("Alert start, handle = %p", handle); + + wifi_register_cmd(handle, id, cmd); + return (wifi_error)cmd->start(); +} + + +/////////////////////////////////////////////////////////////////////////////// +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(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); + return (wifi_error)cmd->start(); +} + diff --git a/mtk/wifi_hal/wifi_offload.cpp b/mtk/wifi_hal/wifi_offload.cpp new file mode 100644 index 0000000..f445527 --- /dev/null +++ b/mtk/wifi_hal/wifi_offload.cpp @@ -0,0 +1,229 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "nl80211_copy.h" +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +using namespace android; + +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_ID = 1, + MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN, + MKEEP_ALIVE_ATTRIBUTE_IP_PKT, + MKEEP_ALIVE_ATTRIBUTE_SRC_MAC_ADDR, + MKEEP_ALIVE_ATTRIBUTE_DST_MAC_ADDR, + MKEEP_ALIVE_ATTRIBUTE_PERIOD_MSEC +} 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; + +public: + + // constructor for start sending + MKeepAliveCommand(wifi_interface_handle iface, u8 index, u8 *ip_packet, u16 ip_packet_len, + u8 *src_mac_addr, u8 *dst_mac_addr, u32 period_msec, GetCmdType cmdType) + : WifiCommand(iface, 0), mIndex(index), 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(iface, 0), mIndex(index), mType(cmdType) + { } + + int createRequest(WifiRequest &request) { + int result; + + 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; + } + + 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, + 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)) { + MKeepAliveCommand *cmd = new MKeepAliveCommand(iface, index, ip_packet, ip_packet_len, + src_mac_addr, dst_mac_addr, period_msec, START_MKEEP_ALIVE); + wifi_error err = (wifi_error) cmd->start(); + delete cmd; + return err; + } 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); + wifi_error err = (wifi_error) cmd->start(); + delete cmd; + return err; + } else { + ALOGE("Invalid mkeep_alive parameters"); + return WIFI_ERROR_INVALID_ARGS; + } +}