diff options
author | Shawn Guo <shawn.guo@linaro.org> | 2018-10-10 10:24:47 +0800 |
---|---|---|
committer | Shawn Guo <shawn.guo@linaro.org> | 2018-10-23 09:24:44 +0800 |
commit | d9bd822cf11ffa4c2813e6027b8172a400f93505 (patch) | |
tree | dde109892403dbbef34a1b53cefa3258eaa4533f /wifi | |
parent | 7498d40d9e4e631b6a558a9c7a04eca1685dcd0b (diff) | |
download | poplar-d9bd822cf11ffa4c2813e6027b8172a400f93505.tar.gz |
Add poplar wifi hal source code
The poplar wifi hal derives from bcmdhd one with a few changes below.
- No GetChannelListCommand support for gscan.
- Use different vendor data structure without internal radio statistics
structure conversion for link_layer_stats.
- Drop BCMDHD_64_BIT_IPC build option support for wpa_supplicant_8_lib.
We should probably consolidate it with bcmdhd at hardware/ folder
level, but let's start from maintaining it at poplar device level before
we find it useful for more devices.
Change-Id: Ibef0f75f02f184b4feb7ba624407ce2f26984962
Diffstat (limited to 'wifi')
-rw-r--r-- | wifi/Android.mk | 3 | ||||
-rw-r--r-- | wifi/wifi_hal/Android.mk | 52 | ||||
-rw-r--r-- | wifi/wifi_hal/MODULE_LICENSE_APACHE2 | 0 | ||||
-rw-r--r-- | wifi/wifi_hal/common.cpp | 263 | ||||
-rw-r--r-- | wifi/wifi_hal/common.h | 274 | ||||
-rw-r--r-- | wifi/wifi_hal/cpp_bindings.cpp | 750 | ||||
-rw-r--r-- | wifi/wifi_hal/cpp_bindings.h | 369 | ||||
-rw-r--r-- | wifi/wifi_hal/gscan.cpp | 1853 | ||||
-rw-r--r-- | wifi/wifi_hal/link_layer_stats.cpp | 139 | ||||
-rw-r--r-- | wifi/wifi_hal/rtt.cpp | 701 | ||||
-rw-r--r-- | wifi/wifi_hal/sync.h | 71 | ||||
-rw-r--r-- | wifi/wifi_hal/wifi_hal.cpp | 1360 | ||||
-rw-r--r-- | wifi/wifi_hal/wifi_logger.cpp | 1109 | ||||
-rw-r--r-- | wifi/wifi_hal/wifi_offload.cpp | 249 | ||||
-rw-r--r-- | wifi/wpa_supplicant_8_lib/Android.mk | 74 | ||||
-rw-r--r-- | wifi/wpa_supplicant_8_lib/MODULE_LICENSE_BSD | 0 | ||||
-rw-r--r-- | wifi/wpa_supplicant_8_lib/NOTICE | 38 | ||||
-rw-r--r-- | wifi/wpa_supplicant_8_lib/driver_cmd_nl80211.c | 201 | ||||
-rw-r--r-- | wifi/wpa_supplicant_8_lib/driver_cmd_wext.c | 391 | ||||
-rw-r--r-- | wifi/wpa_supplicant_8_lib/driver_cmd_wext.h | 32 |
20 files changed, 7929 insertions, 0 deletions
diff --git a/wifi/Android.mk b/wifi/Android.mk new file mode 100644 index 0000000..6d222eb --- /dev/null +++ b/wifi/Android.mk @@ -0,0 +1,3 @@ +ifeq ($(BOARD_WLAN_DEVICE), rtl) + include $(call all-subdir-makefiles) +endif diff --git a/wifi/wifi_hal/Android.mk b/wifi/wifi_hal/Android.mk new file mode 100644 index 0000000..f56bfc4 --- /dev/null +++ b/wifi/wifi_hal/Android.mk @@ -0,0 +1,52 @@ +# Copyright (C) 2011 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) + +# Make the HAL library +# ============================================================ +include $(CLEAR_VARS) + +LOCAL_CFLAGS := \ + -Wall \ + -Werror \ + -Wno-format \ + -Wno-reorder \ + -Wno-unused-function \ + -Wno-unused-parameter \ + -Wno-unused-private-field \ + -Wno-unused-variable \ + +LOCAL_C_INCLUDES += \ + external/libnl/include \ + $(call include-path-for, libhardware_legacy)/hardware_legacy \ + external/wpa_supplicant_8/src/drivers + +LOCAL_HEADER_LIBRARIES := libutils_headers liblog_headers + +LOCAL_SRC_FILES := \ + wifi_hal.cpp \ + rtt.cpp \ + common.cpp \ + cpp_bindings.cpp \ + gscan.cpp \ + link_layer_stats.cpp \ + wifi_logger.cpp \ + wifi_offload.cpp + +LOCAL_MODULE := libwifi-hal-rtl +LOCAL_PROPRIETARY_MODULE := true + +include $(BUILD_STATIC_LIBRARY) + diff --git a/wifi/wifi_hal/MODULE_LICENSE_APACHE2 b/wifi/wifi_hal/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/wifi/wifi_hal/MODULE_LICENSE_APACHE2 diff --git a/wifi/wifi_hal/common.cpp b/wifi/wifi_hal/common.cpp new file mode 100644 index 0000000..3540d49 --- /dev/null +++ b/wifi/wifi_hal/common.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/errqueue.h> + +#include <linux/pkt_sched.h> +#include <netlink/object-api.h> +#include <netlink/netlink.h> +#include <netlink/socket.h> +#include <netlink/handlers.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +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 - 1) * sizeof(cmd_info)); + info->num_cmd--; + ALOGV("Successfully removed command %d: %p from %d", id, cmd, i); + break; + } + } + + if (!cmd) { + ALOGI("Failed to remove command %d: %p", id, cmd); + } + + return cmd; +} + +WifiCommand *wifi_get_cmd(wifi_handle handle, int id) +{ + hal_info *info = (hal_info *)handle; + + WifiCommand *cmd = NULL; + + for (int i = 0; i < info->num_cmd; i++) { + if (info->cmd[i].id == id) { + cmd = info->cmd[i].cmd; + break; + } + } + + return cmd; +} + +void wifi_unregister_cmd(wifi_handle handle, WifiCommand *cmd) +{ + hal_info *info = (hal_info *)handle; + + for (int i = 0; i < info->num_cmd; i++) { + if (info->cmd[i].cmd == cmd) { + int id = info->cmd[i].id; + memmove(&info->cmd[i], &info->cmd[i+1], (info->num_cmd - i - 1) * sizeof(cmd_info)); + info->num_cmd--; + ALOGV("Successfully removed command %d: %p from %d", id, cmd, i); + break; + } + } +} + +wifi_error wifi_cancel_cmd(wifi_request_id id, wifi_interface_handle iface) +{ + wifi_handle handle = getWifiHandle(iface); + + WifiCommand *cmd = wifi_unregister_cmd(handle, id); + ALOGV("Cancel WifiCommand = %p", cmd); + if (cmd) { + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + + return WIFI_ERROR_INVALID_ARGS; +} + diff --git a/wifi/wifi_hal/common.h b/wifi/wifi_hal/common.h new file mode 100644 index 0000000..28e2156 --- /dev/null +++ b/wifi/wifi_hal/common.h @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wifi_hal.h" + +#ifndef __WIFI_HAL_COMMON_H__ +#define __WIFI_HAL_COMMON_H__ + +#define LOG_TAG "WifiHAL" + +#include <log/log.h> +#include "nl80211_copy.h" +#include "sync.h" + +#define SOCKET_BUFFER_SIZE (32768U) +#define RECV_BUF_SIZE (4096) +#define DEFAULT_EVENT_CB_SIZE (64) +#define DEFAULT_CMD_SIZE (64) +#define DOT11_OUI_LEN 3 +#define DOT11_MAX_SSID_LEN 32 + +#define 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 */ + + +/* + 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 */ + VENDOR_NL80211_SUBCMD_UNSPECIFIED, + + /* define all vendor startup commands between 0x0 and 0x0FFF */ + VENDOR_NL80211_SUBCMD_RANGE_START = 0x0001, + VENDOR_NL80211_SUBCMD_RANGE_END = 0x0FFF, + + /* define all GScan related commands between 0x1000 and 0x10FF */ + ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START = 0x1000, + ANDROID_NL80211_SUBCMD_GSCAN_RANGE_END = 0x10FF, + + /* define all NearbyDiscovery related commands between 0x1100 and 0x11FF */ + ANDROID_NL80211_SUBCMD_NBD_RANGE_START = 0x1100, + ANDROID_NL80211_SUBCMD_NBD_RANGE_END = 0x11FF, + + /* define all RTT related commands between 0x1100 and 0x11FF */ + ANDROID_NL80211_SUBCMD_RTT_RANGE_START = 0x1100, + ANDROID_NL80211_SUBCMD_RTT_RANGE_END = 0x11FF, + + ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START = 0x1200, + ANDROID_NL80211_SUBCMD_LSTATS_RANGE_END = 0x12FF, + + /* define all Logger related commands between 0x1400 and 0x14FF */ + ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START = 0x1400, + ANDROID_NL80211_SUBCMD_DEBUG_RANGE_END = 0x14FF, + + /* define all wifi offload related commands between 0x1600 and 0x16FF */ + ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START = 0x1600, + ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_END = 0x16FF, + + /* define all NAN related commands between 0x1700 and 0x17FF */ + ANDROID_NL80211_SUBCMD_NAN_RANGE_START = 0x1700, + ANDROID_NL80211_SUBCMD_NAN_RANGE_END = 0x17FF, + + /* define all Android Packet Filter related commands between 0x1800 and 0x18FF */ + ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START = 0x1800, + ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_END = 0x18FF, + + /* This is reserved for future usage */ + +} ANDROID_VENDOR_SUB_COMMAND; + +typedef enum { + + GSCAN_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START, + + GSCAN_SUBCMD_SET_CONFIG, /* 0x1001 */ + + GSCAN_SUBCMD_SET_SCAN_CONFIG, /* 0x1002 */ + GSCAN_SUBCMD_ENABLE_GSCAN, /* 0x1003 */ + GSCAN_SUBCMD_GET_SCAN_RESULTS, /* 0x1004 */ + GSCAN_SUBCMD_SCAN_RESULTS, /* 0x1005 */ + + GSCAN_SUBCMD_SET_HOTLIST, /* 0x1006 */ + + GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_CONFIG, /* 0x1007 */ + GSCAN_SUBCMD_ENABLE_FULL_SCAN_RESULTS, /* 0x1008 */ + GSCAN_SUBCMD_GET_CHANNEL_LIST, /* 0x1009 */ + + WIFI_SUBCMD_GET_FEATURE_SET, /* 0x100A */ + WIFI_SUBCMD_GET_FEATURE_SET_MATRIX, /* 0x100B */ + WIFI_SUBCMD_SET_PNO_RANDOM_MAC_OUI, /* 0x100C */ + WIFI_SUBCMD_NODFS_SET, /* 0x100D */ + WIFI_SUBCMD_SET_COUNTRY_CODE, /* 0x100E */ + /* Add more sub commands here */ + GSCAN_SUBCMD_SET_EPNO_SSID, /* 0x100F */ + + WIFI_SUBCMD_SET_SSID_WHITE_LIST, /* 0x1010 */ + WIFI_SUBCMD_SET_ROAM_PARAMS, /* 0x1011 */ + WIFI_SUBCMD_ENABLE_LAZY_ROAM, /* 0x1012 */ + WIFI_SUBCMD_SET_BSSID_PREF, /* 0x1013 */ + WIFI_SUBCMD_SET_BSSID_BLACKLIST, /* 0x1014 */ + + GSCAN_SUBCMD_ANQPO_CONFIG, /* 0x1015 */ + WIFI_SUBCMD_SET_RSSI_MONITOR, /* 0x1016 */ + WIFI_SUBCMD_CONFIG_ND_OFFLOAD, /* 0x1017 */ + /* Add more sub commands here */ + + GSCAN_SUBCMD_MAX, + + APF_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START, + APF_SUBCMD_SET_FILTER, +} WIFI_SUB_COMMAND; + +typedef enum { + BRCM_RESERVED1, + BRCM_RESERVED2, + 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, + GSCAN_EVENT_EPNO_EVENT, + GOOGLE_DEBUG_RING_EVENT, + GOOGLE_DEBUG_MEM_DUMP_EVENT, + GSCAN_EVENT_ANQPO_HOTSPOT_MATCH, + GOOGLE_RSSI_MONITOR_EVENT +} WIFI_EVENT; + +typedef void (*wifi_internal_event_handler) (wifi_handle handle, int events); + +class WifiCommand; + +typedef struct { + int nl_cmd; + uint32_t vendor_id; + int vendor_subcmd; + nl_recvmsg_msg_cb_t cb_func; + void *cb_arg; +} cb_info; + +typedef struct { + wifi_request_id id; + WifiCommand *cmd; +} cmd_info; + +typedef struct { + wifi_handle handle; // handle to wifi data + char name[IFNAMSIZ+1]; // interface name + trailing null + int id; // id to use when talking to driver +} interface_info; + +typedef struct { + + struct nl_sock *cmd_sock; // command socket object + struct nl_sock *event_sock; // event socket object + int nl80211_family_id; // family id for 80211 driver + int cleanup_socks[2]; // sockets used to implement wifi_cleanup + + bool in_event_loop; // Indicates that event loop is active + bool clean_up; // Indication to exit since cleanup has started + + wifi_internal_event_handler event_handler; // default event handler + wifi_cleaned_up_handler cleaned_up_handler; // socket cleaned up handler + + cb_info *event_cb; // event callbacks + int num_event_cb; // number of event callbacks + int alloc_event_cb; // number of allocated callback objects + pthread_mutex_t cb_lock; // mutex for the event_cb access + + cmd_info *cmd; // Outstanding commands + int num_cmd; // number of commands + int alloc_cmd; // number of commands allocated + + interface_info **interfaces; // array of interfaces + int num_interfaces; // number of interfaces + + + // add other details +} hal_info; + +#define PNO_SSID_FOUND 0x1 +#define PNO_SSID_LOST 0x2 + +typedef struct wifi_pno_result { + unsigned char ssid[DOT11_MAX_SSID_LEN]; + unsigned char ssid_len; + signed char rssi; + u16 channel; + u16 flags; + mac_addr bssid; +} wifi_pno_result_t; + +typedef struct wifi_gscan_result { + u64 ts; // Time of discovery + u8 ssid[DOT11_MAX_SSID_LEN+1]; // null terminated + mac_addr bssid; // BSSID + u32 channel; // channel frequency in MHz + s32 rssi; // in db + u64 rtt; // in nanoseconds + u64 rtt_sd; // standard deviation in rtt + u16 beacon_period; // units are Kusec + u16 capability; // Capability information + u32 pad; +} wifi_gscan_result_t; + +typedef struct wifi_gscan_full_result { + wifi_gscan_result_t fixed; + u32 scan_ch_bucket; // scan chbucket bitmask + u32 ie_length; // byte length of Information Elements + u8 ie_data[1]; // IE data to follow +} wifi_gscan_full_result_t; + +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)) + +#define NULL_CHECK_RETURN(ptr, str, ret) \ + do { \ + if (!(ptr)) { \ + ALOGE("%s(): null pointer - #ptr (%s)\n", __FUNCTION__, str); \ + return ret; \ + } \ + } while (0) + +#endif + diff --git a/wifi/wifi_hal/cpp_bindings.cpp b/wifi/wifi_hal/cpp_bindings.cpp new file mode 100644 index 0000000..0ac4c08 --- /dev/null +++ b/wifi/wifi_hal/cpp_bindings.cpp @@ -0,0 +1,750 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/errqueue.h> + +#include <linux/pkt_sched.h> +#include <netlink/object-api.h> +#include <netlink/netlink.h> +#include <netlink/socket.h> +#include <netlink/handlers.h> + +#include <ctype.h> + +#include "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) { + + destroy(); + + mMsg = nlmsg_alloc(); + if (mMsg != NULL) { + genlmsg_put(mMsg, /* pid = */ 0, /* seq = */ 0, family, + hdrlen, flags, cmd, /* version = */ 0); + return WIFI_SUCCESS; + } else { + return WIFI_ERROR_OUT_OF_MEMORY; + } +} + +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); + } + + return res; +} + + +static int no_seq_check(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + +int WifiCommand::requestResponse() { + int err = create(); /* create the message */ + if (err < 0) { + return err; + } + + return requestResponse(mMsg); +} + +int WifiCommand::requestResponse(WifiRequest& request) { + int err = 0; + + struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!cb) + goto out; + + err = nl_send_auto_complete(mInfo->cmd_sock, request.getMessage()); /* send message */ + if (err < 0) + goto out; + + err = 1; + + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, response_handler, this); + + while (err > 0) { /* wait for reply */ + int res = nl_recvmsgs(mInfo->cmd_sock, cb); + if (res) { + ALOGE("nl80211: %s->nl_recvmsgs failed: %d", __func__, res); + } + } +out: + nl_cb_put(cb); + return err; +} + +int WifiCommand::requestEvent(int cmd) { + + ALOGD("requesting event %d", cmd); + + int res = wifi_register_handler(wifiHandle(), cmd, event_handler, this); + if (res < 0) { + return res; + } + + res = create(); /* create the message */ + if (res < 0) + goto out; + + ALOGD("waiting for response %d", cmd); + + res = nl_send_auto_complete(mInfo->cmd_sock, mMsg.getMessage()); /* send message */ + if (res < 0) + goto out; + + ALOGD("waiting for event %d", cmd); + res = mCondition.wait(); + if (res < 0) + goto out; + +out: + wifi_unregister_handler(wifiHandle(), cmd); + return res; +} + +int WifiCommand::requestVendorEvent(uint32_t id, int subcmd) { + + int res = wifi_register_vendor_handler(wifiHandle(), id, subcmd, event_handler, this); + if (res < 0) { + return res; + } + + res = create(); /* create the message */ + if (res < 0) + goto out; + + res = nl_send_auto_complete(mInfo->cmd_sock, mMsg.getMessage()); /* send message */ + if (res < 0) + goto out; + + res = mCondition.wait(); + if (res < 0) + goto out; + +out: + wifi_unregister_vendor_handler(wifiHandle(), id, subcmd); + return res; +} + +/* Event handlers */ +int WifiCommand::response_handler(struct nl_msg *msg, void *arg) { + // ALOGD("response_handler called"); + WifiCommand *cmd = (WifiCommand *)arg; + WifiEvent reply(msg); + int res = reply.parse(); + if (res < 0) { + ALOGE("Failed to parse reply message = %d", res); + return NL_SKIP; + } else { + // reply.log(); + return cmd->handleResponse(reply); + } +} + +int WifiCommand::event_handler(struct nl_msg *msg, void *arg) { + WifiCommand *cmd = (WifiCommand *)arg; + WifiEvent event(msg); + int res = event.parse(); + if (res < 0) { + ALOGE("Failed to parse event = %d", res); + res = NL_SKIP; + } else { + res = cmd->handleEvent(event); + } + + cmd->mCondition.signal(); + return res; +} + +/* Other event handlers */ +int WifiCommand::valid_handler(struct nl_msg *msg, void *arg) { + // ALOGD("valid_handler called"); + int *err = (int *)arg; + *err = 0; + return NL_SKIP; +} + +int WifiCommand::ack_handler(struct nl_msg *msg, void *arg) { + // ALOGD("ack_handler called"); + int *err = (int *)arg; + *err = 0; + return NL_STOP; +} + +int WifiCommand::finish_handler(struct nl_msg *msg, void *arg) { + // ALOGD("finish_handler called"); + int *ret = (int *)arg; + *ret = 0; + return NL_SKIP; +} + +int WifiCommand::error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { + int *ret = (int *)arg; + *ret = err->error; + + // ALOGD("error_handler received : %d", err->error); + return NL_SKIP; +} diff --git a/wifi/wifi_hal/cpp_bindings.h b/wifi/wifi_hal/cpp_bindings.h new file mode 100644 index 0000000..2c1aa05 --- /dev/null +++ b/wifi/wifi_hal/cpp_bindings.h @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wifi_hal.h" +#include "common.h" +#include "sync.h" + +class WifiEvent +{ + /* TODO: remove this when nl headers are updated */ + static const unsigned NL80211_ATTR_MAX_INTERNAL = 256; +private: + struct nl_msg *mMsg; + struct genlmsghdr *mHeader; + struct nlattr *mAttributes[NL80211_ATTR_MAX_INTERNAL + 1]; + +public: + WifiEvent(nl_msg *msg) { + mMsg = msg; + mHeader = NULL; + memset(mAttributes, 0, sizeof(mAttributes)); + } + ~WifiEvent() { + /* don't destroy mMsg; it doesn't belong to us */ + } + + void log(); + + int parse(); + + genlmsghdr *header() { + return mHeader; + } + + int get_cmd() { + return mHeader->cmd; + } + + int get_vendor_id() { + return get_u32(NL80211_ATTR_VENDOR_ID); + } + + int get_vendor_subcmd() { + return get_u32(NL80211_ATTR_VENDOR_SUBCMD); + } + + void *get_vendor_data() { + return get_data(NL80211_ATTR_VENDOR_DATA); + } + + int get_vendor_data_len() { + return get_len(NL80211_ATTR_VENDOR_DATA); + } + + const char *get_cmdString(); + + nlattr ** attributes() { + return mAttributes; + } + + nlattr *get_attribute(int attribute) { + return mAttributes[attribute]; + } + + uint8_t get_u8(int attribute) { + return mAttributes[attribute] ? nla_get_u8(mAttributes[attribute]) : 0; + } + + uint16_t get_u16(int attribute) { + return mAttributes[attribute] ? nla_get_u16(mAttributes[attribute]) : 0; + } + + uint32_t get_u32(int attribute) { + return mAttributes[attribute] ? nla_get_u32(mAttributes[attribute]) : 0; + } + + uint64_t get_u64(int attribute) { + return mAttributes[attribute] ? nla_get_u64(mAttributes[attribute]) : 0; + } + + int get_len(int attribute) { + return mAttributes[attribute] ? nla_len(mAttributes[attribute]) : 0; + } + + void *get_data(int attribute) { + return mAttributes[attribute] ? nla_data(mAttributes[attribute]) : NULL; + } + +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: + const char *mType; + hal_info *mInfo; + WifiRequest mMsg; + Condition mCondition; + wifi_request_id mId; + interface_info *mIfaceInfo; + int mRefs; +public: + WifiCommand(const char *type, wifi_handle handle, wifi_request_id id) + : mType(type), mMsg(getHalInfo(handle)->nl80211_family_id), mId(id), mRefs(1) + { + mIfaceInfo = NULL; + mInfo = getHalInfo(handle); + // ALOGD("WifiCommand %p created, mInfo = %p, mIfaceInfo = %p", this, mInfo, mIfaceInfo); + } + + WifiCommand(const char *type, wifi_interface_handle iface, wifi_request_id id) + : mType(type), mMsg(getHalInfo(iface)->nl80211_family_id, getIfaceInfo(iface)->id), + mId(id), mRefs(1) + { + mIfaceInfo = getIfaceInfo(iface); + mInfo = getHalInfo(iface); + // ALOGD("WifiCommand %p created, mInfo = %p, mIfaceInfo = %p", this, mInfo, mIfaceInfo); + } + + virtual ~WifiCommand() { + // ALOGD("WifiCommand %p destroyed", this); + } + + wifi_request_id id() { + return mId; + } + + const char *getType() { + return mType; + } + + virtual void addRef() { + int refs = __sync_add_and_fetch(&mRefs, 1); + // 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/wifi/wifi_hal/gscan.cpp b/wifi/wifi_hal/gscan.cpp new file mode 100644 index 0000000..6e3b32e --- /dev/null +++ b/wifi/wifi_hal/gscan.cpp @@ -0,0 +1,1853 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/errqueue.h> + +#include <linux/pkt_sched.h> +#include <netlink/object-api.h> +#include <netlink/netlink.h> +#include <netlink/socket.h> +#include <netlink/handlers.h> + +#include "sync.h" + +#define LOG_TAG "WifiHAL" +//#define LOG_NDEBUG 0 //uncomment to enable verbose logging + +#include <log/log.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +typedef enum { + + GSCAN_ATTRIBUTE_NUM_BUCKETS = 10, + GSCAN_ATTRIBUTE_BASE_PERIOD, + GSCAN_ATTRIBUTE_BUCKETS_BAND, + GSCAN_ATTRIBUTE_BUCKET_ID, + GSCAN_ATTRIBUTE_BUCKET_PERIOD, + GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS, + GSCAN_ATTRIBUTE_BUCKET_CHANNELS, + GSCAN_ATTRIBUTE_NUM_AP_PER_SCAN, + GSCAN_ATTRIBUTE_REPORT_THRESHOLD, + GSCAN_ATTRIBUTE_NUM_SCANS_TO_CACHE, + GSCAN_ATTRIBUTE_BAND = GSCAN_ATTRIBUTE_BUCKETS_BAND, + + GSCAN_ATTRIBUTE_ENABLE_FEATURE = 20, + GSCAN_ATTRIBUTE_SCAN_RESULTS_COMPLETE, /* indicates no more results */ + GSCAN_ATTRIBUTE_FLUSH_FEATURE, /* Flush all the configs */ + GSCAN_ENABLE_FULL_SCAN_RESULTS, + GSCAN_ATTRIBUTE_REPORT_EVENTS, + + /* remaining reserved for additional attributes */ + GSCAN_ATTRIBUTE_NUM_OF_RESULTS = 30, + GSCAN_ATTRIBUTE_FLUSH_RESULTS, + GSCAN_ATTRIBUTE_SCAN_RESULTS, /* flat array of wifi_scan_result */ + GSCAN_ATTRIBUTE_SCAN_ID, /* indicates scan number */ + GSCAN_ATTRIBUTE_SCAN_FLAGS, /* indicates if scan was aborted */ + GSCAN_ATTRIBUTE_AP_FLAGS, /* flags on significant change event */ + GSCAN_ATTRIBUTE_NUM_CHANNELS, + GSCAN_ATTRIBUTE_CHANNEL_LIST, + GSCAN_ATTRIBUTE_CH_BUCKET_BITMASK, + /* remaining reserved for additional attributes */ + + GSCAN_ATTRIBUTE_SSID = 40, + GSCAN_ATTRIBUTE_BSSID, + GSCAN_ATTRIBUTE_CHANNEL, + GSCAN_ATTRIBUTE_RSSI, + GSCAN_ATTRIBUTE_TIMESTAMP, + GSCAN_ATTRIBUTE_RTT, + GSCAN_ATTRIBUTE_RTTSD, + + /* remaining reserved for additional attributes */ + + GSCAN_ATTRIBUTE_HOTLIST_BSSIDS = 50, + GSCAN_ATTRIBUTE_RSSI_LOW, + GSCAN_ATTRIBUTE_RSSI_HIGH, + GSCAN_ATTRIBUTE_HOTLIST_ELEM, + GSCAN_ATTRIBUTE_HOTLIST_FLUSH, + GSCAN_ATTRIBUTE_HOTLIST_BSSID_COUNT, + + /* remaining reserved for additional attributes */ + GSCAN_ATTRIBUTE_RSSI_SAMPLE_SIZE = 60, + GSCAN_ATTRIBUTE_LOST_AP_SAMPLE_SIZE, + GSCAN_ATTRIBUTE_MIN_BREACHING, + GSCAN_ATTRIBUTE_SIGNIFICANT_CHANGE_BSSIDS, + GSCAN_ATTRIBUTE_SIGNIFICANT_CHANGE_FLUSH, + + /* EPNO */ + GSCAN_ATTRIBUTE_EPNO_SSID_LIST = 70, + GSCAN_ATTRIBUTE_EPNO_SSID, + GSCAN_ATTRIBUTE_EPNO_SSID_LEN, + GSCAN_ATTRIBUTE_EPNO_RSSI, + GSCAN_ATTRIBUTE_EPNO_FLAGS, + GSCAN_ATTRIBUTE_EPNO_AUTH, + GSCAN_ATTRIBUTE_EPNO_SSID_NUM, + GSCAN_ATTRIBUTE_EPNO_FLUSH, + + /* remaining reserved for additional attributes */ + + GSCAN_ATTRIBUTE_WHITELIST_SSID = 80, + GSCAN_ATTRIBUTE_NUM_WL_SSID, + GSCAN_ATTRIBUTE_WL_SSID_LEN, + GSCAN_ATTRIBUTE_WL_SSID_FLUSH, + GSCAN_ATTRIBUTE_WHITELIST_SSID_ELEM, + GSCAN_ATTRIBUTE_NUM_BSSID, + GSCAN_ATTRIBUTE_BSSID_PREF_LIST, + GSCAN_ATTRIBUTE_BSSID_PREF_FLUSH, + GSCAN_ATTRIBUTE_BSSID_PREF, + GSCAN_ATTRIBUTE_RSSI_MODIFIER, + + /* remaining reserved for additional attributes */ + + GSCAN_ATTRIBUTE_A_BAND_BOOST_THRESHOLD = 90, + GSCAN_ATTRIBUTE_A_BAND_PENALTY_THRESHOLD, + GSCAN_ATTRIBUTE_A_BAND_BOOST_FACTOR, + GSCAN_ATTRIBUTE_A_BAND_PENALTY_FACTOR, + GSCAN_ATTRIBUTE_A_BAND_MAX_BOOST, + GSCAN_ATTRIBUTE_LAZY_ROAM_HYSTERESIS, + GSCAN_ATTRIBUTE_ALERT_ROAM_RSSI_TRIGGER, + GSCAN_ATTRIBUTE_LAZY_ROAM_ENABLE, + + /* BSSID blacklist */ + GSCAN_ATTRIBUTE_BSSID_BLACKLIST_FLUSH = 100, + GSCAN_ATTRIBUTE_BLACKLIST_BSSID, + + /* ANQPO */ + GSCAN_ATTRIBUTE_ANQPO_HS_LIST = 110, + GSCAN_ATTRIBUTE_ANQPO_HS_LIST_SIZE, + GSCAN_ATTRIBUTE_ANQPO_HS_NETWORK_ID, + GSCAN_ATTRIBUTE_ANQPO_HS_NAI_REALM, + GSCAN_ATTRIBUTE_ANQPO_HS_ROAM_CONSORTIUM_ID, + GSCAN_ATTRIBUTE_ANQPO_HS_PLMN, + + /* Adaptive scan attributes */ + GSCAN_ATTRIBUTE_BUCKET_STEP_COUNT = 120, + GSCAN_ATTRIBUTE_BUCKET_MAX_PERIOD, + + /* ePNO cfg */ + GSCAN_ATTRIBUTE_EPNO_5G_RSSI_THR = 130, + GSCAN_ATTRIBUTE_EPNO_2G_RSSI_THR, + GSCAN_ATTRIBUTE_EPNO_INIT_SCORE_MAX, + GSCAN_ATTRIBUTE_EPNO_CUR_CONN_BONUS, + GSCAN_ATTRIBUTE_EPNO_SAME_NETWORK_BONUS, + GSCAN_ATTRIBUTE_EPNO_SECURE_BONUS, + GSCAN_ATTRIBUTE_EPNO_5G_BONUS, + + 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("GetGscanCapabilitiesCommand", iface, 0), mCapabilities(capabitlites) + { + memset(mCapabilities, 0, sizeof(*mCapabilities)); + } + + virtual int create() { + ALOGV("Creating message to get scan capablities; iface = %d", mIfaceInfo->id); + + 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) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + + void *data = reply.get_vendor_data(); + int len = reply.get_vendor_data_len(); + + ALOGV("Id = %0x, subcmd = %d, len = %d, expected len = %d", id, subcmd, len, + sizeof(*mCapabilities)); + + memcpy(mCapabilities, data, min(len, (int) sizeof(*mCapabilities))); + + 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(); +} + +class GetChannelListCommand : public WifiCommand +{ + wifi_channel *channels; + int max_channels; + int *num_channels; + int band; +public: + GetChannelListCommand(wifi_interface_handle iface, wifi_channel *channel_buf, int *ch_num, + int num_max_ch, int band) + : WifiCommand("GetChannelListCommand", iface, 0), channels(channel_buf), + max_channels(num_max_ch), num_channels(ch_num), band(band) + { + memset(channels, 0, sizeof(wifi_channel) * max_channels); + } + virtual int create() { + ALOGV("Creating message to get channel list; iface = %d", mIfaceInfo->id); + + int ret = mMsg.create(GOOGLE_OUI, GSCAN_SUBCMD_GET_CHANNEL_LIST); + if (ret < 0) { + return ret; + } + + nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); + ret = mMsg.put_u32(GSCAN_ATTRIBUTE_BAND, band); + if (ret < 0) { + return ret; + } + + mMsg.attr_end(data); + + return ret; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + + ALOGV("In GetChannelList::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + int num_channels_to_copy = 0; + + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + ALOGV("Id = %0x, subcmd = %d, len = %d", id, subcmd, len); + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in GetChannelList response; ignoring it"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == GSCAN_ATTRIBUTE_NUM_CHANNELS) { + num_channels_to_copy = it.get_u32(); + ALOGI("Got channel list with %d channels", num_channels_to_copy); + if(num_channels_to_copy > max_channels) + num_channels_to_copy = max_channels; + *num_channels = num_channels_to_copy; + } else if (it.get_type() == GSCAN_ATTRIBUTE_CHANNEL_LIST && num_channels_to_copy) { + memcpy(channels, it.get_data(), sizeof(int) * num_channels_to_copy); + } else { + ALOGW("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + + return NL_OK; + } +}; + +wifi_error wifi_get_valid_channels(wifi_interface_handle handle, + int band, int max_channels, wifi_channel *channels, int *num_channels) +{ + *num_channels = 12; + return WIFI_SUCCESS; +} +///////////////////////////////////////////////////////////////////////////// + +/* 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("FullScanResultsCommand", 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() { + ALOGV("Enabling Full scan results"); + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request, GSCAN_SUBCMD_ENABLE_FULL_SCAN_RESULTS, 1); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create request; result = %d", result); + return result; + } + + registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_FULL_SCAN_RESULTS); + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + 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() { + ALOGV("Disabling Full scan results"); + + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request, GSCAN_SUBCMD_ENABLE_FULL_SCAN_RESULTS, 0); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create request; result = %d", result); + } else { + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to disable full scan results;result = %d", result); + } + } + + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_FULL_SCAN_RESULTS); + return WIFI_SUCCESS; + } + + virtual int handleResponse(WifiEvent& reply) { + ALOGD("Request complete!"); + /* Nothing to do on response! */ + return NL_SKIP; + } + + virtual int handleEvent(WifiEvent& event) { + ALOGV("Full scan results: Got an event"); + return wifi_handle_full_scan_event(id(), event, mHandler); + } + +}; +///////////////////////////////////////////////////////////////////////////// + +class ScanCommand : public WifiCommand +{ + wifi_scan_cmd_params *mParams; + wifi_scan_result_handler mHandler; +public: + ScanCommand(wifi_interface_handle iface, int id, wifi_scan_cmd_params *params, + wifi_scan_result_handler handler) + : WifiCommand("ScanCommand", iface, id), mParams(params), mHandler(handler) + { } + + int createSetupRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_SET_CONFIG); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(GSCAN_ATTRIBUTE_BASE_PERIOD, mParams->base_period); + if (result < 0) { + return result; + } + + result = request.put_u32(GSCAN_ATTRIBUTE_NUM_BUCKETS, mParams->num_buckets); + if (result < 0) { + return result; + } + + for (int i = 0; i < mParams->num_buckets; i++) { + nlattr * bucket = request.attr_start(i); // next bucket + result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_ID, mParams->buckets[i].bucket); + if (result < 0) { + return result; + } + result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_PERIOD, mParams->buckets[i].period); + if (result < 0) { + return result; + } + result = request.put_u32(GSCAN_ATTRIBUTE_BUCKETS_BAND, + mParams->buckets[i].band); + if (result < 0) { + return result; + } + result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_STEP_COUNT, + mParams->buckets[i].step_count); + if (result < 0) { + return result; + } + result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_MAX_PERIOD, + mParams->buckets[i].max_period); + if (result < 0) { + return result; + } + result = request.put_u32(GSCAN_ATTRIBUTE_REPORT_EVENTS, + mParams->buckets[i].report_events); + if (result < 0) { + return result; + } + + result = request.put_u32(GSCAN_ATTRIBUTE_BUCKET_NUM_CHANNELS, + mParams->buckets[i].num_channels); + if (result < 0) { + return result; + } + + if (mParams->buckets[i].num_channels) { + nlattr *channels = request.attr_start(GSCAN_ATTRIBUTE_BUCKET_CHANNELS); + ALOGV(" channels: "); + for (int j = 0; j < mParams->buckets[i].num_channels; j++) { + result = request.put_u32(j, mParams->buckets[i].channels[j].channel); + ALOGV(" %u", mParams->buckets[i].channels[j].channel); + + if (result < 0) { + return result; + } + } + request.attr_end(channels); + } + + request.attr_end(bucket); + } + + request.attr_end(data); + return WIFI_SUCCESS; + } + + int createScanConfigRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_SET_SCAN_CONFIG); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(GSCAN_ATTRIBUTE_NUM_AP_PER_SCAN, mParams->max_ap_per_scan); + if (result < 0) { + return result; + } + + result = request.put_u32(GSCAN_ATTRIBUTE_REPORT_THRESHOLD, + mParams->report_threshold_percent); + if (result < 0) { + return result; + } + + int num_scans = mParams->report_threshold_num_scans; + + result = request.put_u32(GSCAN_ATTRIBUTE_NUM_SCANS_TO_CACHE, num_scans); + if (result < 0) { + return result; + } + + request.attr_end(data); + return WIFI_SUCCESS; + } + + int createStartRequest(WifiRequest& request) { + return createFeatureRequest(request, GSCAN_SUBCMD_ENABLE_GSCAN, 1); + } + + int createStopRequest(WifiRequest& request) { + return createFeatureRequest(request, GSCAN_SUBCMD_ENABLE_GSCAN, 0); + } + + int start() { + ALOGV("GSCAN start"); + WifiRequest request(familyId(), ifaceId()); + int result = createSetupRequest(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create setup request; result = %d", result); + return result; + } + + 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; + } + + ALOGV(" ....starting scan"); + + 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() { + ALOGV("Stopping scan"); + + WifiRequest request(familyId(), ifaceId()); + int result = createStopRequest(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create stop request; result = %d", result); + } else { + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to stop scan; result = %d", result); + } + } + + 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("Got a scan results event"); + //event.log(); + + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = event.get_vendor_data_len(); + int event_id = event.get_vendor_subcmd(); + + if ((event_id == GSCAN_EVENT_COMPLETE_SCAN) || + (event_id == GSCAN_EVENT_SCAN_RESULTS_AVAILABLE)) { + if (vendor_data == NULL || len != 4) { + ALOGI("Bad event data!"); + return NL_SKIP; + } + wifi_scan_event evt_type; + evt_type = (wifi_scan_event) event.get_u32(NL80211_ATTR_VENDOR_DATA); + ALOGV("Received event type %d", evt_type); + if(*mHandler.on_scan_event) + (*mHandler.on_scan_event)(id(), evt_type); + } else if (event_id == GSCAN_EVENT_FULL_SCAN_RESULTS) { + wifi_handle_full_scan_event(id(), event, mHandler); + } + return NL_SKIP; + } +}; + +wifi_error wifi_start_gscan( + wifi_request_id id, + wifi_interface_handle iface, + wifi_scan_cmd_params params, + wifi_scan_result_handler handler) +{ + wifi_handle handle = getWifiHandle(iface); + + ALOGV("Starting GScan, halHandle = %p", handle); + + ScanCommand *cmd = new ScanCommand(iface, id, ¶ms, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + +wifi_error wifi_stop_gscan(wifi_request_id id, wifi_interface_handle iface) +{ + wifi_handle handle = getWifiHandle(iface); + ALOGV("Stopping GScan, wifi_request_id = %d, halHandle = %p", id, handle); + + if (id == -1) { + wifi_scan_result_handler handler; + wifi_scan_cmd_params dummy_params; + wifi_handle handle = getWifiHandle(iface); + memset(&handler, 0, sizeof(handler)); + + ScanCommand *cmd = new ScanCommand(iface, id, &dummy_params, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + + return wifi_cancel_cmd(id, iface); +} + +wifi_error wifi_enable_full_scan_results( + wifi_request_id id, + wifi_interface_handle iface, + wifi_scan_result_handler handler) +{ + wifi_handle handle = getWifiHandle(iface); + int params_dummy; + + ALOGV("Enabling full scan results, halHandle = %p", handle); + + FullScanResultsCommand *cmd = new FullScanResultsCommand(iface, id, ¶ms_dummy, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + +int wifi_handle_full_scan_event( + wifi_request_id id, + WifiEvent& event, + wifi_scan_result_handler handler) +{ + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + unsigned int len = event.get_vendor_data_len(); + + if (vendor_data == NULL || len < sizeof(wifi_gscan_full_result_t)) { + ALOGI("Full scan results: No scan results found"); + return NL_SKIP; + } + + wifi_gscan_full_result_t *drv_res = (wifi_gscan_full_result_t *)event.get_vendor_data(); + /* To protect against corrupted data, put a ceiling */ + int ie_len = min(MAX_PROBE_RESP_IE_LEN, drv_res->ie_length); + wifi_scan_result *full_scan_result; + wifi_gscan_result_t *fixed = &drv_res->fixed; + + if ((ie_len + offsetof(wifi_gscan_full_result_t, ie_data)) > len) { + ALOGE("BAD event data, len %d ie_len %d fixed length %d!\n", len, + ie_len, offsetof(wifi_gscan_full_result_t, ie_data)); + return NL_SKIP; + } + full_scan_result = (wifi_scan_result *) malloc((ie_len + offsetof(wifi_scan_result, ie_data))); + if (!full_scan_result) { + ALOGE("Full scan results: Can't malloc!\n"); + return NL_SKIP; + } + convert_to_hal_result(full_scan_result, fixed); + full_scan_result->ie_length = ie_len; + memcpy(full_scan_result->ie_data, drv_res->ie_data, ie_len); + if(handler.on_full_scan_result) + handler.on_full_scan_result(id, full_scan_result, drv_res->scan_ch_bucket); + + ALOGV("Full scan result: %-32s %02x:%02x:%02x:%02x:%02x:%02x %d %d %lld %lld %lld %x %d\n", + fixed->ssid, fixed->bssid[0], fixed->bssid[1], fixed->bssid[2], fixed->bssid[3], + fixed->bssid[4], fixed->bssid[5], fixed->rssi, fixed->channel, fixed->ts, + fixed->rtt, fixed->rtt_sd, drv_res->scan_ch_bucket, drv_res->ie_length); + free(full_scan_result); + return NL_SKIP; +} + + +wifi_error wifi_disable_full_scan_results(wifi_request_id id, wifi_interface_handle iface) +{ + ALOGV("Disabling full scan results"); + 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); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + + return wifi_cancel_cmd(id, iface); +} + + +///////////////////////////////////////////////////////////////////////////// + +class GetScanResultsCommand : public WifiCommand { + wifi_cached_scan_results *mScans; + int mMax; + int *mNum; + int mRetrieved; + byte mFlush; + int mCompleted; +public: + GetScanResultsCommand(wifi_interface_handle iface, byte flush, + wifi_cached_scan_results *results, int max, int *num) + : WifiCommand("GetScanResultsCommand", iface, -1), mScans(results), mMax(max), mNum(num), + mRetrieved(0), mFlush(flush), mCompleted(0) + { } + + int createRequest(WifiRequest& request, int num, byte flush) { + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_GET_SCAN_RESULTS); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(GSCAN_ATTRIBUTE_NUM_OF_RESULTS, num); + if (result < 0) { + return result; + } + + result = request.put_u8(GSCAN_ATTRIBUTE_FLUSH_RESULTS, flush); + if (result < 0) { + return result; + } + + request.attr_end(data); + return WIFI_SUCCESS; + } + + int execute() { + WifiRequest request(familyId(), ifaceId()); + ALOGV("retrieving %d scan results", mMax); + + for (int i = 0; i < 10 && mRetrieved < mMax; i++) { + int num_to_retrieve = mMax - mRetrieved; + // ALOGI("retrieving %d scan results in one shot", num_to_retrieve); + int result = createRequest(request, num_to_retrieve, mFlush); + if (result < 0) { + ALOGE("failed to create request"); + return result; + } + + int prev_retrieved = mRetrieved; + + result = requestResponse(request); + + if (result != WIFI_SUCCESS) { + ALOGE("failed to retrieve scan results; result = %d", result); + return result; + } + + if (mRetrieved == prev_retrieved || mCompleted) { + /* no more items left to retrieve */ + break; + } + + request.destroy(); + } + + ALOGV("GetScanResults read %d results", mRetrieved); + *mNum = mRetrieved; + return WIFI_SUCCESS; + } + + virtual int handleResponse(WifiEvent& reply) { + ALOGV("In GetScanResultsCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + + ALOGV("Id = %0x, subcmd = %d", id, subcmd); + + /* + if (subcmd != GSCAN_SUBCMD_SCAN_RESULTS) { + ALOGE("Invalid response to GetScanResultsCommand; ignoring it"); + return NL_SKIP; + } + */ + + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in GetScanResults response; ignoring it"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == GSCAN_ATTRIBUTE_SCAN_RESULTS_COMPLETE) { + mCompleted = it.get_u8(); + ALOGV("retrieved mCompleted flag : %d", mCompleted); + } else if (it.get_type() == GSCAN_ATTRIBUTE_SCAN_RESULTS || it.get_type() == 0) { + int scan_id = 0, flags = 0, num = 0, scan_ch_bucket_mask = 0; + for (nl_iterator it2(it.get()); it2.has_next(); it2.next()) { + if (it2.get_type() == GSCAN_ATTRIBUTE_SCAN_ID) { + scan_id = it2.get_u32(); + ALOGV("retrieved scan_id : 0x%0x", scan_id); + } else if (it2.get_type() == GSCAN_ATTRIBUTE_SCAN_FLAGS) { + flags = it2.get_u8(); + ALOGV("retrieved scan_flags : 0x%0x", flags); + } else if (it2.get_type() == GSCAN_ATTRIBUTE_NUM_OF_RESULTS) { + num = it2.get_u32(); + ALOGV("retrieved num_results: %d", num); + } else if (it2.get_type() == GSCAN_ATTRIBUTE_CH_BUCKET_BITMASK) { + scan_ch_bucket_mask = it2.get_u32(); + ALOGD("retrieved scan_ch_bucket_mask: %x", scan_ch_bucket_mask); + } else if (it2.get_type() == GSCAN_ATTRIBUTE_SCAN_RESULTS && num) { + if (mRetrieved >= mMax) { + ALOGW("Stored %d scans, ignoring excess results", mRetrieved); + break; + } + num = min(num, (int)(it2.get_len()/sizeof(wifi_gscan_result))); + num = min(num, (int)MAX_AP_CACHE_PER_SCAN); + ALOGV("Copying %d scan results", num); + wifi_gscan_result_t *results = (wifi_gscan_result_t *)it2.get_data(); + wifi_scan_result *mScanResults = mScans[mRetrieved].results; + + for (int i = 0; i < num; i++) { + wifi_gscan_result_t *result = &results[i]; + convert_to_hal_result(&mScanResults[i], result); + mScanResults[i].ie_length = 0; + ALOGV("%02d %-32s %02x:%02x:%02x:%02x:%02x:%02x %04d", i, + result->ssid, result->bssid[0], result->bssid[1], result->bssid[2], + result->bssid[3], result->bssid[4], result->bssid[5], + result->rssi); + } + mScans[mRetrieved].scan_id = scan_id; + mScans[mRetrieved].flags = flags; + mScans[mRetrieved].num_results = num; + mScans[mRetrieved].buckets_scanned = scan_ch_bucket_mask; + ALOGV("Setting result of scan_id : 0x%0x", mScans[mRetrieved].scan_id); + mRetrieved++; + } else { + ALOGW("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + } else { + ALOGW("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + ALOGV("GetScanResults read %d results", mRetrieved); + return NL_OK; + } +}; + +wifi_error wifi_get_cached_gscan_results(wifi_interface_handle iface, byte flush, + int max, wifi_cached_scan_results *results, int *num) { + ALOGV("Getting cached scan results, iface handle = %p, num = %d", iface, *num); + + GetScanResultsCommand *cmd = new GetScanResultsCommand(iface, flush, results, max, num); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error err = (wifi_error)cmd->execute(); + cmd->releaseRef(); + return err; +} + +///////////////////////////////////////////////////////////////////////////// + +class BssidHotlistCommand : public WifiCommand +{ +private: + wifi_bssid_hotlist_params mParams; + wifi_hotlist_ap_found_handler mHandler; + static const int MAX_RESULTS = 64; + wifi_scan_result mResults[MAX_RESULTS]; +public: + BssidHotlistCommand(wifi_interface_handle handle, int id, + wifi_bssid_hotlist_params params, wifi_hotlist_ap_found_handler handler) + : WifiCommand("BssidHotlistCommand", handle, id), mParams(params), mHandler(handler) + { } + + int createSetupRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_SET_HOTLIST); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u8(GSCAN_ATTRIBUTE_HOTLIST_FLUSH, 1); + if (result < 0) { + return result; + } + + result = request.put_u32(GSCAN_ATTRIBUTE_LOST_AP_SAMPLE_SIZE, mParams.lost_ap_sample_size); + if (result < 0) { + return result; + } + + result = request.put_u32(GSCAN_ATTRIBUTE_HOTLIST_BSSID_COUNT, mParams.num_bssid); + if (result < 0) { + return result; + } + + struct nlattr * attr = request.attr_start(GSCAN_ATTRIBUTE_HOTLIST_BSSIDS); + for (int i = 0; i < mParams.num_bssid; i++) { + nlattr *attr2 = request.attr_start(GSCAN_ATTRIBUTE_HOTLIST_ELEM); + if (attr2 == NULL) { + return WIFI_ERROR_OUT_OF_MEMORY; + } + result = request.put_addr(GSCAN_ATTRIBUTE_BSSID, mParams.ap[i].bssid); + if (result < 0) { + return result; + } + result = request.put_u8(GSCAN_ATTRIBUTE_RSSI_HIGH, mParams.ap[i].high); + if (result < 0) { + return result; + } + result = request.put_u8(GSCAN_ATTRIBUTE_RSSI_LOW, mParams.ap[i].low); + if (result < 0) { + return result; + } + request.attr_end(attr2); + } + + request.attr_end(attr); + request.attr_end(data); + return result; + } + + int createTeardownRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_SET_HOTLIST); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u8(GSCAN_ATTRIBUTE_HOTLIST_FLUSH, 1); + if (result < 0) { + return result; + } + + struct nlattr * attr = request.attr_start(GSCAN_ATTRIBUTE_HOTLIST_BSSIDS); + request.attr_end(attr); + request.attr_end(data); + return result; + } + + int start() { + ALOGI("Executing hotlist setup request, num = %d", mParams.num_bssid); + WifiRequest request(familyId(), ifaceId()); + int result = createSetupRequest(request); + if (result < 0) { + return result; + } + + 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) { + 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) { + ALOGI("Hotlist AP event"); + int event_id = event.get_vendor_subcmd(); + // event.log(); + + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = event.get_vendor_data_len(); + + if (vendor_data == NULL || len == 0) { + ALOGI("No scan results found"); + return NL_SKIP; + } + + memset(mResults, 0, sizeof(wifi_scan_result) * MAX_RESULTS); + + int num = len / sizeof(wifi_gscan_result_t); + wifi_gscan_result_t *inp = (wifi_gscan_result_t *)event.get_vendor_data(); + num = min(MAX_RESULTS, num); + for (int i = 0; i < num; i++, inp++) { + convert_to_hal_result(&(mResults[i]), inp); + } + + if (event_id == GSCAN_EVENT_HOTLIST_RESULTS_FOUND) { + ALOGI("FOUND %d hotlist APs", num); + if (*mHandler.on_hotlist_ap_found) + (*mHandler.on_hotlist_ap_found)(id(), num, mResults); + } else if (event_id == GSCAN_EVENT_HOTLIST_RESULTS_LOST) { + ALOGI("LOST %d hotlist APs", num); + if (*mHandler.on_hotlist_ap_lost) + (*mHandler.on_hotlist_ap_lost)(id(), num, mResults); + } + return NL_SKIP; + } +}; + +class ePNOCommand : public WifiCommand +{ +private: + wifi_epno_params epno_params; + wifi_epno_handler mHandler; + wifi_scan_result mResults[MAX_EPNO_NETWORKS]; +public: + ePNOCommand(wifi_interface_handle handle, int id, + const wifi_epno_params *params, wifi_epno_handler handler) + : WifiCommand("ePNOCommand", handle, id), mHandler(handler) + { + if (params != NULL) { + memcpy(&epno_params, params, sizeof(wifi_epno_params)); + } else { + memset(&epno_params, 0, sizeof(wifi_epno_params)); + } + } + int createSetupRequest(WifiRequest& request) { + if (epno_params.num_networks > MAX_EPNO_NETWORKS) { + ALOGE("wrong epno num_networks:%d", epno_params.num_networks); + return WIFI_ERROR_INVALID_ARGS; + } + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_SET_EPNO_SSID); + if (result < 0) { + return result; + } + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u8(GSCAN_ATTRIBUTE_EPNO_FLUSH, 1); + if (result < 0) { + return result; + } + + result = request.put_u8(GSCAN_ATTRIBUTE_EPNO_5G_RSSI_THR, + (u8)epno_params.min5GHz_rssi); + if (result < 0) { + return result; + } + result = request.put_u8(GSCAN_ATTRIBUTE_EPNO_2G_RSSI_THR, + (u8)epno_params.min24GHz_rssi); + if (result < 0) { + return result; + } + result = request.put_u16(GSCAN_ATTRIBUTE_EPNO_INIT_SCORE_MAX, + epno_params.initial_score_max); + if (result < 0) { + return result; + } + result = request.put_u16(GSCAN_ATTRIBUTE_EPNO_CUR_CONN_BONUS, + epno_params.current_connection_bonus); + if (result < 0) { + return result; + } + result = request.put_u16(GSCAN_ATTRIBUTE_EPNO_SAME_NETWORK_BONUS, + epno_params.same_network_bonus); + if (result < 0) { + return result; + } + result = request.put_u16(GSCAN_ATTRIBUTE_EPNO_SECURE_BONUS, + epno_params.secure_bonus); + if (result < 0) { + return result; + } + result = request.put_u16(GSCAN_ATTRIBUTE_EPNO_5G_BONUS, + epno_params.band5GHz_bonus); + if (result < 0) { + return result; + } + result = request.put_u8(GSCAN_ATTRIBUTE_EPNO_SSID_NUM, + epno_params.num_networks); + if (result < 0) { + return result; + } + struct nlattr * attr = request.attr_start(GSCAN_ATTRIBUTE_EPNO_SSID_LIST); + wifi_epno_network *ssid_list = epno_params.networks; + for (int i = 0; i < epno_params.num_networks; i++) { + nlattr *attr2 = request.attr_start(i); + if (attr2 == NULL) { + return WIFI_ERROR_OUT_OF_MEMORY; + } + 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); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + +wifi_error wifi_reset_bssid_hotlist(wifi_request_id id, wifi_interface_handle iface) +{ + return wifi_cancel_cmd(id, iface); +} + + +///////////////////////////////////////////////////////////////////////////// + +class SignificantWifiChangeCommand : public WifiCommand +{ + typedef struct { + mac_addr bssid; // BSSID + wifi_channel channel; // channel frequency in MHz + int num_rssi; // number of rssi samples + wifi_rssi rssi[8]; // RSSI history in db + } wifi_significant_change_result_internal; + +private: + wifi_significant_change_params mParams; + wifi_significant_change_handler mHandler; + static const int MAX_RESULTS = 64; + wifi_significant_change_result_internal mResultsBuffer[MAX_RESULTS]; + wifi_significant_change_result *mResults[MAX_RESULTS]; +public: + SignificantWifiChangeCommand(wifi_interface_handle handle, int id, + wifi_significant_change_params params, wifi_significant_change_handler handler) + : WifiCommand("SignificantWifiChangeCommand", handle, id), mParams(params), + mHandler(handler) + { } + + int createSetupRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_CONFIG); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u8(GSCAN_ATTRIBUTE_SIGNIFICANT_CHANGE_FLUSH, 1); + if (result < 0) { + return result; + } + result = request.put_u16(GSCAN_ATTRIBUTE_RSSI_SAMPLE_SIZE, mParams.rssi_sample_size); + if (result < 0) { + return result; + } + result = request.put_u16(GSCAN_ATTRIBUTE_LOST_AP_SAMPLE_SIZE, mParams.lost_ap_sample_size); + if (result < 0) { + return result; + } + result = request.put_u16(GSCAN_ATTRIBUTE_MIN_BREACHING, mParams.min_breaching); + if (result < 0) { + return result; + } + result = request.put_u16(GSCAN_ATTRIBUTE_NUM_BSSID, mParams.num_bssid); + if (result < 0) { + return result; + } + if (mParams.num_bssid != 0) { + nlattr* attr = request.attr_start(GSCAN_ATTRIBUTE_SIGNIFICANT_CHANGE_BSSIDS); + if (attr == NULL) { + return WIFI_ERROR_OUT_OF_MEMORY; + } + + for (int i = 0; i < mParams.num_bssid; i++) { + nlattr* attr2 = request.attr_start(i); + if (attr2 == NULL) { + return WIFI_ERROR_OUT_OF_MEMORY; + } + result = request.put_addr(GSCAN_ATTRIBUTE_BSSID, mParams.ap[i].bssid); + if (result < 0) { + return result; + } + result = request.put_u8(GSCAN_ATTRIBUTE_RSSI_HIGH, mParams.ap[i].high); + if (result < 0) { + return result; + } + result = request.put_u8(GSCAN_ATTRIBUTE_RSSI_LOW, mParams.ap[i].low); + if (result < 0) { + return result; + } + request.attr_end(attr2); + } + + request.attr_end(attr); + } + request.attr_end(data); + + return result; + } + + int createTeardownRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_CONFIG); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u16(GSCAN_ATTRIBUTE_SIGNIFICANT_CHANGE_FLUSH, 1); + if (result < 0) { + return result; + } + + request.attr_end(data); + return result; + } + + int start() { + ALOGI("Set significant wifi change config"); + WifiRequest request(familyId(), ifaceId()); + + int result = createSetupRequest(request); + if (result < 0) { + return result; + } + + result = requestResponse(request); + if (result < 0) { + ALOGI("failed to set significant wifi change config %d", result); + return result; + } + + ALOGI("successfully set significant wifi change config"); + + result = createFeatureRequest(request, GSCAN_SUBCMD_ENABLE_GSCAN, 1); + if (result < 0) { + return result; + } + + registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS); + + result = requestResponse(request); + if (result < 0) { + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS); + return result; + } + + ALOGI("successfully restarted the scan"); + return result; + } + + virtual int cancel() { + /* unregister event handler */ + unregisterVendorHandler(GOOGLE_OUI, GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS); + + /* create set significant change monitor message with empty hotlist */ + WifiRequest request(familyId(), ifaceId()); + + int result = createTeardownRequest(request); + if (result < 0) { + return result; + } + + result = requestResponse(request); + if (result < 0) { + return result; + } + + ALOGI("successfully reset significant wifi change config"); + return result; + } + + virtual int handleResponse(WifiEvent& reply) { + /* Nothing to do on response! */ + return NL_SKIP; + } + + virtual int handleEvent(WifiEvent& event) { + ALOGV("Got a significant wifi change event"); + + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = event.get_vendor_data_len(); + + if (vendor_data == NULL || len == 0) { + ALOGI("No scan results found"); + return NL_SKIP; + } + + typedef struct { + uint16_t flags; + uint16_t channel; + mac_addr bssid; + s8 rssi_history[8]; + } ChangeInfo; + + int num = min(len / sizeof(ChangeInfo), MAX_RESULTS); + ChangeInfo *ci = (ChangeInfo *)event.get_vendor_data(); + + for (int i = 0; i < num; i++) { + memcpy(mResultsBuffer[i].bssid, ci[i].bssid, sizeof(mac_addr)); + mResultsBuffer[i].channel = ci[i].channel; + mResultsBuffer[i].num_rssi = 8; + for (int j = 0; j < mResultsBuffer[i].num_rssi; j++) + mResultsBuffer[i].rssi[j] = (int) ci[i].rssi_history[j]; + mResults[i] = reinterpret_cast<wifi_significant_change_result *>(&(mResultsBuffer[i])); + } + + ALOGV("Retrieved %d scan results", num); + + if (num != 0) { + (*mHandler.on_significant_change)(id(), num, mResults); + } else { + ALOGW("No significant change reported"); + } + + return NL_SKIP; + } +}; + +wifi_error wifi_set_significant_change_handler(wifi_request_id id, wifi_interface_handle iface, + wifi_significant_change_params params, wifi_significant_change_handler handler) +{ + wifi_handle handle = getWifiHandle(iface); + + SignificantWifiChangeCommand *cmd = new SignificantWifiChangeCommand( + iface, id, params, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + +wifi_error wifi_reset_significant_change_handler(wifi_request_id id, wifi_interface_handle iface) +{ + return wifi_cancel_cmd(id, iface); +} + +wifi_error wifi_reset_epno_list(wifi_request_id id, wifi_interface_handle iface) +{ + if (id == -1) { + wifi_epno_handler handler; + wifi_handle handle = getWifiHandle(iface); + + memset(&handler, 0, sizeof(handler)); + ePNOCommand *cmd = new ePNOCommand(iface, id, NULL, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + return wifi_cancel_cmd(id, iface); +} + +wifi_error wifi_set_epno_list(wifi_request_id id, wifi_interface_handle iface, + const wifi_epno_params *params, wifi_epno_handler handler) +{ + wifi_handle handle = getWifiHandle(iface); + + ePNOCommand *cmd = new ePNOCommand(iface, id, params, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + + +//////////////////////////////////////////////////////////////////////////////// + +class AnqpoConfigureCommand : public WifiCommand +{ + int num_hs; + wifi_passpoint_network *mNetworks; + wifi_passpoint_event_handler mHandler; + wifi_scan_result *mResult; +public: + AnqpoConfigureCommand(wifi_request_id id, wifi_interface_handle iface, + int num, wifi_passpoint_network *hs_list, wifi_passpoint_event_handler handler) + : WifiCommand("AnqpoConfigureCommand", iface, id), num_hs(num), mNetworks(hs_list), + mHandler(handler) + { + mResult = NULL; + } + + int createRequest(WifiRequest& request, int val) { + + int result = request.create(GOOGLE_OUI, GSCAN_SUBCMD_ANQPO_CONFIG); + result = request.put_u32(GSCAN_ATTRIBUTE_ANQPO_HS_LIST_SIZE, num_hs); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + + struct nlattr * attr = request.attr_start(GSCAN_ATTRIBUTE_ANQPO_HS_LIST); + for (int i = 0; i < num_hs; i++) { + nlattr *attr2 = request.attr_start(i); + if (attr2 == NULL) { + return WIFI_ERROR_OUT_OF_MEMORY; + } + result = request.put_u32(GSCAN_ATTRIBUTE_ANQPO_HS_NETWORK_ID, mNetworks[i].id); + if (result < 0) { + return result; + } + result = request.put(GSCAN_ATTRIBUTE_ANQPO_HS_NAI_REALM, mNetworks[i].realm, 256); + if (result < 0) { + return result; + } + result = request.put(GSCAN_ATTRIBUTE_ANQPO_HS_ROAM_CONSORTIUM_ID, + mNetworks[i].roamingConsortiumIds, 128); + if (result < 0) { + return result; + } + result = request.put(GSCAN_ATTRIBUTE_ANQPO_HS_PLMN, mNetworks[i].plmn, 3); + if (result < 0) { + return result; + } + + request.attr_end(attr2); + } + + request.attr_end(attr); + request.attr_end(data); + + return WIFI_SUCCESS; + } + + int start() { + + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request, num_hs); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create request; result = %d", result); + return result; + } + + registerVendorHandler(GOOGLE_OUI, GSCAN_EVENT_ANQPO_HOTSPOT_MATCH); + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + 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); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + +wifi_error wifi_reset_passpoint_list(wifi_request_id id, wifi_interface_handle iface) +{ + return wifi_cancel_cmd(id, iface); +} diff --git a/wifi/wifi_hal/link_layer_stats.cpp b/wifi/wifi_hal/link_layer_stats.cpp new file mode 100644 index 0000000..b2b85e4 --- /dev/null +++ b/wifi/wifi_hal/link_layer_stats.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/errqueue.h> + +#include <linux/pkt_sched.h> +#include <netlink/object-api.h> +#include <netlink/netlink.h> +#include <netlink/socket.h> +#include <netlink/handlers.h> + +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include <log/log.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +enum { + LSTATS_SUBCMD_GET_INFO = ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START, +}; + +class GetLinkStatsCommand : public WifiCommand +{ + wifi_stats_result_handler mHandler; +public: + GetLinkStatsCommand(wifi_interface_handle iface, wifi_stats_result_handler handler) + : WifiCommand("GetLinkStatsCommand", iface, 0), mHandler(handler) + { } + + virtual int create() { + // ALOGI("Creating message to get link statistics; iface = %d", mIfaceInfo->id); + + int ret = mMsg.create(GOOGLE_OUI, LSTATS_SUBCMD_GET_INFO); + if (ret < 0) { + ALOGE("Failed to create %x - %d", LSTATS_SUBCMD_GET_INFO, ret); + return ret; + } + + return ret; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + + // ALOGI("In GetLinkStatsCommand::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(); + + // ALOGI("Id = %0x, subcmd = %d", id, subcmd); + + void *data = reply.get_vendor_data(); + int len = reply.get_vendor_data_len(); + wifi_radio_stat *radio_stat = (wifi_radio_stat *)data; + if (!radio_stat) { + ALOGE("Invalid stats pointer received"); + return NL_SKIP; + } + radio_stat->tx_time_per_levels = (u32*)((char*)data + sizeof(wifi_radio_stat) + sizeof(wifi_iface_stat)); + if (radio_stat->num_channels > 11) { + ALOGE("Incorrect number of channels = %d", radio_stat->num_channels); + // dump data before num_channels + ALOGE("radio: = %d", radio_stat->radio); + ALOGE("on_time: = %d", radio_stat->on_time); + ALOGE("tx_time: = %d", radio_stat->tx_time); + ALOGE("rx_time: = %d", radio_stat->rx_time); + ALOGE("on_time_scan: = %d", radio_stat->on_time_scan); + ALOGE("on_time_nbd: = %d", radio_stat->on_time_nbd); + ALOGE("on_time_gscan: = %d", radio_stat->on_time_gscan); + ALOGE("on_time_pno_scan: = %d", radio_stat->on_time_pno_scan); + ALOGE("on_time_hs20: = %d", radio_stat->on_time_hs20); + return NL_SKIP; + } + wifi_iface_stat *iface_stat = (wifi_iface_stat *)((char* )data + sizeof(wifi_radio_stat)); + + if(*mHandler.on_link_stats_results == NULL) { + ALOGE("*mHandler.on_link_stats_results is NULL"); + } else { + (*mHandler.on_link_stats_results)(id, iface_stat, 1, radio_stat); + } + return NL_OK; + } + +}; + +wifi_error wifi_get_link_stats(wifi_request_id id, + wifi_interface_handle iface, wifi_stats_result_handler handler) +{ + GetLinkStatsCommand command(iface, handler); + return (wifi_error) command.requestResponse(); +} + +wifi_error wifi_set_link_stats( + wifi_interface_handle /* iface */, wifi_link_layer_params /* params */) +{ + /* Return success here since bcom HAL does not need set link stats. */ + return WIFI_SUCCESS; +} + +wifi_error wifi_clear_link_stats( + wifi_interface_handle /* iface */, u32 /* stats_clear_req_mask */, + u32 * /* stats_clear_rsp_mask */, u8 /* stop_req */, u8 * /* stop_rsp */) +{ + /* Return success here since bcom HAL does not support clear link stats. */ + return WIFI_SUCCESS; +} diff --git a/wifi/wifi_hal/rtt.cpp b/wifi/wifi_hal/rtt.cpp new file mode 100644 index 0000000..81eb17f --- /dev/null +++ b/wifi/wifi_hal/rtt.cpp @@ -0,0 +1,701 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/errqueue.h> + +#include <linux/pkt_sched.h> +#include <netlink/object-api.h> +#include <netlink/netlink.h> +#include <netlink/socket.h> +#include <netlink-private/object-api.h> +#include <netlink-private/types.h> + +#include "nl80211_copy.h" + +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include <log/log.h> +#include <utils/String8.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +using namespace android; +#define RTT_RESULT_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_TARGET_CNT = 0, + 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("GetRttCapabilitiesCommand", iface, 0), mCapabilities(capabitlites) + { + memset(mCapabilities, 0, sizeof(*mCapabilities)); + } + + virtual int create() { + ALOGD("Creating message to get scan 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(); + + void *data = reply.get_vendor_data(); + int len = reply.get_vendor_data_len(); + + ALOGD("Id = %0x, subcmd = %d, len = %d, expected len = %d", id, subcmd, len, + sizeof(*mCapabilities)); + + memcpy(mCapabilities, data, min(len, (int) sizeof(*mCapabilities))); + + return NL_OK; + } +}; + + +class GetRttResponderInfoCommand : public WifiCommand +{ + wifi_rtt_responder* mResponderInfo; +public: + GetRttResponderInfoCommand(wifi_interface_handle iface, wifi_rtt_responder *responderInfo) + : WifiCommand("GetRttResponderInfoCommand", iface, 0), mResponderInfo(responderInfo) + { + memset(mResponderInfo, 0 , sizeof(*mResponderInfo)); + + } + + virtual int create() { + ALOGD("Creating message to get responder info ; iface = %d", mIfaceInfo->id); + + int ret = mMsg.create(GOOGLE_OUI, RTT_SUBCMD_GETAVAILCHANNEL); + if (ret < 0) { + return ret; + } + + return ret; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + + ALOGD("In GetRttResponderInfoCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + + void *data = reply.get_vendor_data(); + int len = reply.get_vendor_data_len(); + + ALOGD("Id = %0x, subcmd = %d, len = %d, expected len = %d", id, subcmd, len, + sizeof(*mResponderInfo)); + + memcpy(mResponderInfo, data, min(len, (int) sizeof(*mResponderInfo))); + + return NL_OK; + } +}; + + +class EnableResponderCommand : public WifiCommand +{ + wifi_channel_info mChannelInfo; + wifi_rtt_responder* mResponderInfo; + unsigned m_max_duration_sec; +public: + EnableResponderCommand(wifi_interface_handle iface, int id, wifi_channel_info channel_hint, + unsigned max_duration_seconds, wifi_rtt_responder *responderInfo) + : WifiCommand("EnableResponderCommand", iface, 0), mChannelInfo(channel_hint), + m_max_duration_sec(max_duration_seconds), mResponderInfo(responderInfo) + { + memset(mResponderInfo, 0, sizeof(*mResponderInfo)); + } + + virtual int create() { + ALOGD("Creating message to set responder ; iface = %d", mIfaceInfo->id); + + int ret = mMsg.create(GOOGLE_OUI, RTT_SUBCMD_SET_RESPONDER); + if (ret < 0) { + return ret; + } + + return ret; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + + ALOGD("In EnableResponderCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + + void *data = reply.get_vendor_data(); + int len = reply.get_vendor_data_len(); + + ALOGD("Id = %0x, subcmd = %d, len = %d, expected len = %d", id, subcmd, len, + sizeof(*mResponderInfo)); + + memcpy(mResponderInfo, data, min(len, (int) sizeof(*mResponderInfo))); + + return NL_OK; + } +}; + + +class CancelResponderCommand : public WifiCommand +{ + +public: + CancelResponderCommand(wifi_interface_handle iface, int id) + : WifiCommand("CancelResponderCommand", iface, 0)/*, mChannelInfo(channel)*/ + { + + } + + virtual int create() { + ALOGD("Creating message to cancel responder ; iface = %d", mIfaceInfo->id); + + int ret = mMsg.create(GOOGLE_OUI, RTT_SUBCMD_CANCEL_RESPONDER); + if (ret < 0) { + return ret; + } + + return ret; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + /* Nothing to do on response! */ + return NL_SKIP; + } + +}; + + +class RttCommand : public WifiCommand +{ + unsigned numRttParams; + int mCompleted; + int currentIdx; + int 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("RttCommand", 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("RttCommand", 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); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + +/* API to cancel RTT measurements */ +wifi_error wifi_rtt_range_cancel(wifi_request_id id, wifi_interface_handle iface, + unsigned num_devices, mac_addr addr[]) +{ + wifi_handle handle = getWifiHandle(iface); + RttCommand *cmd = new RttCommand(iface, id); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + cmd->cancel_specific(num_devices, addr); + cmd->releaseRef(); + return WIFI_SUCCESS; +} + +/* API to get RTT capability */ +wifi_error wifi_get_rtt_capabilities(wifi_interface_handle iface, + wifi_rtt_capabilities *capabilities) +{ + GetRttCapabilitiesCommand command(iface, capabilities); + return (wifi_error) command.requestResponse(); +} + +/* API to get the responder information */ +wifi_error wifi_rtt_get_responder_info(wifi_interface_handle iface, + wifi_rtt_responder* responderInfo) +{ + GetRttResponderInfoCommand command(iface, responderInfo); + return (wifi_error) command.requestResponse(); + +} + +/** + * Enable RTT responder mode. + * channel_hint - hint of the channel information where RTT responder should be enabled on. + * max_duration_seconds - timeout of responder mode. + * wifi_rtt_responder - information for RTT responder e.g. channel used and preamble supported. + */ +wifi_error wifi_enable_responder(wifi_request_id id, wifi_interface_handle iface, + wifi_channel_info channel_hint, unsigned max_duration_seconds, + wifi_rtt_responder* responderInfo) +{ + EnableResponderCommand command(iface, id, channel_hint, max_duration_seconds, responderInfo); + return (wifi_error) command.requestResponse(); +} + +/** + * Disable RTT responder mode. + */ +wifi_error wifi_disable_responder(wifi_request_id id, wifi_interface_handle iface) +{ + CancelResponderCommand command(iface, id); + return (wifi_error) command.requestResponse(); +} + diff --git a/wifi/wifi_hal/sync.h b/wifi/wifi_hal/sync.h new file mode 100644 index 0000000..64f8d39 --- /dev/null +++ b/wifi/wifi_hal/sync.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <pthread.h> + +#ifndef __WIFI_HAL_SYNC_H__ +#define __WIFI_HAL_SYNC_H__ + +class Mutex +{ +private: + pthread_mutex_t mMutex; +public: + Mutex() { + pthread_mutex_init(&mMutex, NULL); + } + ~Mutex() { + pthread_mutex_destroy(&mMutex); + } + int tryLock() { + return pthread_mutex_trylock(&mMutex); + } + int lock() { + return pthread_mutex_lock(&mMutex); + } + void unlock() { + pthread_mutex_unlock(&mMutex); + } +}; + +class Condition +{ +private: + pthread_cond_t mCondition; + pthread_mutex_t mMutex; + +public: + Condition() { + pthread_mutex_init(&mMutex, NULL); + pthread_cond_init(&mCondition, NULL); + } + ~Condition() { + pthread_cond_destroy(&mCondition); + pthread_mutex_destroy(&mMutex); + } + + int wait() { + return pthread_cond_wait(&mCondition, &mMutex); + } + + void signal() { + pthread_cond_signal(&mCondition); + } +}; + +#endif
\ No newline at end of file diff --git a/wifi/wifi_hal/wifi_hal.cpp b/wifi/wifi_hal/wifi_hal.cpp new file mode 100644 index 0000000..508268e --- /dev/null +++ b/wifi/wifi_hal/wifi_hal.cpp @@ -0,0 +1,1360 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/errqueue.h> +#include <errno.h> + +#include <linux/pkt_sched.h> +#include <netlink/object-api.h> +#include <netlink/netlink.h> +#include <netlink/socket.h> +#include <netlink/attr.h> +#include <netlink/handlers.h> +#include <netlink/msg.h> + +#include <dirent.h> +#include <net/if.h> + +#include <sys/types.h> +#include <unistd.h> + +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include <log/log.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" +#include "rtt.h" +/* + BUGBUG: normally, libnl allocates ports for all connections it makes; but + being a static library, it doesn't really know how many other netlink connections + are made by the same process, if connections come from different shared libraries. + These port assignments exist to solve that problem - temporarily. We need to fix + libnl to try and allocate ports across the entire process. + */ + +#define WIFI_HAL_CMD_SOCK_PORT 644 +#define WIFI_HAL_EVENT_SOCK_PORT 645 + +/* + * Defines for wifi_wait_for_driver_ready() + * Specify durations between polls and max wait time + */ +#define POLL_DRIVER_DURATION_US (100000) +#define POLL_DRIVER_MAX_TIME_MS (10000) + +static void internal_event_handler(wifi_handle handle, int events); +static int internal_no_seq_check(nl_msg *msg, void *arg); +static int internal_valid_message_handler(nl_msg *msg, void *arg); +static int wifi_get_multicast_id(wifi_handle handle, const char *name, const char *group); +static int wifi_add_membership(wifi_handle handle, const char *group); +static wifi_error wifi_init_interfaces(wifi_handle handle); +static wifi_error wifi_start_rssi_monitoring(wifi_request_id id, wifi_interface_handle + iface, s8 max_rssi, s8 min_rssi, wifi_rssi_event_handler eh); +static wifi_error wifi_stop_rssi_monitoring(wifi_request_id id, wifi_interface_handle iface); +static wifi_error wifi_set_packet_filter(wifi_interface_handle handle, + const u8 *program, u32 len); +static wifi_error wifi_get_packet_filter_capabilities(wifi_interface_handle handle, + u32 *version, u32 *max_len); +static wifi_error wifi_configure_nd_offload(wifi_interface_handle iface, u8 enable); + +typedef enum wifi_attr { + ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET, + ANDR_WIFI_ATTRIBUTE_FEATURE_SET, + ANDR_WIFI_ATTRIBUTE_PNO_RANDOM_MAC_OUI, + ANDR_WIFI_ATTRIBUTE_NODFS_SET, + ANDR_WIFI_ATTRIBUTE_COUNTRY, + ANDR_WIFI_ATTRIBUTE_ND_OFFLOAD_VALUE + // Add more attribute here +} wifi_attr_t; + +enum wifi_rssi_monitor_attr { + RSSI_MONITOR_ATTRIBUTE_MAX_RSSI, + RSSI_MONITOR_ATTRIBUTE_MIN_RSSI, + RSSI_MONITOR_ATTRIBUTE_START, +}; + +enum wifi_apf_attr { + APF_ATTRIBUTE_VERSION, + APF_ATTRIBUTE_MAX_LEN, + APF_ATTRIBUTE_PROGRAM, + APF_ATTRIBUTE_PROGRAM_LEN +}; + +enum apf_request_type { + GET_APF_CAPABILITIES, + SET_APF_PROGRAM +}; + +/* Initialize/Cleanup */ + +void wifi_socket_set_local_port(struct nl_sock *sock, uint32_t port) +{ + uint32_t pid = getpid() & 0x3FFFFF; + nl_socket_set_local_port(sock, pid + (port << 22)); +} + +static nl_sock * wifi_create_nl_socket(int port) +{ + // ALOGI("Creating socket"); + struct nl_sock *sock = nl_socket_alloc(); + if (sock == NULL) { + ALOGE("Could not create handle"); + return NULL; + } + + wifi_socket_set_local_port(sock, port); + + struct sockaddr *addr = NULL; + // ALOGI("sizeof(sockaddr) = %d, sizeof(sockaddr_nl) = %d", sizeof(*addr), sizeof(*addr_nl)); + + // ALOGI("Connecting socket"); + if (nl_connect(sock, NETLINK_GENERIC)) { + ALOGE("Could not connect handle"); + nl_socket_free(sock); + return NULL; + } + + // ALOGI("Making socket nonblocking"); + /* + if (nl_socket_set_nonblocking(sock)) { + ALOGE("Could make socket non-blocking"); + nl_socket_free(sock); + return NULL; + } + */ + + return sock; +} + +/*initialize function pointer table with Broadcom HHAL API*/ +wifi_error init_wifi_vendor_hal_func_table(wifi_hal_fn *fn) +{ + if (fn == NULL) { + return WIFI_ERROR_UNKNOWN; + } + fn->wifi_initialize = wifi_initialize; + fn->wifi_wait_for_driver_ready = wifi_wait_for_driver_ready; + fn->wifi_cleanup = wifi_cleanup; + fn->wifi_event_loop = wifi_event_loop; + fn->wifi_get_supported_feature_set = wifi_get_supported_feature_set; + fn->wifi_get_concurrency_matrix = wifi_get_concurrency_matrix; + fn->wifi_set_scanning_mac_oui = wifi_set_scanning_mac_oui; + fn->wifi_get_ifaces = wifi_get_ifaces; + fn->wifi_get_iface_name = wifi_get_iface_name; + fn->wifi_start_gscan = wifi_start_gscan; + fn->wifi_stop_gscan = wifi_stop_gscan; + fn->wifi_get_cached_gscan_results = wifi_get_cached_gscan_results; + fn->wifi_set_bssid_hotlist = wifi_set_bssid_hotlist; + fn->wifi_reset_bssid_hotlist = wifi_reset_bssid_hotlist; + fn->wifi_set_significant_change_handler = wifi_set_significant_change_handler; + fn->wifi_reset_significant_change_handler = wifi_reset_significant_change_handler; + fn->wifi_get_gscan_capabilities = wifi_get_gscan_capabilities; + fn->wifi_get_link_stats = wifi_get_link_stats; + fn->wifi_set_link_stats = wifi_set_link_stats; + fn->wifi_clear_link_stats = wifi_clear_link_stats; + fn->wifi_get_valid_channels = wifi_get_valid_channels; + fn->wifi_rtt_range_request = wifi_rtt_range_request; + fn->wifi_rtt_range_cancel = wifi_rtt_range_cancel; + fn->wifi_get_rtt_capabilities = wifi_get_rtt_capabilities; + fn->wifi_rtt_get_responder_info = wifi_rtt_get_responder_info; + fn->wifi_enable_responder = wifi_enable_responder; + fn->wifi_disable_responder = wifi_disable_responder; + fn->wifi_set_nodfs_flag = wifi_set_nodfs_flag; + fn->wifi_start_logging = wifi_start_logging; + fn->wifi_set_epno_list = wifi_set_epno_list; + fn->wifi_reset_epno_list = wifi_reset_epno_list; + fn->wifi_set_country_code = wifi_set_country_code; + fn->wifi_get_firmware_memory_dump = wifi_get_firmware_memory_dump; + fn->wifi_set_log_handler = wifi_set_log_handler; + fn->wifi_reset_log_handler = wifi_reset_log_handler; + fn->wifi_set_alert_handler = wifi_set_alert_handler; + fn->wifi_reset_alert_handler = wifi_reset_alert_handler; + fn->wifi_get_firmware_version = wifi_get_firmware_version; + fn->wifi_get_ring_buffers_status = wifi_get_ring_buffers_status; + fn->wifi_get_logger_supported_feature_set = wifi_get_logger_supported_feature_set; + fn->wifi_get_ring_data = wifi_get_ring_data; + fn->wifi_get_driver_version = wifi_get_driver_version; + fn->wifi_start_rssi_monitoring = wifi_start_rssi_monitoring; + fn->wifi_stop_rssi_monitoring = wifi_stop_rssi_monitoring; + fn->wifi_configure_nd_offload = wifi_configure_nd_offload; + fn->wifi_start_sending_offloaded_packet = wifi_start_sending_offloaded_packet; + fn->wifi_stop_sending_offloaded_packet = wifi_stop_sending_offloaded_packet; + fn->wifi_start_pkt_fate_monitoring = wifi_start_pkt_fate_monitoring; + fn->wifi_get_tx_pkt_fates = wifi_get_tx_pkt_fates; + fn->wifi_get_rx_pkt_fates = wifi_get_rx_pkt_fates; + fn->wifi_get_packet_filter_capabilities = wifi_get_packet_filter_capabilities; + fn->wifi_set_packet_filter = wifi_set_packet_filter; + return WIFI_SUCCESS; +} + +wifi_error wifi_initialize(wifi_handle *handle) +{ + srand(getpid()); + + ALOGI("Initializing wifi"); + hal_info *info = (hal_info *)malloc(sizeof(hal_info)); + if (info == NULL) { + ALOGE("Could not allocate hal_info"); + return WIFI_ERROR_UNKNOWN; + } + + memset(info, 0, sizeof(*info)); + + ALOGI("Creating socket"); + if (socketpair(AF_UNIX, SOCK_STREAM, 0, info->cleanup_socks) == -1) { + ALOGE("Could not create cleanup sockets"); + free(info); + return WIFI_ERROR_UNKNOWN; + } + + struct nl_sock *cmd_sock = wifi_create_nl_socket(WIFI_HAL_CMD_SOCK_PORT); + if (cmd_sock == NULL) { + ALOGE("Could not create handle"); + free(info); + return WIFI_ERROR_UNKNOWN; + } + + struct nl_sock *event_sock = wifi_create_nl_socket(WIFI_HAL_EVENT_SOCK_PORT); + if (event_sock == NULL) { + ALOGE("Could not create handle"); + nl_socket_free(cmd_sock); + free(info); + return WIFI_ERROR_UNKNOWN; + } + + struct nl_cb *cb = nl_socket_get_cb(event_sock); + if (cb == NULL) { + ALOGE("Could not create handle"); + nl_socket_free(cmd_sock); + nl_socket_free(event_sock); + free(info); + return WIFI_ERROR_UNKNOWN; + } + + // 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->cmd_sock = cmd_sock; + info->event_sock = event_sock; + 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; + + info->cmd = (cmd_info *)malloc(sizeof(cmd_info) * DEFAULT_CMD_SIZE); + info->alloc_cmd = DEFAULT_CMD_SIZE; + info->num_cmd = 0; + + info->nl80211_family_id = genl_ctrl_resolve(cmd_sock, "nl80211"); + if (info->nl80211_family_id < 0) { + ALOGE("Could not resolve nl80211 familty id"); + nl_socket_free(cmd_sock); + nl_socket_free(event_sock); + free(info); + return WIFI_ERROR_UNKNOWN; + } + + pthread_mutex_init(&info->cb_lock, NULL); + + *handle = (wifi_handle) info; + + if (wifi_init_interfaces(*handle) != WIFI_SUCCESS) { + ALOGE("No wifi interface found"); + nl_socket_free(cmd_sock); + nl_socket_free(event_sock); + pthread_mutex_destroy(&info->cb_lock); + free(info); + return WIFI_ERROR_NOT_AVAILABLE; + } + + if ((wifi_add_membership(*handle, "scan") < 0) || + (wifi_add_membership(*handle, "mlme") < 0) || + (wifi_add_membership(*handle, "regulatory") < 0) || + (wifi_add_membership(*handle, "vendor") < 0)) { + ALOGE("Add membership failed"); + nl_socket_free(cmd_sock); + nl_socket_free(event_sock); + pthread_mutex_destroy(&info->cb_lock); + free(info); + return WIFI_ERROR_NOT_AVAILABLE; + } + + // ALOGI("Found %d interfaces", info->num_interfaces); + + ALOGI("Initialized Wifi HAL Successfully; vendor cmd = %d", NL80211_CMD_VENDOR); + return WIFI_SUCCESS; +} + +wifi_error wifi_wait_for_driver_ready(void) +{ + // This function will wait to make sure basic client netdev is created + // Function times out after 10 seconds + int count = (POLL_DRIVER_MAX_TIME_MS * 1000) / POLL_DRIVER_DURATION_US; + FILE *fd; + + do { + if ((fd = fopen("/sys/class/net/wlan0", "r")) != NULL) { + fclose(fd); + return WIFI_SUCCESS; + } + usleep(POLL_DRIVER_DURATION_US); + } while(--count > 0); + + ALOGE("Timed out waiting on Driver ready ... "); + return WIFI_ERROR_TIMED_OUT; +} + +static int wifi_add_membership(wifi_handle handle, const char *group) +{ + hal_info *info = getHalInfo(handle); + + int id = wifi_get_multicast_id(handle, "nl80211", group); + if (id < 0) { + ALOGE("Could not find group %s", group); + return id; + } + + int ret = nl_socket_add_membership(info->event_sock, id); + if (ret < 0) { + ALOGE("Could not add membership to group %s", group); + } + + // ALOGI("Successfully added membership for group %s", group); + return ret; +} + +static void internal_cleaned_up_handler(wifi_handle handle) +{ + hal_info *info = getHalInfo(handle); + wifi_cleaned_up_handler cleaned_up_handler = info->cleaned_up_handler; + + if (info->cmd_sock != 0) { + close(info->cleanup_socks[0]); + close(info->cleanup_socks[1]); + nl_socket_free(info->cmd_sock); + nl_socket_free(info->event_sock); + info->cmd_sock = NULL; + info->event_sock = NULL; + } + + (*cleaned_up_handler)(handle); + pthread_mutex_destroy(&info->cb_lock); + free(info); + + ALOGI("Internal cleanup completed"); +} + +void wifi_cleanup(wifi_handle handle, wifi_cleaned_up_handler handler) +{ + hal_info *info = getHalInfo(handle); + char buf[64]; + + info->cleaned_up_handler = handler; + if (TEMP_FAILURE_RETRY(write(info->cleanup_socks[0], "Exit", 4)) < 1) { + // As a fallback set the cleanup flag to TRUE + ALOGE("could not write to the cleanup socket"); + } else { + // Listen to the response + // Hopefully we dont get errors or get hung up + // Not much can be done in that case, but assume that + // it has rx'ed the Exit message to exit the thread. + // As a fallback set the cleanup flag to TRUE + memset(buf, 0, sizeof(buf)); + ssize_t result = TEMP_FAILURE_RETRY(read(info->cleanup_socks[0], buf, sizeof(buf))); + ALOGE("%s: Read after POLL returned %zd, error no = %d (%s)", __FUNCTION__, + result, errno, strerror(errno)); + if (strncmp(buf, "Done", 4) == 0) { + ALOGE("Event processing terminated"); + } else { + ALOGD("Rx'ed %s", buf); + } + } + info->clean_up = true; + pthread_mutex_lock(&info->cb_lock); + + int bad_commands = 0; + + for (int i = 0; i < info->num_event_cb; i++) { + cb_info *cbi = &(info->event_cb[i]); + WifiCommand *cmd = (WifiCommand *)cbi->cb_arg; + ALOGI("Command left in event_cb %p:%s", cmd, (cmd ? cmd->getType(): "")); + } + + while (info->num_cmd > bad_commands) { + int num_cmd = info->num_cmd; + cmd_info *cmdi = &(info->cmd[bad_commands]); + WifiCommand *cmd = cmdi->cmd; + if (cmd != NULL) { + ALOGI("Cancelling command %p:%s", cmd, cmd->getType()); + pthread_mutex_unlock(&info->cb_lock); + cmd->cancel(); + pthread_mutex_lock(&info->cb_lock); + if (num_cmd == info->num_cmd) { + ALOGI("Cancelling command %p:%s did not work", cmd, (cmd ? cmd->getType(): "")); + bad_commands++; + } + /* release reference added when command is saved */ + cmd->releaseRef(); + } + } + + for (int i = 0; i < info->num_event_cb; i++) { + cb_info *cbi = &(info->event_cb[i]); + WifiCommand *cmd = (WifiCommand *)cbi->cb_arg; + ALOGE("Leaked command %p", cmd); + } + pthread_mutex_unlock(&info->cb_lock); + internal_cleaned_up_handler(handle); +} + +static int internal_pollin_handler(wifi_handle handle) +{ + hal_info *info = getHalInfo(handle); + struct nl_cb *cb = nl_socket_get_cb(info->event_sock); + int res = nl_recvmsgs(info->event_sock, cb); + // ALOGD("nl_recvmsgs returned %d", res); + nl_cb_put(cb); + return res; +} + +/* Run event handler */ +void wifi_event_loop(wifi_handle handle) +{ + hal_info *info = getHalInfo(handle); + if (info->in_event_loop) { + return; + } else { + info->in_event_loop = true; + } + + pollfd pfd[2]; + memset(&pfd[0], 0, sizeof(pollfd) * 2); + + pfd[0].fd = nl_socket_get_fd(info->event_sock); + pfd[0].events = POLLIN; + pfd[1].fd = info->cleanup_socks[1]; + pfd[1].events = POLLIN; + + char buf[2048]; + /* TODO: Add support for timeouts */ + + do { + int timeout = -1; /* Infinite timeout */ + pfd[0].revents = 0; + pfd[1].revents = 0; + // ALOGI("Polling socket"); + int result = TEMP_FAILURE_RETRY(poll(pfd, 2, timeout)); + if (result < 0) { + // ALOGE("Error polling socket"); + } else if (pfd[0].revents & POLLERR) { + ALOGE("POLL Error; error no = %d (%s)", errno, strerror(errno)); + ssize_t result2 = TEMP_FAILURE_RETRY(read(pfd[0].fd, buf, sizeof(buf))); + ALOGE("Read after POLL returned %zd, error no = %d (%s)", result2, + errno, strerror(errno)); + } else if (pfd[0].revents & POLLHUP) { + ALOGE("Remote side hung up"); + break; + } else if (pfd[0].revents & POLLIN) { + // ALOGI("Found some events!!!"); + internal_pollin_handler(handle); + } else if (pfd[1].revents & POLLIN) { + memset(buf, 0, sizeof(buf)); + ssize_t result2 = TEMP_FAILURE_RETRY(read(pfd[1].fd, buf, sizeof(buf))); + ALOGE("%s: Read after POLL returned %zd, error no = %d (%s)", __FUNCTION__, + result2, errno, strerror(errno)); + if (strncmp(buf, "Exit", 4) == 0) { + ALOGD("Got a signal to exit!!!"); + if (TEMP_FAILURE_RETRY(write(pfd[1].fd, "Done", 4)) < 1) { + ALOGE("could not write to the cleanup socket"); + } + break; + } else { + ALOGD("Rx'ed %s on the cleanup socket\n", buf); + } + } else { + ALOGE("Unknown event - %0x, %0x", pfd[0].revents, pfd[1].revents); + } + } while (!info->clean_up); + ALOGI("Exit %s", __FUNCTION__); +} + +/////////////////////////////////////////////////////////////////////////////////////// + +static int internal_no_seq_check(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + +static int internal_valid_message_handler(nl_msg *msg, void *arg) +{ + // ALOGI("got an event"); + + wifi_handle handle = (wifi_handle)arg; + hal_info *info = getHalInfo(handle); + + WifiEvent event(msg); + int res = event.parse(); + if (res < 0) { + ALOGE("Failed to parse event: %d", res); + return NL_SKIP; + } + + int cmd = event.get_cmd(); + uint32_t vendor_id = 0; + int subcmd = 0; + + if (cmd == NL80211_CMD_VENDOR) { + vendor_id = event.get_u32(NL80211_ATTR_VENDOR_ID); + subcmd = event.get_u32(NL80211_ATTR_VENDOR_SUBCMD); + ALOGV("event received %s, vendor_id = 0x%0x, subcmd = 0x%0x", + event.get_cmdString(), vendor_id, subcmd); + } else { + // ALOGV("event received %s", event.get_cmdString()); + } + + // ALOGV("event received %s, vendor_id = 0x%0x", event.get_cmdString(), vendor_id); + // event.log(); + + 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); + if (cb_func) + (*cb_func)(msg, cb_arg); + if (cmd != NULL) { + cmd->releaseRef(); + } + + return NL_OK; + } + } + + pthread_mutex_unlock(&info->cb_lock); + return NL_OK; +} + +/////////////////////////////////////////////////////////////////////////////////////// + +class GetMulticastIdCommand : public WifiCommand +{ +private: + const char *mName; + const char *mGroup; + int mId; +public: + GetMulticastIdCommand(wifi_handle handle, const char *name, const char *group) + : WifiCommand("GetMulticastIdCommand", handle, 0) + { + mName = name; + mGroup = group; + mId = -1; + } + + int getId() { + return mId; + } + + virtual int create() { + int nlctrlFamily = genl_ctrl_resolve(mInfo->cmd_sock, "nlctrl"); + // ALOGI("ctrl family = %d", nlctrlFamily); + int ret = mMsg.create(nlctrlFamily, CTRL_CMD_GETFAMILY, 0, 0); + if (ret < 0) { + return ret; + } + ret = mMsg.put_string(CTRL_ATTR_FAMILY_NAME, mName); + return ret; + } + + virtual int handleResponse(WifiEvent& reply) { + + // ALOGI("handling 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]) { + ALOGI("No multicast groups found"); + return NL_SKIP; + } else { + // ALOGI("Multicast groups attr size = %d", nla_len(tb[CTRL_ATTR_MCAST_GROUPS])); + } + + for_each_attr(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) { + + // ALOGI("Processing group"); + struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1]; + nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, (nlattr *)nla_data(mcgrp), + nla_len(mcgrp), NULL); + if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] || !tb2[CTRL_ATTR_MCAST_GRP_ID]) { + continue; + } + + char *grpName = (char *)nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]); + int grpNameLen = nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME]); + + // ALOGI("Found group name %s", grpName); + + if (strncmp(grpName, mGroup, grpNameLen) != 0) + continue; + + mId = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]); + break; + } + + return NL_SKIP; + } + +}; + +class SetPnoMacAddrOuiCommand : public WifiCommand { + +private: + byte *mOui; + feature_set *fset; + feature_set *feature_matrix; + int *fm_size; + int set_size_max; +public: + SetPnoMacAddrOuiCommand(wifi_interface_handle handle, oui scan_oui) + : WifiCommand("SetPnoMacAddrOuiCommand", handle, 0) + { + mOui = scan_oui; + } + + int createRequest(WifiRequest& request, int subcmd, byte *scan_oui) { + int result = request.create(GOOGLE_OUI, subcmd); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put(ANDR_WIFI_ATTRIBUTE_PNO_RANDOM_MAC_OUI, scan_oui, DOT11_OUI_LEN); + if (result < 0) { + return result; + } + + request.attr_end(data); + return WIFI_SUCCESS; + + } + + int start() { + ALOGD("Sending mac address OUI"); + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request, WIFI_SUBCMD_SET_PNO_RANDOM_MAC_OUI, mOui); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create request; result = %d", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to set scanning mac OUI; result = %d", result); + } + + return result; + } +protected: + virtual int handleResponse(WifiEvent& reply) { + ALOGD("Request complete!"); + /* Nothing to do on response! */ + return NL_SKIP; + } +}; + +class SetNodfsCommand : public WifiCommand { + +private: + u32 mNoDfs; +public: + SetNodfsCommand(wifi_interface_handle handle, u32 nodfs) + : WifiCommand("SetNodfsCommand", handle, 0) { + mNoDfs = nodfs; + } + virtual int create() { + int ret; + + ret = mMsg.create(GOOGLE_OUI, WIFI_SUBCMD_NODFS_SET); + if (ret < 0) { + ALOGE("Can't create message to send to driver - %d", ret); + return ret; + } + + nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); + ret = mMsg.put_u32(ANDR_WIFI_ATTRIBUTE_NODFS_SET, mNoDfs); + if (ret < 0) { + return ret; + } + + mMsg.attr_end(data); + return WIFI_SUCCESS; + } +}; + +class SetCountryCodeCommand : public WifiCommand { +private: + const char *mCountryCode; +public: + SetCountryCodeCommand(wifi_interface_handle handle, const char *country_code) + : WifiCommand("SetCountryCodeCommand", handle, 0) { + mCountryCode = country_code; + } + virtual int create() { + int ret; + + ret = mMsg.create(GOOGLE_OUI, WIFI_SUBCMD_SET_COUNTRY_CODE); + if (ret < 0) { + ALOGE("Can't create message to send to driver - %d", ret); + return ret; + } + + nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); + ret = mMsg.put_string(ANDR_WIFI_ATTRIBUTE_COUNTRY, mCountryCode); + if (ret < 0) { + return ret; + } + + mMsg.attr_end(data); + return WIFI_SUCCESS; + + } +}; + +class SetRSSIMonitorCommand : public WifiCommand { +private: + s8 mMax_rssi; + s8 mMin_rssi; + wifi_rssi_event_handler mHandler; +public: + SetRSSIMonitorCommand(wifi_request_id id, wifi_interface_handle handle, + s8 max_rssi, s8 min_rssi, wifi_rssi_event_handler eh) + : WifiCommand("SetRSSIMonitorCommand", handle, id), mMax_rssi(max_rssi), mMin_rssi + (min_rssi), mHandler(eh) + { + } + int createRequest(WifiRequest& request, int enable) { + int result = request.create(GOOGLE_OUI, WIFI_SUBCMD_SET_RSSI_MONITOR); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(RSSI_MONITOR_ATTRIBUTE_MAX_RSSI, (enable ? mMax_rssi: 0)); + if (result < 0) { + return result; + } + ALOGD("create request"); + result = request.put_u32(RSSI_MONITOR_ATTRIBUTE_MIN_RSSI, (enable? mMin_rssi: 0)); + if (result < 0) { + return result; + } + result = request.put_u32(RSSI_MONITOR_ATTRIBUTE_START, enable); + if (result < 0) { + return result; + } + request.attr_end(data); + return result; + } + + int start() { + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request, 1); + if (result < 0) { + return result; + } + result = requestResponse(request); + if (result < 0) { + ALOGI("Failed to set RSSI Monitor, result = %d", result); + return result; + } + ALOGI("Successfully set RSSI monitoring"); + registerVendorHandler(GOOGLE_OUI, GOOGLE_RSSI_MONITOR_EVENT); + + + if (result < 0) { + unregisterVendorHandler(GOOGLE_OUI, GOOGLE_RSSI_MONITOR_EVENT); + return result; + } + ALOGI("Done!"); + 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 = %d", result); + } + } + unregisterVendorHandler(GOOGLE_OUI, GOOGLE_RSSI_MONITOR_EVENT); + return WIFI_SUCCESS; + } + + virtual int handleResponse(WifiEvent& reply) { + /* Nothing to do on response! */ + return NL_SKIP; + } + + virtual int handleEvent(WifiEvent& event) { + ALOGI("Got a RSSI monitor event"); + + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = event.get_vendor_data_len(); + + if (vendor_data == NULL || len == 0) { + ALOGI("RSSI monitor: No data"); + return NL_SKIP; + } + /* driver<->HAL event structure */ + #define RSSI_MONITOR_EVT_VERSION 1 + typedef struct { + u8 version; + s8 cur_rssi; + mac_addr BSSID; + } rssi_monitor_evt; + + rssi_monitor_evt *data = (rssi_monitor_evt *)event.get_vendor_data(); + + if (data->version != RSSI_MONITOR_EVT_VERSION) { + ALOGI("Event version mismatch %d, expected %d", data->version, RSSI_MONITOR_EVT_VERSION); + return NL_SKIP; + } + + if (*mHandler.on_rssi_threshold_breached) { + (*mHandler.on_rssi_threshold_breached)(id(), data->BSSID, data->cur_rssi); + } else { + ALOGW("No RSSI monitor handler registered"); + } + + return NL_SKIP; + } + +}; + +class AndroidPktFilterCommand : public WifiCommand { + private: + const u8* mProgram; + u32 mProgramLen; + u32* mVersion; + u32* mMaxLen; + int mReqType; + public: + AndroidPktFilterCommand(wifi_interface_handle handle, + u32* version, u32* max_len) + : WifiCommand("AndroidPktFilterCommand", handle, 0), + mVersion(version), mMaxLen(max_len), + mReqType(GET_APF_CAPABILITIES) + { + } + + AndroidPktFilterCommand(wifi_interface_handle handle, + const u8* program, u32 len) + : WifiCommand("AndroidPktFilterCommand", handle, 0), + mProgram(program), mProgramLen(len), + mReqType(SET_APF_PROGRAM) + { + } + + int createRequest(WifiRequest& request) { + if (mReqType == SET_APF_PROGRAM) { + ALOGI("\n%s: APF set program request\n", __FUNCTION__); + return createSetPktFilterRequest(request); + } else if (mReqType == GET_APF_CAPABILITIES) { + ALOGI("\n%s: APF get capabilities request\n", __FUNCTION__); + return createGetPktFilterCapabilitesRequest(request); + } else { + ALOGE("\n%s Unknown APF request\n", __FUNCTION__); + return WIFI_ERROR_NOT_SUPPORTED; + } + return WIFI_SUCCESS; + } + + int createSetPktFilterRequest(WifiRequest& request) { + u8 *program = new u8[mProgramLen]; + NULL_CHECK_RETURN(program, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + int result = request.create(GOOGLE_OUI, APF_SUBCMD_SET_FILTER); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(APF_ATTRIBUTE_PROGRAM_LEN, mProgramLen); + if (result < 0) { + return result; + } + memcpy(program, mProgram, mProgramLen); + result = request.put(APF_ATTRIBUTE_PROGRAM, program, mProgramLen); + if (result < 0) { + return result; + } + request.attr_end(data); + delete[] program; + return result; + } + + int createGetPktFilterCapabilitesRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, APF_SUBCMD_GET_CAPABILITIES); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + request.attr_end(data); + return result; + } + + int start() { + WifiRequest request(familyId(), ifaceId()); + int result = createRequest(request); + if (result < 0) { + return result; + } + result = requestResponse(request); + if (result < 0) { + ALOGI("Request Response failed for APF, result = %d", result); + return result; + } + ALOGI("Done!"); + return result; + } + + int cancel() { + return WIFI_SUCCESS; + } + + int handleResponse(WifiEvent& reply) { + ALOGD("In SetAPFCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + ALOGD("Id = %0x, subcmd = %d, len = %d", id, subcmd, len); + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in SetAPFCommand response; ignoring it"); + return NL_SKIP; + } + if( mReqType == SET_APF_PROGRAM) { + ALOGD("Response recieved for set packet filter command\n"); + } else if (mReqType == GET_APF_CAPABILITIES) { + *mVersion = 0; + *mMaxLen = 0; + ALOGD("Response recieved for get packet filter capabilities command\n"); + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == APF_ATTRIBUTE_VERSION) { + *mVersion = it.get_u32(); + ALOGI("APF version is %d\n", *mVersion); + } else if (it.get_type() == APF_ATTRIBUTE_MAX_LEN) { + *mMaxLen = it.get_u32(); + ALOGI("APF max len is %d\n", *mMaxLen); + } else { + ALOGE("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + } + return NL_OK; + } + + int handleEvent(WifiEvent& event) { + /* No Event to recieve for APF commands */ + return NL_SKIP; + } +}; + +class SetNdoffloadCommand : public WifiCommand { + +private: + u8 mEnable; +public: + SetNdoffloadCommand(wifi_interface_handle handle, u8 enable) + : WifiCommand("SetNdoffloadCommand", handle, 0) { + mEnable = enable; + } + virtual int create() { + int ret; + + ret = mMsg.create(GOOGLE_OUI, WIFI_SUBCMD_CONFIG_ND_OFFLOAD); + if (ret < 0) { + ALOGE("Can't create message to send to driver - %d", ret); + return ret; + } + + nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); + ret = mMsg.put_u8(ANDR_WIFI_ATTRIBUTE_ND_OFFLOAD_VALUE, mEnable); + if (ret < 0) { + return ret; + } + + mMsg.attr_end(data); + return WIFI_SUCCESS; + } +}; + +class GetFeatureSetCommand : public WifiCommand { + +private: + int feature_type; + feature_set *fset; + feature_set *feature_matrix; + int *fm_size; + int set_size_max; +public: + GetFeatureSetCommand(wifi_interface_handle handle, int feature, feature_set *set, + feature_set set_matrix[], int *size, int max_size) + : WifiCommand("GetFeatureSetCommand", handle, 0) + { + feature_type = feature; + fset = set; + feature_matrix = set_matrix; + fm_size = size; + set_size_max = max_size; + } + + virtual int create() { + int ret; + + if(feature_type == ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET) { + ret = mMsg.create(GOOGLE_OUI, WIFI_SUBCMD_GET_FEATURE_SET); + } else if (feature_type == ANDR_WIFI_ATTRIBUTE_FEATURE_SET) { + ret = mMsg.create(GOOGLE_OUI, WIFI_SUBCMD_GET_FEATURE_SET_MATRIX); + } else { + ALOGE("Unknown feature type %d", feature_type); + return -1; + } + + if (ret < 0) { + ALOGE("Can't create message to send to driver - %d", ret); + } + + return ret; + } + +protected: + virtual int handleResponse(WifiEvent& reply) { + + ALOGV("In GetFeatureSetCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + ALOGV("Id = %0x, subcmd = %d, len = %d", id, subcmd, len); + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in GetFeatureSetCommand response; ignoring it"); + return NL_SKIP; + } + if(feature_type == ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET) { + void *data = reply.get_vendor_data(); + if(!fset) { + ALOGE("Buffers pointers not set"); + return NL_SKIP; + } + memcpy(fset, data, min(len, (int) sizeof(*fset))); + } else { + int num_features_set = 0; + int i = 0; + + if(!feature_matrix || !fm_size) { + ALOGE("Buffers pointers not set"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET) { + num_features_set = it.get_u32(); + ALOGV("Got feature list with %d concurrent sets", num_features_set); + if(set_size_max && (num_features_set > set_size_max)) + num_features_set = set_size_max; + *fm_size = num_features_set; + } else if ((it.get_type() == ANDR_WIFI_ATTRIBUTE_FEATURE_SET) && + i < num_features_set) { + feature_matrix[i] = it.get_u32(); + i++; + } else { + ALOGW("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + + } + return NL_OK; + } + +}; + +static int wifi_get_multicast_id(wifi_handle handle, const char *name, const char *group) +{ + GetMulticastIdCommand cmd(handle, name, group); + int res = cmd.requestResponse(); + if (res < 0) + return res; + else + return cmd.getId(); +} + +///////////////////////////////////////////////////////////////////////// + +static bool is_wifi_interface(const char *name) +{ + if (strncmp(name, "wlan", 4) != 0 && strncmp(name, "p2p", 3) != 0) { + /* not a wifi interface; ignore it */ + return false; + } else { + return true; + } +} + +static int get_interface(const char *name, interface_info *info) +{ + strcpy(info->name, name); + info->id = if_nametoindex(name); + // ALOGI("found an interface : %s, id = %d", name, info->id); + return WIFI_SUCCESS; +} + +wifi_error wifi_init_interfaces(wifi_handle handle) +{ + hal_info *info = (hal_info *)handle; + + struct dirent *de; + + DIR *d = opendir("/sys/class/net"); + if (d == 0) + return WIFI_ERROR_UNKNOWN; + + int n = 0; + while ((de = readdir(d))) { + if (de->d_name[0] == '.') + continue; + if (is_wifi_interface(de->d_name) ) { + n++; + } + } + + closedir(d); + + if (n == 0) + return WIFI_ERROR_NOT_AVAILABLE; + + d = opendir("/sys/class/net"); + if (d == 0) + return WIFI_ERROR_UNKNOWN; + + info->interfaces = (interface_info **)malloc(sizeof(interface_info *) * n); + + int i = 0; + while ((de = readdir(d))) { + if (de->d_name[0] == '.') + continue; + if (is_wifi_interface(de->d_name)) { + interface_info *ifinfo = (interface_info *)malloc(sizeof(interface_info)); + if (get_interface(de->d_name, ifinfo) != WIFI_SUCCESS) { + free(ifinfo); + continue; + } + ifinfo->handle = handle; + info->interfaces[i] = ifinfo; + i++; + } + } + + closedir(d); + + info->num_interfaces = n; + return WIFI_SUCCESS; +} + +wifi_error wifi_get_ifaces(wifi_handle handle, int *num, wifi_interface_handle **interfaces) +{ + hal_info *info = (hal_info *)handle; + + *interfaces = (wifi_interface_handle *)info->interfaces; + *num = info->num_interfaces; + + return WIFI_SUCCESS; +} + +wifi_error wifi_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; +} + +wifi_error wifi_get_supported_feature_set(wifi_interface_handle handle, feature_set *set) +{ + GetFeatureSetCommand command(handle, ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET, set, NULL, NULL, 1); + return (wifi_error) command.requestResponse(); +} + +wifi_error wifi_get_concurrency_matrix(wifi_interface_handle handle, int set_size_max, + feature_set set[], int *set_size) +{ + GetFeatureSetCommand command(handle, ANDR_WIFI_ATTRIBUTE_FEATURE_SET, NULL, + set, set_size, set_size_max); + return (wifi_error) command.requestResponse(); +} + +wifi_error wifi_set_scanning_mac_oui(wifi_interface_handle handle, oui scan_oui) +{ + SetPnoMacAddrOuiCommand command(handle, scan_oui); + return (wifi_error)command.start(); + +} + +wifi_error wifi_set_nodfs_flag(wifi_interface_handle handle, u32 nodfs) +{ + SetNodfsCommand command(handle, nodfs); + return (wifi_error) command.requestResponse(); +} + +wifi_error wifi_set_country_code(wifi_interface_handle handle, const char *country_code) +{ + SetCountryCodeCommand command(handle, country_code); + return (wifi_error) command.requestResponse(); +} + +static wifi_error wifi_start_rssi_monitoring(wifi_request_id id, wifi_interface_handle + iface, s8 max_rssi, s8 min_rssi, wifi_rssi_event_handler eh) +{ + ALOGD("Start RSSI monitor %d", id); + wifi_handle handle = getWifiHandle(iface); + SetRSSIMonitorCommand *cmd = new SetRSSIMonitorCommand(id, iface, max_rssi, min_rssi, eh); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + +static wifi_error wifi_stop_rssi_monitoring(wifi_request_id id, wifi_interface_handle iface) +{ + ALOGD("Stopping RSSI monitor"); + + 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); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + return wifi_cancel_cmd(id, iface); +} + +static wifi_error wifi_get_packet_filter_capabilities(wifi_interface_handle handle, + u32 *version, u32 *max_len) +{ + ALOGD("Getting APF capabilities, halHandle = %p\n", handle); + AndroidPktFilterCommand *cmd = new AndroidPktFilterCommand(handle, version, max_len); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + if (result == WIFI_SUCCESS) { + ALOGD("Getting APF capability, version = %d, max_len = %d\n", *version, *max_len); + } + cmd->releaseRef(); + return result; +} + +static wifi_error wifi_set_packet_filter(wifi_interface_handle handle, + const u8 *program, u32 len) +{ + ALOGD("Setting APF program, halHandle = %p\n", handle); + AndroidPktFilterCommand *cmd = new AndroidPktFilterCommand(handle, program, len); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; +} + +static wifi_error wifi_configure_nd_offload(wifi_interface_handle handle, u8 enable) +{ + SetNdoffloadCommand command(handle, enable); + return (wifi_error) command.requestResponse(); +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/wifi/wifi_hal/wifi_logger.cpp b/wifi/wifi_hal/wifi_logger.cpp new file mode 100644 index 0000000..7a38883 --- /dev/null +++ b/wifi/wifi_hal/wifi_logger.cpp @@ -0,0 +1,1109 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/errqueue.h> + +#include <linux/pkt_sched.h> +#include <netlink/object-api.h> +#include <netlink/netlink.h> +#include <netlink/socket.h> +#include <netlink-private/object-api.h> +#include <netlink-private/types.h> + +#include "nl80211_copy.h" +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include <log/log.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +typedef enum { + LOGGER_START_LOGGING = ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START, + LOGGER_TRIGGER_MEM_DUMP, + LOGGER_GET_MEM_DUMP, + LOGGER_GET_VER, + LOGGER_GET_RING_STATUS, + LOGGER_GET_RING_DATA, + LOGGER_GET_FEATURE, + LOGGER_RESET_LOGGING, + LOGGER_TRIGGER_DRIVER_MEM_DUMP, + LOGGER_GET_DRIVER_MEM_DUMP, + LOGGER_START_PKT_FATE_MONITORING, + LOGGER_GET_TX_PKT_FATES, + LOGGER_GET_RX_PKT_FATES, +} 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_DRIVER_DUMP_LEN, + LOGGER_ATTRIBUTE_DRIVER_DUMP_DATA, + LOGGER_ATTRIBUTE_PKT_FATE_NUM, + LOGGER_ATTRIBUTE_PKT_FATE_DATA, +} 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; + +typedef enum { + PACKET_MONITOR_START, + TX_PACKET_FATE, + RX_PACKET_FATE, +} PktFateReqType; + + +/////////////////////////////////////////////////////////////////////////////// +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("DebugCommand", 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("DebugCommand", 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("DebugCommand", 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("DebugCommand", 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("DebugCommand", 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); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; + } else { + ALOGE("FW version buffer NULL"); + return WIFI_ERROR_INVALID_ARGS; + } +} + +/* API to collect a driver version string */ +wifi_error wifi_get_driver_version(wifi_interface_handle iface, char *buffer, int buffer_size) +{ + if (buffer && (buffer_size > 0)) { + DebugCommand *cmd = new DebugCommand(iface, buffer, &buffer_size, GET_DRV_VER); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; + } else { + ALOGE("Driver version buffer NULL"); + return WIFI_ERROR_INVALID_ARGS; + } +} + +/* API to collect driver records */ +wifi_error wifi_get_ring_data(wifi_interface_handle iface, char *ring_name) +{ + DebugCommand *cmd = new DebugCommand(iface, ring_name, GET_RING_DATA); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; +} + +/* API to get the status of all ring buffers supported by driver */ +wifi_error wifi_get_ring_buffers_status(wifi_interface_handle iface, + u32 *num_rings, wifi_ring_buffer_status *status) +{ + if (status && num_rings) { + DebugCommand *cmd = new DebugCommand(iface, num_rings, status, GET_RING_STATUS); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; + } else { + ALOGE("Ring status buffer NULL"); + return WIFI_ERROR_INVALID_ARGS; + } +} + +/* API to get supportable feature */ +wifi_error wifi_get_logger_supported_feature_set(wifi_interface_handle iface, + unsigned int *support) +{ + if (support) { + DebugCommand *cmd = new DebugCommand(iface, support, GET_FEATURE); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; + } else { + ALOGE("Get support buffer NULL"); + return WIFI_ERROR_INVALID_ARGS; + } +} + +wifi_error wifi_start_logging(wifi_interface_handle iface, u32 verbose_level, + u32 flags, u32 max_interval_sec, u32 min_data_size, char *ring_name) +{ + if (ring_name) { + DebugCommand *cmd = new DebugCommand(iface, verbose_level, flags, max_interval_sec, + min_data_size, ring_name, START_RING_LOG); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; + } else { + ALOGE("Ring name NULL"); + return WIFI_ERROR_INVALID_ARGS; + } +} + + +/////////////////////////////////////////////////////////////////////////////// +class SetLogHandler : public WifiCommand +{ + wifi_ring_buffer_data_handler mHandler; + +public: + SetLogHandler(wifi_interface_handle iface, int id, wifi_ring_buffer_data_handler handler) + : WifiCommand("SetLogHandler", iface, id), mHandler(handler) + { } + + int start() { + ALOGV("Register loghandler"); + registerVendorHandler(GOOGLE_OUI, GOOGLE_DEBUG_RING_EVENT); + return WIFI_SUCCESS; + } + + virtual int cancel() { + /* Send a command to driver to stop generating logging events */ + ALOGV("Clear loghandler"); + + /* unregister event handler */ + unregisterVendorHandler(GOOGLE_OUI, GOOGLE_DEBUG_RING_EVENT); + + WifiRequest request(familyId(), ifaceId()); + int result = request.create(GOOGLE_OUI, LOGGER_RESET_LOGGING); + if (result != WIFI_SUCCESS) { + ALOGE("failed to create reset request; result = %d", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("failed to request reset; result = %d", result); + return result; + } + + ALOGD("Success to clear loghandler"); + return WIFI_SUCCESS; + } + + virtual int handleEvent(WifiEvent& event) { + char *buffer = NULL; + int buffer_size = 0; + + // ALOGD("In SetLogHandler::handleEvent"); + nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = event.get_vendor_data_len(); + int event_id = event.get_vendor_subcmd(); + // ALOGI("Got Logger event: %d", event_id); + + if (vendor_data == NULL || len == 0) { + ALOGE("No Debug data found"); + return NL_SKIP; + } + + if(event_id == GOOGLE_DEBUG_RING_EVENT) { + wifi_ring_buffer_status status; + memset(&status, 0, sizeof(status)); + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == LOGGER_ATTRIBUTE_RING_STATUS) { + 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); + ALOGV("Loghandler start, handle = %p", handle); + + SetLogHandler *cmd = new SetLogHandler(iface, id, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + +wifi_error wifi_reset_log_handler(wifi_request_id id, wifi_interface_handle iface) +{ + wifi_handle handle = getWifiHandle(iface); + ALOGV("Loghandler reset, wifi_request_id = %d, handle = %p", id, handle); + + if (id == -1) { + wifi_ring_buffer_data_handler handler; + memset(&handler, 0, sizeof(handler)); + + SetLogHandler *cmd = new SetLogHandler(iface, id, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + + return wifi_cancel_cmd(id, iface); +} + +/////////////////////////////////////////////////////////////////////////////// +class SetAlertHandler : public WifiCommand +{ + wifi_alert_handler mHandler; + int mBuffSize; + char *mBuff; + int mErrCode; + +public: + SetAlertHandler(wifi_interface_handle iface, int id, wifi_alert_handler handler) + : WifiCommand("SetAlertHandler", iface, id), mHandler(handler), mBuffSize(0), mBuff(NULL), + mErrCode(0) + { } + + int start() { + ALOGV("Start Alerting"); + registerVendorHandler(GOOGLE_OUI, GOOGLE_DEBUG_MEM_DUMP_EVENT); + return WIFI_SUCCESS; + } + + virtual int cancel() { + ALOGV("Clear alerthandler"); + + /* unregister alert handler */ + unregisterVendorHandler(GOOGLE_OUI, GOOGLE_DEBUG_MEM_DUMP_EVENT); + wifi_unregister_cmd(wifiHandle(), id()); + ALOGD("Success to clear alerthandler"); + return WIFI_SUCCESS; + } + + virtual int handleResponse(WifiEvent& reply) { + ALOGD("In SetAlertHandler::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + ALOGD("len = %d", len); + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in memory dump response; ignoring it"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == LOGGER_ATTRIBUTE_FW_DUMP_DATA) { + ALOGI("Initiating alert callback"); + if (mHandler.on_alert) { + (*mHandler.on_alert)(id(), mBuff, mBuffSize, mErrCode); + } + if (mBuff) { + free(mBuff); + mBuff = NULL; + } + } + } + return NL_OK; + } + + virtual int handleEvent(WifiEvent& event) { + 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); + ALOGV("Alerthandler start, handle = %p", handle); + + SetAlertHandler *cmd = new SetAlertHandler(iface, id, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = wifi_register_cmd(handle, id, cmd); + if (result != WIFI_SUCCESS) { + cmd->releaseRef(); + return result; + } + result = (wifi_error)cmd->start(); + if (result != WIFI_SUCCESS) { + wifi_unregister_cmd(handle, id); + cmd->releaseRef(); + return result; + } + return result; +} + +wifi_error wifi_reset_alert_handler(wifi_request_id id, wifi_interface_handle iface) +{ + wifi_handle handle = getWifiHandle(iface); + ALOGV("Alerthandler reset, wifi_request_id = %d, handle = %p", id, handle); + + if (id == -1) { + wifi_alert_handler handler; + memset(&handler, 0, sizeof(handler)); + + SetAlertHandler *cmd = new SetAlertHandler(iface, id, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + cmd->cancel(); + cmd->releaseRef(); + return WIFI_SUCCESS; + } + + return wifi_cancel_cmd(id, iface); +} + +/////////////////////////////////////////////////////////////////////////////// +class MemoryDumpCommand: public WifiCommand +{ + wifi_firmware_memory_dump_handler mHandler; + int mBuffSize; + char *mBuff; + +public: + MemoryDumpCommand(wifi_interface_handle iface, wifi_firmware_memory_dump_handler handler) + : WifiCommand("MemoryDumpCommand", iface, 0), mHandler(handler), mBuffSize(0), mBuff(NULL) + { } + + int start() { + ALOGD("Start memory dump command"); + WifiRequest request(familyId(), ifaceId()); + + int result = request.create(GOOGLE_OUI, LOGGER_TRIGGER_MEM_DUMP); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to create trigger fw memory dump request; result = %d", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to register trigger memory dump response; result = %d", result); + } + return result; + } + + virtual int handleResponse(WifiEvent& reply) { + ALOGD("In MemoryDumpCommand::handleResponse"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + ALOGD("len = %d", len); + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in memory dump response; ignoring it"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == LOGGER_ATTRIBUTE_FW_DUMP_LEN) { + mBuffSize = it.get_u32(); + + if (mBuff) + free(mBuff); + mBuff = (char *)malloc(mBuffSize); + if (!mBuff) { + ALOGE("Buffer allocation failed"); + return NL_SKIP; + } + WifiRequest request(familyId(), ifaceId()); + int result = request.create(GOOGLE_OUI, LOGGER_GET_MEM_DUMP); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to create get memory dump request; result = %d", result); + free(mBuff); + return NL_SKIP; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(LOGGER_ATTRIBUTE_FW_DUMP_LEN, mBuffSize); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put get memory dump request; result = %d", result); + return result; + } + + result = request.put_u64(LOGGER_ATTRIBUTE_FW_DUMP_DATA, (uint64_t)mBuff); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to put get memory dump request; result = %d", result); + return result; + } + request.attr_end(data); + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to register get momory dump response; result = %d", result); + } + } else if (it.get_type() == LOGGER_ATTRIBUTE_FW_DUMP_DATA) { + ALOGI("Initiating memory dump callback"); + if (mHandler.on_firmware_memory_dump) { + (*mHandler.on_firmware_memory_dump)(mBuff, mBuffSize); + } + if (mBuff) { + free(mBuff); + mBuff = NULL; + } + } else { + ALOGW("Ignoring invalid attribute type = %d, size = %d", + it.get_type(), it.get_len()); + } + } + return NL_OK; + } + + virtual int handleEvent(WifiEvent& event) { + /* NO events! */ + return NL_SKIP; + } +}; + +/* API to collect a firmware memory dump for a given iface */ +wifi_error wifi_get_firmware_memory_dump( wifi_interface_handle iface, + wifi_firmware_memory_dump_handler handler) +{ + MemoryDumpCommand *cmd = new MemoryDumpCommand(iface, handler); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; +} + +class PacketFateCommand: public WifiCommand +{ + void *mReportBufs; + size_t mNoReqFates; + size_t *mNoProvidedFates; + PktFateReqType mReqType; + +public: + PacketFateCommand(wifi_interface_handle handle) + : WifiCommand("PacketFateCommand", handle, 0), mReqType(PACKET_MONITOR_START) + { } + + PacketFateCommand(wifi_interface_handle handle, wifi_tx_report *tx_report_bufs, + size_t n_requested_fates, size_t *n_provided_fates) + : WifiCommand("PacketFateCommand", handle, 0), mReportBufs(tx_report_bufs), + mNoReqFates(n_requested_fates), mNoProvidedFates(n_provided_fates), + mReqType(TX_PACKET_FATE) + { } + + PacketFateCommand(wifi_interface_handle handle, wifi_rx_report *rx_report_bufs, + size_t n_requested_fates, size_t *n_provided_fates) + : WifiCommand("PacketFateCommand", handle, 0), mReportBufs(rx_report_bufs), + mNoReqFates(n_requested_fates), mNoProvidedFates(n_provided_fates), + mReqType(RX_PACKET_FATE) + { } + + int createRequest(WifiRequest& request) { + if (mReqType == TX_PACKET_FATE) { + ALOGD("%s Get Tx packet fate request\n", __FUNCTION__); + return createTxPktFateRequest(request); + } else if (mReqType == RX_PACKET_FATE) { + ALOGD("%s Get Rx packet fate request\n", __FUNCTION__); + return createRxPktFateRequest(request); + } else if (mReqType == PACKET_MONITOR_START) { + ALOGD("%s Monitor packet fate request\n", __FUNCTION__); + return createMonitorPktFateRequest(request); + } else { + ALOGE("%s Unknown packet fate request\n", __FUNCTION__); + return WIFI_ERROR_NOT_SUPPORTED; + } + return WIFI_SUCCESS; + } + + int createMonitorPktFateRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, LOGGER_START_PKT_FATE_MONITORING); + if (result < 0) { + return result; + } + + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + request.attr_end(data); + return result; + } + + int createTxPktFateRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, LOGGER_GET_TX_PKT_FATES); + if (result < 0) { + return result; + } + + memset(mReportBufs, 0, (mNoReqFates * sizeof(wifi_tx_report))); + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(LOGGER_ATTRIBUTE_PKT_FATE_NUM, mNoReqFates); + if (result < 0) { + return result; + } + result = request.put_u64(LOGGER_ATTRIBUTE_PKT_FATE_DATA, (uint64_t)mReportBufs); + if (result < 0) { + return result; + } + request.attr_end(data); + return result; + } + + int createRxPktFateRequest(WifiRequest& request) { + int result = request.create(GOOGLE_OUI, LOGGER_GET_RX_PKT_FATES); + if (result < 0) { + return result; + } + + memset(mReportBufs, 0, (mNoReqFates * sizeof(wifi_rx_report))); + nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); + result = request.put_u32(LOGGER_ATTRIBUTE_PKT_FATE_NUM, mNoReqFates); + if (result < 0) { + return result; + } + result = request.put_u64(LOGGER_ATTRIBUTE_PKT_FATE_DATA, (uint64_t)mReportBufs); + if (result < 0) { + return result; + } + request.attr_end(data); + return result; + } + + int start() { + ALOGD("Start get packet fate command\n"); + WifiRequest request(familyId(), ifaceId()); + + int result = createRequest(request); + if (result < 0) { + ALOGE("Failed to create get pkt fate request; result = %d\n", result); + return result; + } + + result = requestResponse(request); + if (result != WIFI_SUCCESS) { + ALOGE("Failed to register get pkt fate response; result = %d\n", result); + } + return result; + } + + int handleResponse(WifiEvent& reply) { + ALOGD("In GetPktFateCommand::handleResponse\n"); + + if (reply.get_cmd() != NL80211_CMD_VENDOR) { + ALOGI("Ignoring reply with cmd = %d", reply.get_cmd()); + return NL_SKIP; + } + + int id = reply.get_vendor_id(); + int subcmd = reply.get_vendor_subcmd(); + nlattr *vendor_data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); + int len = reply.get_vendor_data_len(); + + ALOGI("Id = %0x, subcmd = %d, len = %d", id, subcmd, len); + + if (mReqType == TX_PACKET_FATE) { + ALOGI("Response recieved for get TX pkt fate command\n"); + } else if (mReqType == RX_PACKET_FATE) { + ALOGI("Response recieved for get RX pkt fate command\n"); + } else if (mReqType == PACKET_MONITOR_START) { + ALOGI("Response recieved for monitor pkt fate command\n"); + return NL_OK; + } else { + ALOGE("Response recieved for unknown pkt fate command\n"); + return NL_SKIP; + } + + if (vendor_data == NULL || len == 0) { + ALOGE("no vendor data in GetPktFateCommand response; ignoring it\n"); + return NL_SKIP; + } + + for (nl_iterator it(vendor_data); it.has_next(); it.next()) { + if (it.get_type() == LOGGER_ATTRIBUTE_PKT_FATE_NUM) { + *mNoProvidedFates = it.get_u32(); + ALOGI("No: of pkt fates provided is %d\n", *mNoProvidedFates); + } else { + ALOGE("Ignoring invalid attribute type = %d, size = %d\n", + it.get_type(), it.get_len()); + } + } + + return NL_OK; + } + + int handleEvent(WifiEvent& event) { + /* NO events to handle here! */ + return NL_SKIP; + } +}; + +wifi_error wifi_start_pkt_fate_monitoring(wifi_interface_handle handle) +{ + PacketFateCommand *cmd = new PacketFateCommand(handle); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; +} + +wifi_error wifi_get_tx_pkt_fates(wifi_interface_handle handle, + wifi_tx_report *tx_report_bufs, size_t n_requested_fates, + size_t *n_provided_fates) +{ + PacketFateCommand *cmd = new PacketFateCommand(handle, tx_report_bufs, + n_requested_fates, n_provided_fates); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; +} + +wifi_error wifi_get_rx_pkt_fates(wifi_interface_handle handle, + wifi_rx_report *rx_report_bufs, size_t n_requested_fates, + size_t *n_provided_fates) +{ + PacketFateCommand *cmd = new PacketFateCommand(handle, rx_report_bufs, + n_requested_fates, n_provided_fates); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; +} diff --git a/wifi/wifi_hal/wifi_offload.cpp b/wifi/wifi_hal/wifi_offload.cpp new file mode 100644 index 0000000..9ef1912 --- /dev/null +++ b/wifi/wifi_hal/wifi_offload.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Portions copyright (C) 2017 Broadcom Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <linux/rtnetlink.h> +#include <netpacket/packet.h> +#include <linux/filter.h> +#include <linux/errqueue.h> + +#include <linux/pkt_sched.h> +#include <netlink/object-api.h> +#include <netlink/netlink.h> +#include <netlink/socket.h> +#include <netlink-private/object-api.h> +#include <netlink-private/types.h> + + +#include "nl80211_copy.h" +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include <log/log.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +typedef enum { + WIFI_OFFLOAD_START_MKEEP_ALIVE = ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START, + WIFI_OFFLOAD_STOP_MKEEP_ALIVE, +} WIFI_OFFLOAD_SUB_COMMAND; + +typedef enum { + MKEEP_ALIVE_ATTRIBUTE_ID, + MKEEP_ALIVE_ATTRIBUTE_IP_PKT, + MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN, + 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("MKeepAliveCommand", 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("MKeepAliveCommand", 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); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; + } else { + ALOGE("Invalid mkeep_alive parameters"); + return WIFI_ERROR_INVALID_ARGS; + } +} + +/* API to stop sending mkeep_alive packet. */ +wifi_error wifi_stop_sending_offloaded_packet(wifi_request_id index, wifi_interface_handle iface) +{ + if (index > 0 && index <= N_AVAIL_ID) { + MKeepAliveCommand *cmd = new MKeepAliveCommand(iface, index, STOP_MKEEP_ALIVE); + NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); + wifi_error result = (wifi_error)cmd->start(); + cmd->releaseRef(); + return result; + } else { + ALOGE("Invalid mkeep_alive parameters"); + return WIFI_ERROR_INVALID_ARGS; + } +} diff --git a/wifi/wpa_supplicant_8_lib/Android.mk b/wifi/wpa_supplicant_8_lib/Android.mk new file mode 100644 index 0000000..0d8a47f --- /dev/null +++ b/wifi/wpa_supplicant_8_lib/Android.mk @@ -0,0 +1,74 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +LOCAL_PATH := $(call my-dir) + +ifeq ($(WPA_SUPPLICANT_VERSION),VER_0_8_X) + +ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),) + CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y +endif + +WPA_SUPPL_DIR = external/wpa_supplicant_8 +WPA_SRC_FILE := + +include $(WPA_SUPPL_DIR)/wpa_supplicant/android.config + +WPA_SUPPL_DIR_INCLUDE = $(WPA_SUPPL_DIR)/src \ + $(WPA_SUPPL_DIR)/src/common \ + $(WPA_SUPPL_DIR)/src/drivers \ + $(WPA_SUPPL_DIR)/src/l2_packet \ + $(WPA_SUPPL_DIR)/src/utils \ + $(WPA_SUPPL_DIR)/src/wps \ + $(WPA_SUPPL_DIR)/wpa_supplicant + +ifdef CONFIG_DRIVER_NL80211 +WPA_SUPPL_DIR_INCLUDE += external/libnl/include +WPA_SRC_FILE += driver_cmd_nl80211.c +endif + +ifdef CONFIG_DRIVER_WEXT +WPA_SRC_FILE += driver_cmd_wext.c +endif + +ifeq ($(TARGET_ARCH),arm) +# To force sizeof(enum) = 4 +L_CFLAGS += -mabi=aapcs-linux +endif + +ifdef CONFIG_ANDROID_LOG +L_CFLAGS += -DCONFIG_ANDROID_LOG +endif + +ifdef CONFIG_P2P +L_CFLAGS += -DCONFIG_P2P +endif + +L_CFLAGS += -Wall -Werror -Wno-unused-parameter -Wno-macro-redefined + +######################## + +include $(CLEAR_VARS) +LOCAL_MODULE := lib_driver_cmd_rtl +LOCAL_SHARED_LIBRARIES := libc libcutils +LOCAL_CFLAGS := $(L_CFLAGS) +LOCAL_SRC_FILES := $(WPA_SRC_FILE) +LOCAL_C_INCLUDES := $(WPA_SUPPL_DIR_INCLUDE) +LOCAL_VENDOR_MODULE := true +include $(BUILD_STATIC_LIBRARY) + +######################## + +endif diff --git a/wifi/wpa_supplicant_8_lib/MODULE_LICENSE_BSD b/wifi/wpa_supplicant_8_lib/MODULE_LICENSE_BSD new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/wifi/wpa_supplicant_8_lib/MODULE_LICENSE_BSD diff --git a/wifi/wpa_supplicant_8_lib/NOTICE b/wifi/wpa_supplicant_8_lib/NOTICE new file mode 100644 index 0000000..d4d14d0 --- /dev/null +++ b/wifi/wpa_supplicant_8_lib/NOTICE @@ -0,0 +1,38 @@ + +Copyright (c) 2005-2010, The Android Open Source Project +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of The Android Open Source Project nor the names + of its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net> + * Copyright (c) 2009-2010, Atheros Communications + * + * This software is distributed under the terms of BSD license. diff --git a/wifi/wpa_supplicant_8_lib/driver_cmd_nl80211.c b/wifi/wpa_supplicant_8_lib/driver_cmd_nl80211.c new file mode 100644 index 0000000..9b4c5e3 --- /dev/null +++ b/wifi/wpa_supplicant_8_lib/driver_cmd_nl80211.c @@ -0,0 +1,201 @@ +/* + * Driver interaction with extended Linux CFG8021 + * + * This software is distributed under the terms of BSD license. + * + */ + +#include "includes.h" +#include <sys/types.h> +#include <fcntl.h> +#include <net/if.h> + +#include "common.h" +#include "linux_ioctl.h" +#include "driver_nl80211.h" +#include "wpa_supplicant_i.h" +#include "config.h" +#ifdef ANDROID +#include "android_drv.h" +#endif + +typedef struct android_wifi_priv_cmd { + char *bufaddr; + int used_len; + int total_len; +} android_wifi_priv_cmd; + +static int drv_errors = 0; + +static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv) +{ + drv_errors++; + if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { + drv_errors = 0; + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); + } +} + +static void wpa_driver_notify_country_change(void *ctx, char *cmd) +{ + if ((os_strncasecmp(cmd, "COUNTRY", 7) == 0) || + (os_strncasecmp(cmd, "SETBAND", 7) == 0)) { + union wpa_event_data event; + + os_memset(&event, 0, sizeof(event)); + event.channel_list_changed.initiator = REGDOM_SET_BY_USER; + if (os_strncasecmp(cmd, "COUNTRY", 7) == 0) { + event.channel_list_changed.type = REGDOM_TYPE_COUNTRY; + if (os_strlen(cmd) > 9) { + event.channel_list_changed.alpha2[0] = cmd[8]; + event.channel_list_changed.alpha2[1] = cmd[9]; + } + } else { + event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN; + } + wpa_supplicant_event(ctx, EVENT_CHANNEL_LIST_CHANGED, &event); + } +} + +int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf, + size_t buf_len ) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ifreq ifr; + android_wifi_priv_cmd priv_cmd; + int ret = 0; + + if (bss->ifindex <= 0 && bss->wdev_id > 0) { + /* DRIVER CMD received on the DEDICATED P2P Interface which doesn't + * have an NETDEVICE associated with it. So we have to re-route the + * command to the parent NETDEVICE + */ + struct wpa_supplicant *wpa_s = (struct wpa_supplicant *)(drv->ctx); + + wpa_printf(MSG_DEBUG, "Re-routing DRIVER cmd to parent iface"); + if (wpa_s && wpa_s->parent) { + /* Update the nl80211 pointers corresponding to parent iface */ + bss = wpa_s->parent->drv_priv; + drv = bss->drv; + wpa_printf(MSG_DEBUG, "Re-routing command to iface: %s" + " cmd (%s)", bss->ifname, cmd); + } + } + + if (os_strcasecmp(cmd, "STOP") == 0) { + linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0); + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED"); + } else if (os_strcasecmp(cmd, "START") == 0) { + linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1); + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED"); + } else if (os_strcasecmp(cmd, "MACADDR") == 0) { + u8 macaddr[ETH_ALEN] = {}; + + ret = linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, macaddr); + if (!ret) + ret = os_snprintf(buf, buf_len, + "Macaddr = " MACSTR "\n", MAC2STR(macaddr)); + } else { /* Use private command */ + os_memcpy(buf, cmd, strlen(cmd) + 1); + memset(&ifr, 0, sizeof(ifr)); + memset(&priv_cmd, 0, sizeof(priv_cmd)); + os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); + priv_cmd.bufaddr = buf; + priv_cmd.used_len = buf_len; + priv_cmd.total_len = buf_len; + ifr.ifr_data = &priv_cmd; + + if ((ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr)) < 0) { + wpa_printf(MSG_ERROR, "%s: failed to issue private command: %s", __func__, cmd); + wpa_driver_send_hang_msg(drv); + } else { + drv_errors = 0; + ret = 0; + if ((os_strcasecmp(cmd, "LINKSPEED") == 0) || + (os_strcasecmp(cmd, "RSSI") == 0) || + (os_strcasecmp(cmd, "GETBAND") == 0) || + (os_strncasecmp(cmd, "WLS_BATCHING", 12) == 0)) + ret = strlen(buf); + wpa_driver_notify_country_change(drv->ctx, cmd); + wpa_printf(MSG_DEBUG, "%s %s len = %d, %zu", __func__, buf, ret, strlen(buf)); + } + } + return ret; +} + +int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration) +{ + char buf[MAX_DRV_CMD_SIZE]; + + memset(buf, 0, sizeof(buf)); + wpa_printf(MSG_DEBUG, "%s: Entry", __func__); + snprintf(buf, sizeof(buf), "P2P_SET_NOA %d %d %d", count, start, duration); + return wpa_driver_nl80211_driver_cmd(priv, buf, buf, strlen(buf)+1); +} + +int wpa_driver_get_p2p_noa(void *priv __unused, u8 *buf __unused, size_t len __unused) +{ + /* Return 0 till we handle p2p_presence request completely in the driver */ + return 0; +} + +int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow) +{ + char buf[MAX_DRV_CMD_SIZE]; + + memset(buf, 0, sizeof(buf)); + wpa_printf(MSG_DEBUG, "%s: Entry", __func__); + snprintf(buf, sizeof(buf), "P2P_SET_PS %d %d %d", legacy_ps, opp_ps, ctwindow); + return wpa_driver_nl80211_driver_cmd(priv, buf, buf, strlen(buf) + 1); +} + +int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) +{ + char *buf; + const struct wpabuf *ap_wps_p2p_ie = NULL; + + char *_cmd = "SET_AP_WPS_P2P_IE"; + char *pbuf; + int ret = 0; + int i, buf_len; + struct cmd_desc { + int cmd; + const struct wpabuf *src; + } cmd_arr[] = { + {0x1, beacon}, + {0x2, proberesp}, + {0x4, assocresp}, + {-1, NULL} + }; + + wpa_printf(MSG_DEBUG, "%s: Entry", __func__); + for (i = 0; cmd_arr[i].cmd != -1; i++) { + ap_wps_p2p_ie = cmd_arr[i].src; + if (ap_wps_p2p_ie) { + buf_len = strlen(_cmd) + 3 + wpabuf_len(ap_wps_p2p_ie); + buf = os_zalloc(buf_len); + if (NULL == buf) { + wpa_printf(MSG_ERROR, "%s: Out of memory", + __func__); + ret = -1; + break; + } + } else { + continue; + } + pbuf = buf; + pbuf += snprintf(pbuf, buf_len - wpabuf_len(ap_wps_p2p_ie), + "%s %d",_cmd, cmd_arr[i].cmd); + *pbuf++ = '\0'; + os_memcpy(pbuf, wpabuf_head(ap_wps_p2p_ie), wpabuf_len(ap_wps_p2p_ie)); + ret = wpa_driver_nl80211_driver_cmd(priv, buf, buf, buf_len); + os_free(buf); + if (ret < 0) + break; + } + + return ret; +} diff --git a/wifi/wpa_supplicant_8_lib/driver_cmd_wext.c b/wifi/wpa_supplicant_8_lib/driver_cmd_wext.c new file mode 100644 index 0000000..7331964 --- /dev/null +++ b/wifi/wpa_supplicant_8_lib/driver_cmd_wext.c @@ -0,0 +1,391 @@ +/* + * Driver interaction with extended Linux Wireless Extensions + * + * This software is distributed under the terms of BSD license. + * + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <net/if_arp.h> +#include <net/if.h> + +#include "linux_wext.h" +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "priv_netlink.h" +#include "driver_wext.h" +#include "ieee802_11_defs.h" +#include "wpa_common.h" +#include "wpa_ctrl.h" +#include "wpa_supplicant_i.h" +#include "config.h" +#include "linux_ioctl.h" +#include "scan.h" + +#include "driver_cmd_wext.h" +#ifdef ANDROID +#include "android_drv.h" +#endif /* ANDROID */ + +#define RSSI_CMD "RSSI" +#define LINKSPEED_CMD "LINKSPEED" + +/** + * wpa_driver_wext_set_scan_timeout - Set scan timeout to report scan completion + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * + * This function can be used to set registered timeout when starting a scan to + * generate a scan completed event if the driver does not report this. + */ +static void wpa_driver_wext_set_scan_timeout(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + int timeout = 10; /* In case scan A and B bands it can be long */ + + /* Not all drivers generate "scan completed" wireless event, so try to + * read results after a timeout. */ + if (drv->scan_complete_events) { + /* + * The driver seems to deliver SIOCGIWSCAN events to notify + * when scan is complete, so use longer timeout to avoid race + * conditions with scanning and following association request. + */ + timeout = 30; + } + wpa_printf(MSG_DEBUG, "Scan requested - scan timeout %d seconds", + timeout); + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx); + eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv, + drv->ctx); +} + +/** + * wpa_driver_wext_combo_scan - Request the driver to initiate combo scan + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @params: Scan parameters + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_combo_scan(void *priv, struct wpa_driver_scan_params *params) +{ + char buf[WEXT_CSCAN_BUF_LEN]; + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret, bp; + unsigned i; + + if (!drv->driver_is_started) { + wpa_printf(MSG_DEBUG, "%s: Driver stopped", __func__); + return 0; + } + + wpa_printf(MSG_DEBUG, "%s: Start", __func__); + + /* Set list of SSIDs */ + bp = WEXT_CSCAN_HEADER_SIZE; + os_memcpy(buf, WEXT_CSCAN_HEADER, bp); + for(i=0; i < params->num_ssids; i++) { + if ((bp + IW_ESSID_MAX_SIZE + 10) >= (int)sizeof(buf)) + break; + wpa_printf(MSG_DEBUG, "For Scan: %s", params->ssids[i].ssid); + buf[bp++] = WEXT_CSCAN_SSID_SECTION; + buf[bp++] = params->ssids[i].ssid_len; + os_memcpy(&buf[bp], params->ssids[i].ssid, params->ssids[i].ssid_len); + bp += params->ssids[i].ssid_len; + } + + /* Set list of channels */ + buf[bp++] = WEXT_CSCAN_CHANNEL_SECTION; + buf[bp++] = 0; + + /* Set passive dwell time (default is 250) */ + buf[bp++] = WEXT_CSCAN_PASV_DWELL_SECTION; + buf[bp++] = (u8)WEXT_CSCAN_PASV_DWELL_TIME; + buf[bp++] = (u8)(WEXT_CSCAN_PASV_DWELL_TIME >> 8); + + /* Set home dwell time (default is 40) */ + buf[bp++] = WEXT_CSCAN_HOME_DWELL_SECTION; + buf[bp++] = (u8)WEXT_CSCAN_HOME_DWELL_TIME; + buf[bp++] = (u8)(WEXT_CSCAN_HOME_DWELL_TIME >> 8); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = buf; + iwr.u.data.length = bp; + + if ((ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr)) < 0) { + if (!drv->bgscan_enabled) + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (cscan): %d", ret); + else + ret = 0; /* Hide error in case of bg scan */ + } + return ret; +} + +static int wpa_driver_wext_set_cscan_params(char *buf, size_t buf_len, char *cmd) +{ + char *pasv_ptr; + int bp, i; + u16 pasv_dwell = WEXT_CSCAN_PASV_DWELL_TIME_DEF; + u8 channel; + + wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd); + + /* Get command parameters */ + pasv_ptr = os_strstr(cmd, ",TIME="); + if (pasv_ptr) { + *pasv_ptr = '\0'; + pasv_ptr += 6; + pasv_dwell = (u16)atoi(pasv_ptr); + if (pasv_dwell == 0) + pasv_dwell = WEXT_CSCAN_PASV_DWELL_TIME_DEF; + } + channel = (u8)atoi(cmd + 5); + + bp = WEXT_CSCAN_HEADER_SIZE; + os_memcpy(buf, WEXT_CSCAN_HEADER, bp); + + /* Set list of channels */ + buf[bp++] = WEXT_CSCAN_CHANNEL_SECTION; + buf[bp++] = channel; + if (channel != 0) { + i = (pasv_dwell - 1) / WEXT_CSCAN_PASV_DWELL_TIME_DEF; + for (; i > 0; i--) { + if ((size_t)(bp + 12) >= buf_len) + break; + buf[bp++] = WEXT_CSCAN_CHANNEL_SECTION; + buf[bp++] = channel; + } + } else { + if (pasv_dwell > WEXT_CSCAN_PASV_DWELL_TIME_MAX) + pasv_dwell = WEXT_CSCAN_PASV_DWELL_TIME_MAX; + } + + /* Set passive dwell time (default is 250) */ + buf[bp++] = WEXT_CSCAN_PASV_DWELL_SECTION; + if (channel != 0) { + buf[bp++] = (u8)WEXT_CSCAN_PASV_DWELL_TIME_DEF; + buf[bp++] = (u8)(WEXT_CSCAN_PASV_DWELL_TIME_DEF >> 8); + } else { + buf[bp++] = (u8)pasv_dwell; + buf[bp++] = (u8)(pasv_dwell >> 8); + } + + /* Set home dwell time (default is 40) */ + buf[bp++] = WEXT_CSCAN_HOME_DWELL_SECTION; + buf[bp++] = (u8)WEXT_CSCAN_HOME_DWELL_TIME; + buf[bp++] = (u8)(WEXT_CSCAN_HOME_DWELL_TIME >> 8); + + /* Set cscan type */ + buf[bp++] = WEXT_CSCAN_TYPE_SECTION; + buf[bp++] = WEXT_CSCAN_TYPE_PASSIVE; + return bp; +} + +static char *wpa_driver_get_country_code(int channels) +{ + char *country = "US"; /* WEXT_NUMBER_SCAN_CHANNELS_FCC */ + + if (channels == WEXT_NUMBER_SCAN_CHANNELS_ETSI) + country = "EU"; + else if( channels == WEXT_NUMBER_SCAN_CHANNELS_MKK1) + country = "JP"; + return country; +} + +static int wpa_driver_set_backgroundscan_params(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + struct wpa_supplicant *wpa_s; + struct iwreq iwr; + int ret = 0, i = 0, bp; + char buf[WEXT_PNO_MAX_COMMAND_SIZE]; + struct wpa_ssid *ssid_conf; + + if (drv == NULL) { + wpa_printf(MSG_ERROR, "%s: drv is NULL. Exiting", __func__); + return -1; + } + if (drv->ctx == NULL) { + wpa_printf(MSG_ERROR, "%s: drv->ctx is NULL. Exiting", __func__); + return -1; + } + wpa_s = (struct wpa_supplicant *)(drv->ctx); + if (wpa_s->conf == NULL) { + wpa_printf(MSG_ERROR, "%s: wpa_s->conf is NULL. Exiting", __func__); + return -1; + } + ssid_conf = wpa_s->conf->ssid; + + bp = WEXT_PNOSETUP_HEADER_SIZE; + os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp); + buf[bp++] = WEXT_PNO_TLV_PREFIX; + buf[bp++] = WEXT_PNO_TLV_VERSION; + buf[bp++] = WEXT_PNO_TLV_SUBVERSION; + buf[bp++] = WEXT_PNO_TLV_RESERVED; + + while ((i < WEXT_PNO_AMOUNT) && (ssid_conf != NULL)) { + /* Check that there is enough space needed for 1 more SSID, the other sections and null termination */ + if ((bp + WEXT_PNO_SSID_HEADER_SIZE + IW_ESSID_MAX_SIZE + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int)sizeof(buf)) + break; + if ((!ssid_conf->disabled) && (ssid_conf->ssid_len <= IW_ESSID_MAX_SIZE)){ + wpa_printf(MSG_DEBUG, "For PNO Scan: %s", ssid_conf->ssid); + buf[bp++] = WEXT_PNO_SSID_SECTION; + buf[bp++] = ssid_conf->ssid_len; + os_memcpy(&buf[bp], ssid_conf->ssid, ssid_conf->ssid_len); + bp += ssid_conf->ssid_len; + i++; + } + ssid_conf = ssid_conf->next; + } + + buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x", WEXT_PNO_SCAN_INTERVAL); + bp += WEXT_PNO_SCAN_INTERVAL_LENGTH; + + buf[bp++] = WEXT_PNO_REPEAT_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x", WEXT_PNO_REPEAT); + bp += WEXT_PNO_REPEAT_LENGTH; + + buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x", WEXT_PNO_MAX_REPEAT); + bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = buf; + iwr.u.data.length = bp; + + ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr); + + if (ret < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d", ret); + drv->errors++; + if (drv->errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { + drv->errors = 0; + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); + } + } else { + drv->errors = 0; + } + return ret; + +} + +int wpa_driver_wext_driver_cmd( void *priv, char *cmd, char *buf, size_t buf_len ) +{ + struct wpa_driver_wext_data *drv = priv; + struct wpa_supplicant *wpa_s = (struct wpa_supplicant *)(drv->ctx); + struct iwreq iwr; + int ret = 0, flags; + + wpa_printf(MSG_DEBUG, "%s %s len = %d", __func__, cmd, buf_len); + + if (!drv->driver_is_started && (os_strcasecmp(cmd, "START") != 0)) { + wpa_printf(MSG_ERROR,"WEXT: Driver not initialized yet"); + return -1; + } + + if (os_strcasecmp(cmd, "RSSI-APPROX") == 0) { + os_strlcpy(cmd, RSSI_CMD, MAX_DRV_CMD_SIZE); + } else if( os_strncasecmp(cmd, "SCAN-CHANNELS", 13) == 0 ) { + int no_of_chan; + + no_of_chan = atoi(cmd + 13); + os_snprintf(cmd, MAX_DRV_CMD_SIZE, "COUNTRY %s", + wpa_driver_get_country_code(no_of_chan)); + } else if (os_strcasecmp(cmd, "STOP") == 0) { + linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0); + } else if( os_strcasecmp(cmd, "RELOAD") == 0 ) { + wpa_printf(MSG_DEBUG,"Reload command"); + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); + return ret; + } else if( os_strcasecmp(cmd, "BGSCAN-START") == 0 ) { + ret = wpa_driver_set_backgroundscan_params(priv); + if (ret < 0) { + return ret; + } + os_strlcpy(cmd, "PNOFORCE 1", MAX_DRV_CMD_SIZE); + drv->bgscan_enabled = 1; + } else if( os_strcasecmp(cmd, "BGSCAN-STOP") == 0 ) { + os_strlcpy(cmd, "PNOFORCE 0", MAX_DRV_CMD_SIZE); + drv->bgscan_enabled = 0; + } + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + os_memcpy(buf, cmd, strlen(cmd) + 1); + iwr.u.data.pointer = buf; + iwr.u.data.length = buf_len; + + if( os_strncasecmp(cmd, "CSCAN", 5) == 0 ) { + if (!wpa_s->scanning && ((wpa_s->wpa_state <= WPA_SCANNING) || + (wpa_s->wpa_state >= WPA_COMPLETED))) { + iwr.u.data.length = wpa_driver_wext_set_cscan_params(buf, buf_len, cmd); + } else { + wpa_printf(MSG_ERROR, "Ongoing Scan action..."); + return ret; + } + } + + ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr); + + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s failed (%d): %s", __func__, ret, cmd); + drv->errors++; + if (drv->errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { + drv->errors = 0; + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); + } + } else { + drv->errors = 0; + ret = 0; + if ((os_strcasecmp(cmd, RSSI_CMD) == 0) || + (os_strcasecmp(cmd, LINKSPEED_CMD) == 0) || + (os_strcasecmp(cmd, "MACADDR") == 0) || + (os_strcasecmp(cmd, "GETPOWER") == 0) || + (os_strcasecmp(cmd, "GETBAND") == 0)) { + ret = strlen(buf); + } else if (os_strcasecmp(cmd, "START") == 0) { + drv->driver_is_started = TRUE; + linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1); + /* os_sleep(0, WPA_DRIVER_WEXT_WAIT_US); + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED"); */ + } else if (os_strcasecmp(cmd, "STOP") == 0) { + drv->driver_is_started = FALSE; + /* wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED"); */ + } else if (os_strncasecmp(cmd, "CSCAN", 5) == 0) { + wpa_driver_wext_set_scan_timeout(priv); + wpa_supplicant_notify_scanning(wpa_s, 1); + } + wpa_printf(MSG_DEBUG, "%s %s len = %d, %d", __func__, buf, ret, strlen(buf)); + } + return ret; +} + +int wpa_driver_signal_poll(void *priv, struct wpa_signal_info *si) +{ + char buf[MAX_DRV_CMD_SIZE]; + struct wpa_driver_wext_data *drv = priv; + char *prssi; + int res; + + os_memset(si, 0, sizeof(*si)); + res = wpa_driver_wext_driver_cmd(priv, RSSI_CMD, buf, sizeof(buf)); + /* Answer: SSID rssi -Val */ + if (res < 0) + return res; + prssi = strcasestr(buf, RSSI_CMD); + if (!prssi) + return -1; + si->current_signal = atoi(prssi + strlen(RSSI_CMD) + 1); + + res = wpa_driver_wext_driver_cmd(priv, LINKSPEED_CMD, buf, sizeof(buf)); + /* Answer: LinkSpeed Val */ + if (res < 0) + return res; + si->current_txrate = atoi(buf + strlen(LINKSPEED_CMD) + 1) * 1000; + + return 0; +} diff --git a/wifi/wpa_supplicant_8_lib/driver_cmd_wext.h b/wifi/wpa_supplicant_8_lib/driver_cmd_wext.h new file mode 100644 index 0000000..116f902 --- /dev/null +++ b/wifi/wpa_supplicant_8_lib/driver_cmd_wext.h @@ -0,0 +1,32 @@ +/* + * Driver interaction with extended Linux Wireless Extensions + * + * This software is distributed under the terms of BSD license. + * + */ +#ifndef DRIVER_CMD_WEXT_H +#define DRIVER_CMD_WEXT_H + +#define WEXT_NUMBER_SCAN_CHANNELS_FCC 11 +#define WEXT_NUMBER_SCAN_CHANNELS_ETSI 13 +#define WEXT_NUMBER_SCAN_CHANNELS_MKK1 14 + +#define WPA_DRIVER_WEXT_WAIT_US 400000 +#define WEXT_CSCAN_BUF_LEN 360 +#define WEXT_CSCAN_HEADER "CSCAN S\x01\x00\x00S\x00" +#define WEXT_CSCAN_HEADER_SIZE 12 +#define WEXT_CSCAN_SSID_SECTION 'S' +#define WEXT_CSCAN_CHANNEL_SECTION 'C' +#define WEXT_CSCAN_NPROBE_SECTION 'N' +#define WEXT_CSCAN_ACTV_DWELL_SECTION 'A' +#define WEXT_CSCAN_PASV_DWELL_SECTION 'P' +#define WEXT_CSCAN_HOME_DWELL_SECTION 'H' +#define WEXT_CSCAN_TYPE_SECTION 'T' +#define WEXT_CSCAN_TYPE_DEFAULT 0 +#define WEXT_CSCAN_TYPE_PASSIVE 1 +#define WEXT_CSCAN_PASV_DWELL_TIME 130 +#define WEXT_CSCAN_PASV_DWELL_TIME_DEF 250 +#define WEXT_CSCAN_PASV_DWELL_TIME_MAX 3000 +#define WEXT_CSCAN_HOME_DWELL_TIME 130 + +#endif /* DRIVER_CMD_WEXT_H */ |