diff options
-rw-r--r-- | service/Android.mk | 31 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/WifiNative.java | 41 | ||||
-rw-r--r-- | service/jni/com_android_server_wifi_WifiNative.cpp | 31 | ||||
-rw-r--r-- | service/lib/common.cpp | 134 | ||||
-rw-r--r-- | service/lib/common.h | 125 | ||||
-rw-r--r-- | service/lib/cpp_bindings.cpp | 184 | ||||
-rw-r--r-- | service/lib/cpp_bindings.h | 238 | ||||
-rw-r--r-- | service/lib/gscan.cpp | 384 | ||||
-rw-r--r-- | service/lib/gscan.h | 66 | ||||
-rw-r--r-- | service/lib/nbd.h | 276 | ||||
-rw-r--r-- | service/lib/rtt.h | 64 | ||||
-rw-r--r-- | service/lib/sync.h | 54 | ||||
-rw-r--r-- | service/lib/wifi_hal.cpp | 488 | ||||
-rw-r--r-- | service/lib/wifi_hal.h | 106 |
14 files changed, 2212 insertions, 10 deletions
diff --git a/service/Android.mk b/service/Android.mk index e8d9d57..e8575cb 100644 --- a/service/Android.mk +++ b/service/Android.mk @@ -29,6 +29,29 @@ LOCAL_MODULE := wifi-service include $(BUILD_JAVA_LIBRARY) +# Make the HAL library +# ============================================================ +include $(CLEAR_VARS) + +LOCAL_REQUIRED_MODULES := + +LOCAL_CFLAGS += -Wno-unused-parameter -Wno-int-to-pointer-cast +LOCAL_CFLAGS += -Wno-maybe-uninitialized -Wno-parentheses +LOCAL_CPPFLAGS += -Wno-conversion-null + +LOCAL_C_INCLUDES += \ + external/libnl-headers \ + +LOCAL_SRC_FILES := \ + lib/wifi_hal.cpp \ + lib/common.cpp \ + lib/cpp_bindings.cpp \ + lib/gscan.cpp + +LOCAL_MODULE := libwifi-hal + +include $(BUILD_STATIC_LIBRARY) + # Make the JNI part # ============================================================ include $(CLEAR_VARS) @@ -43,7 +66,9 @@ LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ $(call include-path-for, libhardware)/hardware \ $(call include-path-for, libhardware_legacy)/hardware_legacy \ - libcore/include + libcore/include \ + $(LOCAL_PATH)/lib + LOCAL_SHARED_LIBRARIES += \ libnativehelper \ @@ -51,7 +76,9 @@ LOCAL_SHARED_LIBRARIES += \ libutils \ libhardware \ libhardware_legacy \ - libandroid_runtime + libandroid_runtime + +LOCAL_STATIC_LIBRARIES += libwifi-hal libnl_2 LOCAL_SRC_FILES := jni/com_android_server_wifi_WifiNative.cpp LOCAL_MODULE := libwifi-service diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java index 4343d5d..f3b1eb9 100644 --- a/service/java/com/android/server/wifi/WifiNative.java +++ b/service/java/com/android/server/wifi/WifiNative.java @@ -86,6 +86,12 @@ public class WifiNative { private native void closeSupplicantConnectionNative(); + public native static boolean startHalNative(); + + public native static void stopHalNative(); + + public native static void waitForHalEventNative(); + /** * Wait for the supplicant to send an event, returning the event string. * @return the event string sent by the supplicant. @@ -423,14 +429,14 @@ public class WifiNative { return doBooleanCommand("DRIVER SETBAND " + band); } - /** - * Sets the bluetooth coexistence mode. - * - * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED}, - * {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or - * {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}. - * @return Whether the mode was successfully set. - */ + /** + * Sets the bluetooth coexistence mode. + * + * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED}, + * {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or + * {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}. + * @return Whether the mode was successfully set. + */ public boolean setBluetoothCoexistenceMode(int mode) { return doBooleanCommand("DRIVER BTCOEXMODE " + mode); } @@ -983,4 +989,23 @@ public class WifiNative { public String getNfcWpsConfigurationToken(int netId) { return doStringCommand("WPS_NFC_CONFIG_TOKEN WPS " + netId); } + + + private class MonitorThread extends Thread { + public void run() { + waitForHalEventNative(); + } + } + + public void startHal() { + if (startHalNative()) { + new MonitorThread().start(); + } else { + Log.i(mTAG, "Could not start hal"); + } + } + + public void stopHal() { + stopHalNative(); + } } diff --git a/service/jni/com_android_server_wifi_WifiNative.cpp b/service/jni/com_android_server_wifi_WifiNative.cpp index d7c6510..1fb2abb 100644 --- a/service/jni/com_android_server_wifi_WifiNative.cpp +++ b/service/jni/com_android_server_wifi_WifiNative.cpp @@ -24,6 +24,7 @@ #include <utils/String16.h> #include "wifi.h" +#include "wifi_hal.h" #define REPLY_BUF_SIZE 4096 // wpa_supplicant's maximum size. #define EVENT_BUF_SIZE 2048 @@ -140,6 +141,33 @@ static jstring android_net_wifi_doStringCommand(JNIEnv* env, jobject, jstring ja return doStringCommand(env,javaCommand); } +static wifi_handle halHandle; +static jboolean android_net_wifi_startHal(JNIEnv* env, jobject) { + ALOGD("In wifi start Hal"); + if (halHandle == NULL) { + wifi_error res = wifi_initialize(&halHandle); + return res == WIFI_SUCCESS; + } + else + return false; +} + +void android_net_wifi_hal_cleaned_up_handler(wifi_handle handle) { + ALOGD("In wifi cleaned up handler"); + halHandle = NULL; +} + +static void android_net_wifi_stopHal(JNIEnv* env, jobject) { + ALOGD("In wifi stop Hal"); + wifi_cleanup(halHandle, android_net_wifi_hal_cleaned_up_handler); +} + +static void android_net_wifi_waitForHalEvents(JNIEnv* env, jobject) { + ALOGD("In wifi waitForHalEvents"); + wifi_event_loop(halHandle); +} + + // ---------------------------------------------------------------------------- /* @@ -161,6 +189,9 @@ static JNINativeMethod gWifiMethods[] = { { "doIntCommandNative", "(Ljava/lang/String;)I", (void*)android_net_wifi_doIntCommand }, { "doStringCommandNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*) android_net_wifi_doStringCommand }, + { "startHalNative", "()Z", (void*) android_net_wifi_startHal }, + { "stopHalNative", "()V", (void*) android_net_wifi_stopHal }, + { "waitForHalEventNative", "()V", (void*) android_net_wifi_waitForHalEvents }, }; int register_android_net_wifi_WifiNative(JNIEnv* env) { diff --git a/service/lib/common.cpp b/service/lib/common.cpp new file mode 100644 index 0000000..2dc634d --- /dev/null +++ b/service/lib/common.cpp @@ -0,0 +1,134 @@ + +#include <stdlib.h> +#include <netlink-types.h> + +#include "wifi_hal.h" +#include "common.h" + +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? */ + + if (info->num_event_cb < info->alloc_event_cb) { + info->event_cb[info->num_event_cb].nl_cmd = cmd; + info->event_cb[info->num_event_cb].vendor_id = 0; + info->event_cb[info->num_event_cb].vendor_subcmd = 0; + info->event_cb[info->num_event_cb].cb_func = func; + info->event_cb[info->num_event_cb].cb_arg = arg; + info->num_event_cb++; + ALOGI("Successfully added event handler %p for command %d", func, cmd); + return WIFI_SUCCESS; + } else { + return WIFI_ERROR_OUT_OF_MEMORY; + } +} + +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? */ + + if (info->num_event_cb < info->alloc_event_cb) { + info->event_cb[info->num_event_cb].nl_cmd = NL80211_CMD_VENDOR; + info->event_cb[info->num_event_cb].vendor_id = id; + info->event_cb[info->num_event_cb].vendor_subcmd = subcmd; + info->event_cb[info->num_event_cb].cb_func = func; + info->event_cb[info->num_event_cb].cb_arg = arg; + info->num_event_cb++; + ALOGI("Successfully added event handler %p for vendor 0x%0x", func, id); + return WIFI_SUCCESS; + } else { + return WIFI_ERROR_OUT_OF_MEMORY; + } +} + +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"); + } + + for (int i = 0; i < info->num_event_cb; i++) { + if (info->event_cb[i].nl_cmd == cmd) { + memmove(&info->event_cb[i], &info->event_cb[i+1], + (info->num_event_cb - i) * sizeof(cb_info)); + info->num_event_cb--; + ALOGI("Successfully removed event handler for command %d", cmd); + return; + } + } +} + +void wifi_unregister_vendor_handler(wifi_handle handle, uint32_t id, int subcmd) +{ + hal_info *info = (hal_info *)handle; + + for (int i = 0; i < info->num_event_cb; i++) { + + if (info->event_cb[i].nl_cmd == NL80211_CMD_VENDOR + && info->event_cb[i].vendor_id == id + && info->event_cb[i].vendor_subcmd == subcmd) { + + memmove(&info->event_cb[i], &info->event_cb[i+1], + (info->num_event_cb - i) * sizeof(cb_info)); + info->num_event_cb--; + ALOGI("Successfully removed event handler for vendor 0x%0x", id); + return; + } + } +} + + +wifi_error wifi_register_cmd(wifi_handle handle, int id, WifiCommand *cmd) +{ + hal_info *info = (hal_info *)handle; + + if (info->num_cmd < info->alloc_cmd) { + info->cmd[info->num_cmd].id = id; + info->cmd[info->num_cmd].cmd = cmd; + info->num_cmd++; + ALOGI("Successfully added command %d: %p", id, cmd); + return WIFI_SUCCESS; + } else { + return WIFI_ERROR_OUT_OF_MEMORY; + } +} + +WifiCommand *wifi_unregister_cmd(wifi_handle handle, int id) +{ + hal_info *info = (hal_info *)handle; + + for (int i = 0; i < info->num_cmd; i++) { + if (info->cmd[i].id == id) { + WifiCommand *cmd = info->cmd[i].cmd; + memmove(&info->cmd[i], &info->cmd[i+1], (info->num_cmd - i) * sizeof(cmd_info)); + info->num_cmd--; + ALOGI("Successfully removed command %d: %p", id, cmd); + return cmd; + } + } + + return NULL; +} + +void wifi_unregister_cmd(wifi_handle handle, WifiCommand *cmd) +{ + hal_info *info = (hal_info *)handle; + + for (int i = 0; i < info->num_cmd; i++) { + if (info->cmd[i].cmd == cmd) { + int id = info->cmd[i].id; + memmove(&info->cmd[i], &info->cmd[i+1], (info->num_cmd - i) * sizeof(cmd_info)); + info->num_cmd--; + ALOGI("Successfully removed command %d: %p", id, cmd); + return; + } + } +} + diff --git a/service/lib/common.h b/service/lib/common.h new file mode 100644 index 0000000..ec993de --- /dev/null +++ b/service/lib/common.h @@ -0,0 +1,125 @@ + +#include "wifi_hal.h" + +#ifndef __WIFI_HAL_COMMON_H__ +#define __WIFI_HAL_COMMON_H__ + +#define LOG_TAG "WifiHAL" + +#include <utils/Log.h> + +#define SOCKET_BUFFER_SIZE (32768U) +#define RECV_BUF_SIZE (4096) +#define DEFAULT_EVENT_CB_SIZE (64) +#define DEFAULT_CMD_SIZE (64) + +/* + 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 */ + +/* TODO: remove these definitions after updating the NL80211 header file */ +#define NL80211_CMD_VENDOR 60 +#define NL80211_ATTR_VENDOR_ID 195 +#define NL80211_ATTR_VENDOR_SUBCMD 196 +#define NL80211_ATTR_VENDOR_DATA 197 +#define NL80211_ATTR_VENDOR_EVENTS 198 + + +/* + 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, + + /* This is reserved for future usage */ + +} ANDROID_VENDOR_SUB_COMMAND; + +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[8+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 + + bool in_event_loop; // Indicates that event loop is active + bool clean_up; // Indication to clean up the socket + + wifi_internal_event_handler event_handler; // default event handler + wifi_cleaned_up_handler cleaned_up_handler; // socket cleaned up handler + + cb_info *event_cb; // event callbacks + int num_event_cb; // number of event callbacks + int alloc_event_cb; // number of allocated callback objects + + 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; + +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); +void wifi_unregister_cmd(wifi_handle handle, WifiCommand *cmd); + + +#endif + diff --git a/service/lib/cpp_bindings.cpp b/service/lib/cpp_bindings.cpp new file mode 100644 index 0000000..28d2552 --- /dev/null +++ b/service/lib/cpp_bindings.cpp @@ -0,0 +1,184 @@ + +#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-types.h> + +#include <linux/nl80211.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +int WifiEvent::parse() { + mHeader = (genlmsghdr *)nlmsg_data(nlmsg_hdr(mMsg)); + return nla_parse(mAttributes, NL80211_ATTR_MAX, genlmsg_attrdata(mHeader, 0), + genlmsg_attrlen(mHeader, 0), NULL); +} + +int WifiRequest::create(int family, uint8_t cmd, int flags, int hdrlen) { + mMsg = nlmsg_alloc(); + if (mMsg != NULL) { + genlmsg_put(mMsg, /* pid = */ 0, /* seq = */ 0, family, + hdrlen, flags, cmd, /* version = */ 0); + return WIFI_SUCCESS; + } else { + return WIFI_ERROR_OUT_OF_MEMORY; + } +} + +int WifiRequest::create(uint32_t id, int subcmd) { + int res = create(NL80211_CMD_VENDOR); + if (res < 0) { + return res; + } + + res = put_u32(NL80211_ATTR_VENDOR_ID, id); + if (res < 0) { + return res; + } + + res = put_u32(NL80211_ATTR_VENDOR_SUBCMD, subcmd); + if (res < 0) { + return res; + } + return 0; +} + +int WifiCommand::requestResponse() { + struct nl_cb *cb = NULL; + int err = create(); /* create the message */ + if (err < 0) + goto out; + + cb = nl_cb_alloc(NL_CB_DEFAULT); /* override the callbacks */ + if (!cb) + goto out; + + err = nl_send_auto_complete(mInfo->cmd_sock, mMsg.getMessage()); /* send message */ + if (err < 0) + goto out; + + err = 1; + + 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) { + int res = wifi_register_handler(mInfo, cmd, 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_handler(mInfo, cmd); + return res; +} + +int WifiCommand::requestVendorEvent(uint32_t id, int subcmd) { + + int res = wifi_register_vendor_handler(mInfo, 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(mInfo, id, subcmd); + return res; +} + +/* Event handlers */ +int WifiCommand::response_handler(struct nl_msg *msg, void *arg) { + 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 { + 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 reply message = %d", res); + res = NL_SKIP; + } else { + res = cmd->handleEvent(event); + } + + cmd->mCondition.signal(); + return res; +} + +/* Other event handlers */ +int WifiCommand::ack_handler(struct nl_msg *msg, void *arg) { + int *err = (int *)arg; + *err = 0; + return NL_STOP; +} + +int WifiCommand::finish_handler(struct nl_msg *msg, void *arg) { + 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; + return NL_SKIP; +} diff --git a/service/lib/cpp_bindings.h b/service/lib/cpp_bindings.h new file mode 100644 index 0000000..525742c --- /dev/null +++ b/service/lib/cpp_bindings.h @@ -0,0 +1,238 @@ + +#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; + } + ~WifiEvent() { + /* don't destroy mMsg; it doesn't belong to us */ + } + + int parse(); + + genlmsghdr *header() { + return mHeader; + } + + int get_cmd() { + return mHeader->cmd; + } + + nlattr ** attributes() { + return mAttributes; + } + + nlattr *get_attribute(int attribute) { + return mAttributes[attribute]; + } + + uint8_t get_u8(int attribute) { + return nla_get_u8(mAttributes[attribute]); + } + + uint16_t get_u16(int attribute) { + return nla_get_u16(mAttributes[attribute]); + } + + uint32_t get_u32(int attribute) { + return nla_get_u32(mAttributes[attribute]); + } + + uint64_t get_u64(int attribute) { + return nla_get_u64(mAttributes[attribute]); + } + + int len(int attribute) { + return nla_len(mAttributes[attribute]); + } + + void *get_data(int attribute) { + return nla_data(mAttributes[attribute]); + } + +}; + +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; + } +}; + +class WifiRequest +{ +private: + int mFamily; + struct nl_msg *mMsg; + +public: + WifiRequest(int family) { + mMsg = NULL; + mFamily = family; + } + ~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, int flags, int hdrlen) { + return create(mFamily, cmd, flags, hdrlen); + } + int create(uint8_t cmd) { + return create(cmd, 0, 0); + } + + int create(uint32_t id, int subcmd); + + 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); + } +}; + +class WifiCommand +{ +protected: + hal_info *mInfo; + WifiRequest mMsg; + Condition mCondition; + wifi_request_id mId; +public: + WifiCommand(wifi_handle handle, wifi_request_id id) + : mMsg(((hal_info *)handle)->nl80211_family_id), mId(id) + { + mInfo = (hal_info *)handle; + ALOGV("WifiCommand %p created", this); + } + + virtual ~WifiCommand() { + ALOGV("WifiCommand %p destroyed", this); + } + + wifi_request_id id() { + return mId; + } + + virtual int create() = 0; + 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); + +protected: + + /* 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("got an event"); + return NL_SKIP; + } + + int registerHandler(int cmd) { + return wifi_register_handler(mInfo, cmd, &event_handler, this); + } + + void unregisterHandler(int cmd) { + wifi_unregister_handler(mInfo, cmd); + } + + int registerVendorHandler(uint32_t id, int subcmd) { + return wifi_register_vendor_handler(mInfo, id, subcmd, &event_handler, this); + } + + void unregisterVendorHandler(uint32_t id, int subcmd) { + wifi_unregister_vendor_handler(mInfo, id, subcmd); + } + +private: + + /* 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 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/service/lib/gscan.cpp b/service/lib/gscan.cpp new file mode 100644 index 0000000..f6ce57b --- /dev/null +++ b/service/lib/gscan.cpp @@ -0,0 +1,384 @@ + +#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-types.h> + +#include <linux/nl80211.h> + +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include <utils/Log.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.h" + +/* TODO: define vendor subcommands */ +typedef enum { + + GSCAN_SUBCMD_START_GSCAN = ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START, + GSCAN_SUBCMD_STOP_GSCAN, + GSCAN_SUBCMD_GSCAN_RESULTS, + + GSCAN_SUBCMD_SET_HOTLIST, + GSCAN_SUBCMD_HOTLIST_RESULTS, + + GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_MONITOR, + GSCAN_SUBCMD_SIGNIFICANT_CHANGE_RESULTS, + + /* Add more sub commands here */ + + GSCAN_SUBCMD_MAX + +} GSCAN_SUB_COMMAND; + +typedef enum { + GSCAN_ATTRIBUTE_SCAN_CHANNELS = 10, + GSCAN_ATTRIBUTE_SCAN_SINGLE_SHOT, + GSCAN_ATTRIBUTE_SCAN_FREQUENCY, + + /* remaining reserved for additional attributes */ + + GSCAN_ATTRIBUTE_HOTLIST_BSSIDS = 20, + + /* remaining reserved for additional attributes */ + + GSCAN_ATTRIBUTE_SIGNIFICANT_CHANGE_ENABLE = 30, + + + GSCAN_ATTRIBUTE_MAX + +} GSCAN_ATTRIBUTE; + +///////////////////////////////////////////////////////////////////////////// + + +class ScanCommand : public WifiCommand +{ +private: + static const unsigned int MAX_CHANNELS = 64; + int mChannels[MAX_CHANNELS]; + int mNumChannels; + IScanResultsHandler mHandler; + static const unsigned int MAX_RESULTS = 64; + wifi_scan_result mResults[MAX_RESULTS]; +public: + ScanCommand(wifi_handle handle, int id, int *channels, unsigned n, + IScanResultsHandler handler) + : WifiCommand(handle, id), mHandler(handler) + { + mNumChannels = n > MAX_CHANNELS ? MAX_CHANNELS : n; + for (int i = 0; i < mNumChannels; i++) { + mChannels[i] = channels[i]; + } + } + + virtual int create() { + int ret = mMsg.create(NL80211_CMD_START_SCHED_SCAN, 0, 0); + if (ret < 0) { + return ret; + } + struct nlattr * attr = mMsg.attr_start(NL80211_ATTR_SCAN_FREQUENCIES); + for (int i = 0; i < mNumChannels; i++) { + ret = mMsg.put_u32(i + 1, mChannels[i]); + if (ret < 0) { + return ret; + } + } + mMsg.attr_end(attr); + mMsg.put_u32(NL80211_ATTR_SCAN_FLAGS, NL80211_SCAN_FLAG_FLUSH); + return ret; + } + + int start() { + return requestEvent(NL80211_CMD_SCHED_SCAN_RESULTS); + } + + virtual int cancel() { + /* TODO: send another command to the driver to cancel the scan */ + wifi_unregister_handler(mInfo, NL80211_CMD_SCHED_SCAN_RESULTS); + return WIFI_SUCCESS; + } + + virtual int handleResponse(WifiEvent reply) { + /* Nothing to do on response! */ + return NL_SKIP; + } + + virtual int handleEvent(WifiEvent event) { + ALOGI("Got a scan results event"); + + int rem = 0, i = 0; + + nl_iterator it(event.get_attribute(NL80211_ATTR_SCAN_SSIDS)); + for ( ; it.has_next(); it.next()) { + struct nlattr *attr = it.get(); + wifi_scan_result *result = &mResults[i]; + char *ssid = (char *)nla_data(attr); + int len = nla_len(attr); + memcpy(result->ssid, ssid, len); + ssid[len] = 0; + } + + (*mHandler.on_scan_results)(id(), i, mResults); + return NL_SKIP; + } +}; + +wifi_error wifi_start_gscan( + wifi_request_id id, + wifi_interface_handle iface, + wifi_scan_cmd_params params, + IScanResultsHandler handler) +{ + interface_info *iinfo = (interface_info *)iface; + wifi_handle handle = iinfo->handle; + hal_info *info = (hal_info *)handle; + + ScanCommand *cmd = new ScanCommand(handle, id, params.channels, params.num_channels, handler); + wifi_register_cmd(handle, id, cmd); + return (wifi_error)cmd->start(); +} + +wifi_error wifi_stop_gscan(wifi_request_id id, wifi_interface_handle iface) +{ + interface_info *iinfo = (interface_info *)iface; + wifi_handle handle = iinfo->handle; + hal_info *info = (hal_info *)handle; + + WifiCommand *cmd = wifi_unregister_cmd(handle, id); + if (cmd) { + cmd->cancel(); + delete cmd; + return WIFI_SUCCESS; + } + + return WIFI_ERROR_INVALID_ARGS; +} + + +///////////////////////////////////////////////////////////////////////////// + +class BssidHotlistCommand : public WifiCommand +{ +private: + static const uint32_t VENDOR_OUI = GOOGLE_OUI; + int mNum; + mac_addr *mBssids; + wifi_hotlist_ap_found_handler mHandler; + static const unsigned int MAX_RESULTS = 64; + wifi_scan_result mResults[MAX_RESULTS]; +public: + BssidHotlistCommand(wifi_handle handle, int id, + mac_addr bssid[], int num, wifi_hotlist_ap_found_handler handler) + : WifiCommand(handle, id), mNum(num), mBssids(bssid), mHandler(handler) + { } + + virtual int create() { + int ret = mMsg.create(VENDOR_OUI, GSCAN_SUBCMD_SET_HOTLIST); + if (ret < 0) { + return ret; + } + struct nlattr * attr = mMsg.attr_start(GSCAN_ATTRIBUTE_HOTLIST_BSSIDS); + for (int i = 0; i < mNum; i++) { + ret = mMsg.put_addr(i + 1, mBssids[i]); + if (ret < 0) { + return ret; + } + } + mMsg.attr_end(attr); + return ret; + } + + int start() { + registerVendorHandler(VENDOR_OUI, GSCAN_SUBCMD_HOTLIST_RESULTS); + int res = requestResponse(); + mMsg.destroy(); + return res; + } + + virtual int cancel() { + /* unregister event handler */ + unregisterVendorHandler(VENDOR_OUI, GSCAN_SUBCMD_HOTLIST_RESULTS); + + /* create set hotlist message with empty hotlist */ + int ret = mMsg.create(VENDOR_OUI, GSCAN_SUBCMD_SET_HOTLIST); + if (ret < 0) { + return WIFI_ERROR_OUT_OF_MEMORY; + } + + struct nlattr * attr = mMsg.attr_start(GSCAN_ATTRIBUTE_HOTLIST_BSSIDS); + if (attr == NULL) { + return WIFI_ERROR_OUT_OF_MEMORY; + } + + mMsg.attr_end(attr); + return requestResponse(); + } + + virtual int handleResponse(WifiEvent reply) { + /* Nothing to do on response! */ + return NL_SKIP; + } + + virtual int handleEvent(WifiEvent event) { + ALOGI("Got a scan results event"); + + int rem = 0, i = 0; + + nl_iterator it(event.get_attribute(NL80211_ATTR_SCAN_SSIDS)); + for ( ; it.has_next(); it.next()) { + struct nlattr *attr = it.get(); + wifi_scan_result *result = &mResults[i]; + char *ssid = (char *)nla_data(attr); + int len = nla_len(attr); + memcpy(result->ssid, ssid, len); + ssid[len] = 0; + } + + (*mHandler.on_hotlist_ap_found)(id(), i, mResults); + return NL_SKIP; + } +}; + +wifi_error wifi_set_bssid_hotlist(wifi_request_id id, wifi_interface_handle iface, + int num_bssid, mac_addr bssid[], wifi_hotlist_ap_found_handler handler) +{ + interface_info *iinfo = (interface_info *)iface; + wifi_handle handle = iinfo->handle; + hal_info *info = (hal_info *)handle; + + BssidHotlistCommand *cmd = new BssidHotlistCommand(handle, id, bssid, num_bssid, handler); + wifi_register_cmd(handle, id, cmd); + return (wifi_error)cmd->start(); +} + +wifi_error wifi_reset_bssid_hotlist(wifi_request_id id, wifi_interface_handle iface) +{ + interface_info *iinfo = (interface_info *)iface; + wifi_handle handle = iinfo->handle; + hal_info *info = (hal_info *)handle; + + WifiCommand *cmd = wifi_unregister_cmd(handle, id); + if (cmd) { + cmd->cancel(); + delete cmd; + return WIFI_SUCCESS; + } + + return WIFI_ERROR_INVALID_ARGS; +} + + +///////////////////////////////////////////////////////////////////////////// + + +class SignificantWifiChangeCommand : public WifiCommand +{ +private: + static const uint32_t VENDOR_OUI = GOOGLE_OUI; + wifi_significant_change_handler mHandler; + static const unsigned int MAX_RESULTS = 64; + wifi_scan_result mResults[MAX_RESULTS]; +public: + SignificantWifiChangeCommand(wifi_handle handle, int id, + wifi_significant_change_handler handler) + : WifiCommand(handle, id), mHandler(handler) + { } + + virtual int create() { + int ret = mMsg.create(VENDOR_OUI, GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_MONITOR); + if (ret < 0) { + return ret; + } + mMsg.put_u8(GSCAN_ATTRIBUTE_SIGNIFICANT_CHANGE_ENABLE, 1); + return ret; + } + + int start() { + registerVendorHandler(VENDOR_OUI, GSCAN_SUBCMD_SIGNIFICANT_CHANGE_RESULTS); + int res = requestResponse(); + mMsg.destroy(); + return res; + } + + virtual int cancel() { + /* unregister event handler */ + unregisterVendorHandler(VENDOR_OUI, GSCAN_SUBCMD_SIGNIFICANT_CHANGE_RESULTS); + + /* create set significant change monitor message with empty hotlist */ + int ret = mMsg.create(VENDOR_OUI, GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_MONITOR); + if (ret < 0) { + return WIFI_ERROR_OUT_OF_MEMORY; + } + mMsg.put_u8(GSCAN_ATTRIBUTE_SIGNIFICANT_CHANGE_ENABLE, 0); + return requestResponse(); + } + + virtual int handleResponse(WifiEvent reply) { + /* Nothing to do on response! */ + return NL_SKIP; + } + + virtual int handleEvent(WifiEvent event) { + ALOGI("Got a scan results event"); + + int rem = 0, i = 0; + + nl_iterator it(event.get_attribute(NL80211_ATTR_SCAN_SSIDS)); + for ( ; it.has_next(); it.next()) { + struct nlattr *attr = it.get(); + wifi_scan_result *result = &mResults[i]; + char *ssid = (char *)nla_data(attr); + int len = nla_len(attr); + memcpy(result->ssid, ssid, len); + ssid[len] = 0; + } + + (*mHandler.on_significant_change)(id(), i, mResults); + return NL_SKIP; + } +}; + +wifi_error wifi_set_significant_change_handler(wifi_request_id id, wifi_interface_handle iface, + wifi_significant_change_handler handler) +{ + interface_info *iinfo = (interface_info *)iface; + wifi_handle handle = iinfo->handle; + hal_info *info = (hal_info *)handle; + + SignificantWifiChangeCommand *cmd = new SignificantWifiChangeCommand(handle, id, handler); + wifi_register_cmd(handle, id, cmd); + return (wifi_error)cmd->start(); +} + +wifi_error wifi_reset_significant_change_handler(wifi_request_id id, wifi_interface_handle iface) +{ + interface_info *iinfo = (interface_info *)iface; + wifi_handle handle = iinfo->handle; + hal_info *info = (hal_info *)handle; + + WifiCommand *cmd = wifi_unregister_cmd(handle, id); + if (cmd) { + cmd->cancel(); + delete cmd; + return WIFI_SUCCESS; + } + + return WIFI_ERROR_INVALID_ARGS; +} diff --git a/service/lib/gscan.h b/service/lib/gscan.h new file mode 100644 index 0000000..43da69e --- /dev/null +++ b/service/lib/gscan.h @@ -0,0 +1,66 @@ + +#include "wifi_hal.h" + +#ifndef __WIFI_HAL_GSCAN_H__ +#define __WIFI_HAL_GSCAN_H__ + +/* AP Scans */ + +typedef struct { + wifi_timestamp ts; // Time of discovery + char ssid[32]; // May not be null terminated + mac_addr bssid; + wifi_channel channel; // channel number (includes all bands) + wifi_rssi rssi; // in db + wifi_timespan rtt; // in nanoseconds + wifi_timespan rtt_sd; // standard deviation in rtt + + // other fields +} wifi_scan_result; + +typedef struct { + void (*on_scan_results) (wifi_request_id id, unsigned num_results, wifi_scan_result *results); +} IScanResultsHandler; + +typedef struct { + int num_channels; + wifi_channel channels[]; // channels to scan; these may include DFS channels + int single_shot; // boolean, 0 => repeated, 1 => single + int frequency; // desired frequency, in scans per minute; if this is too + // high, the firmware should choose to generate results as + // fast as it can instead of failing the command +} wifi_scan_cmd_params; + +wifi_error wifi_start_gscan(wifi_request_id id, wifi_interface_handle iface, + wifi_scan_cmd_params params, IScanResultsHandler handler); +wifi_error wifi_stop_gscan(wifi_request_id id, wifi_interface_handle iface); + +/* + * Expect multiple scans to be active at the same time; the firmware is free to schedule scans + * as it sees fit; as long as frequency requirements are met. This will allow us to have separate + * schedules for DFS scans versus 1/6/11 scans. + * If any channel is supplied multiple times, then the last specification wins. + */ + +/* Background scan - it works with above API with single_shot set to '0' */ + +/* BSSID Hotlist */ +typedef struct { + void (*on_hotlist_ap_found)(wifi_request_id id, unsigned num_results, wifi_scan_result *results); +} wifi_hotlist_ap_found_handler; + +wifi_error wifi_set_bssid_hotlist(wifi_request_id id, wifi_interface_handle iface, + int num_bssid, mac_addr bssid[], wifi_hotlist_ap_found_handler handler); +wifi_error wifi_reset_bssid_hotlist(wifi_request_id id, wifi_interface_handle iface); + +/* Significant wifi change*/ +typedef struct { + void (*on_significant_change)(wifi_request_id id, unsigned num_results, wifi_scan_result *results); +} wifi_significant_change_handler; + +wifi_error wifi_set_significant_change_handler(wifi_request_id id, wifi_interface_handle iface, + wifi_significant_change_handler handler); +wifi_error wifi_reset_significant_change_handler(wifi_request_id id, wifi_interface_handle iface); + +#endif + diff --git a/service/lib/nbd.h b/service/lib/nbd.h new file mode 100644 index 0000000..d4c1592 --- /dev/null +++ b/service/lib/nbd.h @@ -0,0 +1,276 @@ + +#include "wifi_hal.h" + +#ifndef __WIFI_HAL_NBD_H__ +#define __WIFI_HAL_NBD_H__ + +/***************************************************************************** + * NearBy Discovery + *****************************************************************************/ + +#define NBD_MAC_ADDR_LEN 6 +#define NBD_COUNTRY_STRING_LEN 3 + +typedef struct { + u8 addr[NBD_MAC_ADDR_LEN]; + u8 channel; + u8 role; + u8 country_string[NBD_COUNTRY_STRING_LEN]; + u8 operating_class; + u32 availability_bitmap; +} NbdWlanInfrastructureAttr; + +typedef struct { + u8 addr[NBD_MAC_ADDR_LEN]; + u8 channel; + u8 role; + u8 country_string[NBD_COUNTRY_STRING_LEN]; + u8 operating_class; + u32 availability_bitmap; +} NbdP2pOperationAttr; + +typedef struct { + u8 addr[NBD_MAC_ADDR_LEN]; + u8 channel; + u8 reserved; + u8 country_string[NBD_COUNTRY_STRING_LEN]; + u8 operating_class; + u32 availability_bitmap; +} NbdWlanIbssAttr; + +typedef struct { + u8 addr[NBD_MAC_ADDR_LEN]; + u8 channel; + u8 reserved; + u8 country_string[NBD_COUNTRY_STRING_LEN]; + u8 operating_class; + u32 availability_bitmap; + u8 tlvs[]; +} NbdWlanMeshAttr; + +typedef enum { + NBD_RESPONSE_ENABLED, + NBD_RESPONSE_PUBLISH, + NBD_RESPONSE_PUBLISH_CANCEL, + NBD_RESPONSE_PUBLISH_SSI, + NBD_RESPONSE_SUBSCRIBE, + NBD_RESPONSE_SUBSCRIBE_CANCEL, + NBD_RESPONSE_SUBSCRIBE_FOLLOWUP, + NBD_RESPONSE_STATS, + NBD_RESPONSE_DISABLED +} NbdWlanRspType; + +typedef struct { + u16 version : 4; + u16 message_id : 12; + u16 message_length; + u16 handle; + u16 transaction_id; +} NbdWlanHeader; + +typedef struct { + +} NbdHeader; + +typedef struct { + NbdWlanHeader header; + u16 status; + u16 value; + u8 *tlvs; + u16 tlv_len; +} NbdWlanRspData; + +typedef struct { + NbdHeader header; + u8 addr[NBD_MAC_ADDR_LEN]; +} NbdPublishRepliedIndType; + +typedef struct { + NbdHeader header; + u16 reason; +} NbdPublishTerminatedIndType; + +typedef struct { + NbdHeader header; + u8 addr[NBD_MAC_ADDR_LEN]; + u8 *tlvs; + u16 tlv_len; +} NbdPublishFollowupIndType; + +typedef struct { + NbdHeader header; + u16 match_handle; + u8 addr[NBD_MAC_ADDR_LEN]; + u8 *tlvs; + u16 tlv_len; +} NbdSubscribeMatchIndType; + +typedef struct { + NbdHeader header; + u16 match_handle; +} NbdSubscribeUnmatchIndType; + +typedef struct { + NbdHeader header; + u16 reason; +} NbdSubscribeTerminatedIndType; + +typedef struct { + NbdHeader header; + u16 match_handle; + u8 *tlvs; + u16 tlv_len; +} NbdSubscribeSsiIndType; + +typedef struct { + NbdHeader header; + u8 event_id; + u8 *tlvs; + u16 tlv_len; +} NbdDeEventIndType; + +typedef struct { + NbdHeader header; + u16 reason; +} NbdDisableIndType; + +/* Response and Event Callbacks */ +typedef struct { + /* NotifyResponse invoked to notify the status of the Request */ + void (*NotifyResponse)(NbdWlanRspType rsp_type, NbdWlanRspData* rsp_data); + /* Various Event Callback */ + void (*EventPublishReplied)(NbdPublishRepliedIndType* event_data); + void (*EventPublishTerminated)(NbdPublishTerminatedIndType* event_data); + void (*EventPublishFollowup) (NbdPublishFollowupIndType* event_data); + void (*EventSubscribeMatch) (NbdSubscribeMatchIndType* event_data); + /* void (*EventSubscribeUnMatch) (NbdSubscribeUnMatchIndType* event_data); */ + void (*EventSubscribeTerminated) (NbdSubscribeTerminatedIndType* event_data); + void (*EventSubscribeSSI) (NbdSubscribeSsiIndType* event_data); + void (*EventNbdDeEvent) (NbdDeEventIndType* event_data); + void (*EventNbdDisabled) (NbdDisableIndType* event_data); +} NbdCallbackHandler; + +/* Enable NBD in driver*/ +wifi_error wifi_nbd_enable( + wifi_request_id id, + wifi_interface_handle handle, + u8 support_5g, + u16 cluster_low, + u16 cluster_high, + u8 sid, + u8 sync_disc_5g, + u8 rssi_close, + u8 rssi_med, + u8 hc_limit, + u8 random_update_time, + u8 master_pref, + u8 periodic_scan_interval, + NbdWlanInfrastructureAttr* wlan_infra, + NbdP2pOperationAttr* p2p_oper, + NbdWlanIbssAttr* wlan_ibss, + NbdWlanMeshAttr* wlan_mesh, + size_t wlan_mesh_len, + NbdCallbackHandler handler + /* TODO : Add support for Google Specific IE */ + ); + +/* Disable NBD request*/ +wifi_error wifi_nbd_disable( + wifi_request_id id); + +/* Cluster discovery/joining */ +typedef struct { + void (*NotifyClusterDiscovery)(wifi_request_id id, mac_addr addr); + void (*NotifyClusterLoss)(wifi_request_id id); +} NbdClusterDiscoveryHandler; + +wifi_error wifi_nbd_discover( + wifi_interface_handle handle, + NbdClusterDiscoveryHandler handler, + int single_shot + ); + +wifi_error wifi_nbd_start_or_join_cluster( + wifi_interface_handle handle, + int preference, // Local Master preference + byte token[64], // token to publish in discovery beacon + int recommended_hop_count, // hop count to publish in the discovery beacon + int maximum_rssi); // maximum_rssi to publish in the discovery beacon + +/* Send NBD Publish request */ +wifi_error wifi_nbd_publish( + wifi_request_id id, + wifi_interface_handle handle, + u32 ttl, + u32 period, + u32 replied_event, + u32 count, + u32 publish_type, + u32 tx_type, + char *service_name, + size_t service_name_len, + char *rx_match_filter, + size_t rx_match_filter_len, + char *tx_match_filter, + size_t tx_match_filter_len, + char *service_specific_info, + size_t service_specific_info_len, + char *group_key, + size_t group_key_len); + +/* Cancel NBD Publish request */ +wifi_error wifi_nbd_publish_cancel( + wifi_request_id id); + +/* Send NBD Publish Service Specific Info Request */ +wifi_error wifi_nbd_publish_ssi( + wifi_request_id id, + wifi_interface_handle handle, + u32 priority, // should we make this frequency? + char *addr, + char *service_name, + size_t service_name_len, + char *tx_match_filter, + size_t tx_match_filter_len, + char *service_specific_info, + size_t service_specific_info_len); + +/* Send NBD subscribe request */ +wifi_error wifi_nbd_subscribe( + wifi_request_id id, + wifi_interface_handle handle, + u32 subscribe_type, + u32 period, + u32 ttl, + u32 count, + u32 srf, + u32 srfa, + u32 srfi, + u32 ssin, + u32 match, + char* service_name, + size_t service_name_len, + char* rx_match_filter, + size_t rx_match_filter_len, + char *tx_match_filter, + size_t tx_match_filter_len, + char *service_specific_info, + size_t service_specific_info_len, + char *group_key, + size_t group_key_len); + +/* Cancel NBD subscribe request*/ +wifi_error wifi_nbd_subscribe_cancel( + wifi_request_id id); + +/* Send NBD subscribe followup request*/ +wifi_error wifi_nbd_subscribe_followup( + wifi_request_id id, + u16 match_handle); + +/* Get NBD Stat */ +wifi_error wifi_nbd_stats( + wifi_request_id id, + u8 stats_id); + +#endif
\ No newline at end of file diff --git a/service/lib/rtt.h b/service/lib/rtt.h new file mode 100644 index 0000000..143b2dd --- /dev/null +++ b/service/lib/rtt.h @@ -0,0 +1,64 @@ + +#include "wifi_hal.h" + +#ifndef __WIFI_HAL_RTT_H__ +#define __WIFI_HAL_RTT_H__ + +/* RTT */ + +/* Type */ + +#define RTT_TYPE_RTS_CTS 1 +#define RTT_TYPE_NULL_ACK 2 +#define RTT_TYPE_3 3 // This is required for D2D RTT + +typedef struct { + int type; // One of RTT_TYPE_ values + byte token[16]; // Local token to be published in rtt service info + wifi_channel primary; // Use primary when it doesn't conflict with infra + wifi_channel secondary; // Use secondary when primary conflicts with infra + wifi_timespan offset_from_dw_timestamp; // Time offset to go to primary or secondary channel + char rtt_service_name[32]; // Service name to use + byte rtt_servic_info[256]; // Blob to publish in service info +} RttConfig; + +typedef struct { + void (*on_device_ranged)(wifi_request_id id, mac_addr addr, byte token[16], wifi_timespan ts); + void (*on_failed)(wifi_request_id id, wifi_error reason); +} wifi_rtt_event_handler; + +/* Enable rtt - publishes a service advertising RTT; and starts playing + * two phase nXn RTT protocol. To stop it, use wifi_disable_rtt */ + +wifi_error wifi_enable_rtt(wifi_request_id id, wifi_interface_handle iface, RttConfig config, + wifi_rtt_event_handler handler); +wifi_error wifi_disable_rtt(wifi_request_id id); + +/* RTT Hotlist */ + +typedef struct { + byte token[16]; + int64_t max_rtt_timespan; +} wifi_rtt_hotlist_criterion; + +typedef struct { + void (*on_token_found)(wifi_request_id id, byte token[16], wifi_timespan ts); +} wifi_rtt_hotlist_event_handler; + +wifi_error wifi_set_rtt_hotlist(wifi_request_id id, wifi_interface_handle iface, int num_criteria, + wifi_rtt_hotlist_criterion *criteria, wifi_rtt_hotlist_event_handler handler); +wifi_error wifi_reset_rtt_hotlist(wifi_request_id id); + +/* single shot RTT (Device to AP??) */ + +typedef struct { + void (*on_succeeded) (wifi_request_id id, wifi_timestamp ts); + void (*on_failed) (wifi_request_id id, wifi_error reason); +} IRttEventHandler; + +wifi_error wifi_rtt_request_range(wifi_request_id id, wifi_interface_handle iface, mac_addr addr, + RttConfig config, int continuous, IRttEventHandler *handler); +void wifi_rtt_cancel_range_request(wifi_request_id id); + +#endif + diff --git a/service/lib/sync.h b/service/lib/sync.h new file mode 100644 index 0000000..cea2ea9 --- /dev/null +++ b/service/lib/sync.h @@ -0,0 +1,54 @@ + +#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/service/lib/wifi_hal.cpp b/service/lib/wifi_hal.cpp new file mode 100644 index 0000000..d300ea6 --- /dev/null +++ b/service/lib/wifi_hal.cpp @@ -0,0 +1,488 @@ + +#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-types.h> + +#include <linux/nl80211.h> + +#include <dirent.h> +#include <net/if.h> + +#include "sync.h" + +#define LOG_TAG "WifiHAL" + +#include <utils/Log.h> + +#include "wifi_hal.h" +#include "common.h" +#include "cpp_bindings.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 + +static void internal_event_handler(wifi_handle handle, int events); +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); + +/* Initialize/Cleanup */ + +void wifi_socket_set_local_port(struct nl_sock *sock, uint32_t port) +{ + uint32_t pid = getpid() & 0x3FFFFF; + + if (port == 0) { + sock->s_flags &= ~NL_OWN_PORT; + } else { + sock->s_flags |= NL_OWN_PORT; + } + + sock->s_local.nl_pid = 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_nl *addr_nl = &(sock->s_local); + ALOGI("socket address is %d:%d:%d:%d", + addr_nl->nl_family, addr_nl->nl_pad, addr_nl->nl_pid, addr_nl->nl_groups); + + 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; +} + +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"); + struct nl_sock *cmd_sock = wifi_create_nl_socket(WIFI_HAL_CMD_SOCK_PORT); + if (cmd_sock == NULL) { + ALOGE("Could not create handle"); + 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); + return WIFI_ERROR_UNKNOWN; + } + + struct nl_cb *cb = nl_socket_get_cb(event_sock); + if (cb == NULL) { + ALOGE("Could not create handle"); + return WIFI_ERROR_UNKNOWN; + } + + ALOGI("cb->refcnt = %d", cb->cb_refcnt); + 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; + } + + *handle = info; + + wifi_add_membership(*handle, "scan"); + wifi_add_membership(*handle, "mlme"); + wifi_add_membership(*handle, "regulatory"); + wifi_add_membership(*handle, "vendor"); + + wifi_init_interfaces(*handle); + // ALOGI("Found %d interfaces", info->num_interfaces); + + ALOGI("Initialized Wifi HAL Successfully"); + return WIFI_SUCCESS; +} + +static int wifi_add_membership(wifi_handle handle, const char *group) +{ + hal_info *info = (hal_info *)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 = (hal_info *)handle; + wifi_cleaned_up_handler cleaned_up_handler = info->cleaned_up_handler; + + if (info->cmd_sock != 0) { + nl_socket_free(info->cmd_sock); + nl_socket_free(info->event_sock); + info->cmd_sock = NULL; + info->event_sock = NULL; + } + + (*cleaned_up_handler)(handle); + free(info); + + ALOGI("Internal cleanup completed"); +} + +void wifi_cleanup(wifi_handle handle, wifi_cleaned_up_handler handler) +{ + hal_info *info = (hal_info *)handle; + info->cleaned_up_handler = handler; + info->clean_up = true; + + ALOGI("Wifi cleanup completed"); +} + +static int internal_pollin_handler(wifi_handle handle) +{ + hal_info *info = (hal_info *)handle; + struct nl_cb *cb = nl_socket_get_cb(info->event_sock); + int res = nl_recvmsgs(info->event_sock, cb); + nl_cb_put(cb); + return res; +} + +static void internal_event_handler(wifi_handle handle, int events) +{ + if (events & POLLERR) { + ALOGE("Error reading from socket"); + } else if (events & POLLHUP) { + ALOGE("Remote side hung up"); + } else if (events & POLLIN) { + ALOGI("Found some events!!!"); + internal_pollin_handler(handle); + } else { + ALOGE("Unknown event - %0x", events); + } +} + +/* Run event handler */ +void wifi_event_loop(wifi_handle handle) +{ + hal_info *info = (hal_info *)handle; + if (info->in_event_loop) { + return; + } else { + info->in_event_loop = true; + } + + pollfd pfd; + memset(&pfd, 0, sizeof(pfd)); + + pfd.fd = nl_socket_get_fd(info->event_sock); + pfd.events = POLLIN; + + /* TODO: Add support for timeouts */ + + do { + int timeout = -1; /* Infinite timeout */ + pfd.revents = 0; + ALOGI("Polling socket"); + int result = poll(&pfd, 1, -1); + ALOGI("Poll result = %0x", result); + if (result < 0) { + ALOGE("Error polling socket"); + } else if (pfd.revents & (POLLIN | POLLHUP | POLLERR)) { + internal_event_handler(handle, pfd.revents); + } + } while (!info->clean_up); + + + ALOGI("Cleaning up"); + internal_cleaned_up_handler(handle); +} + +/////////////////////////////////////////////////////////////////////////////////////// + +static int internal_valid_message_handler(nl_msg *msg, void *arg) +{ + wifi_handle handle = (wifi_handle)arg; + hal_info *info = (hal_info *)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; + + if (cmd == NL80211_CMD_VENDOR) { + vendor_id = event.get_u32(NL80211_ATTR_VENDOR_ID); + // subcmd = event.get_u32(NL80211_ATTR_VENDOR_SUBCMD); + } + + ALOGI("event received %d, vendor_id = 0x%0x", cmd, vendor_id); + + 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) { + /* event for a different vendor, ignore it */ + continue; + } + + cb_info *cbi = &(info->event_cb[i]); + return (*(cbi->cb_func))(msg, cbi->cb_arg); + } + } + + return NL_OK; +} + +/////////////////////////////////////////////////////////////////////////////////////// + +class GetMulticastIdCommand : public WifiCommand +{ +private: + const char *mName; + const char *mGroup; + int mId; +public: + GetMulticastIdCommand(wifi_handle handle, const char *name, const char *group) + : WifiCommand(handle, 0) + { + mName = name; + mGroup = group; + mId = -1; + } + + int getId() { + return mId; + } + + virtual int create() { + int nlctrlFamily = genl_ctrl_resolve(mInfo->cmd_sock, "nlctrl"); + // 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; + } + +}; + +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); + + 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; + } + 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; +} + +///////////////////////////////////////////////////////////////////////////// + diff --git a/service/lib/wifi_hal.h b/service/lib/wifi_hal.h new file mode 100644 index 0000000..5852528 --- /dev/null +++ b/service/lib/wifi_hal.h @@ -0,0 +1,106 @@ + +#ifndef __WIFI_HAL_H__ +#define __WIFI_HAL_H__ + +typedef enum { + WIFI_SUCCESS = 0, + WIFI_ERROR_NONE = 0, + WIFI_ERROR_UNKNOWN = -1, + WIFI_ERROR_UNINITIALIZED = -2, + WIFI_ERROR_NOT_SUPPORTED = -3, + WIFI_ERROR_NOT_AVAILABLE = -4, // Not available right now, but try later + WIFI_ERROR_INVALID_ARGS = -5, + WIFI_ERROR_INVALID_REQUEST_ID = -6, + WIFI_ERROR_TIMED_OUT = -7, + WIFI_ERROR_TOO_MANY_REQUESTS = -8, // Too many instances of this request + WIFI_ERROR_OUT_OF_MEMORY = -9 +} wifi_error; + +typedef unsigned char byte; +typedef unsigned char u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef void * wifi_handle; +typedef int wifi_request_id; +typedef int wifi_channel; +typedef int wifi_rssi; +typedef byte mac_addr[6]; +typedef int64_t wifi_timestamp; // In microseconds (us) +typedef int64_t wifi_timespan; // In nanoseconds (ns) +typedef void * wifi_interface_handle; + +/* Initialize/Cleanup */ + +wifi_error wifi_initialize(wifi_handle *handle); +typedef void (*wifi_cleaned_up_handler) (wifi_handle handle); +void wifi_cleanup(wifi_handle handle, wifi_cleaned_up_handler handler); +void wifi_event_loop(wifi_handle handle); + +/* Error handling */ +void wifi_get_error_info(wifi_error err, const char **msg); // return a pointer to a static string + +/* Feature enums */ +#define WIFI_FEATURE_INFRA 0x0001 // Basic infrastructure mode +#define WIFI_FEATURE_INFRA_5G 0x0002 // Support for 5 GHz Band +#define WIFI_FEATURE_HOTSPOT 0x0004 // Support for GAS/ANQP +#define WIFI_FEATURE_P2P 0x0008 // Wifi-Direct +#define WIFI_FEATURE_SOFT_AP 0x0010 // Soft AP +#define WIFI_FEATURE_GSCAN 0x0020 // Google-Scan APIs +#define WIFI_FEATURE_NBD 0x0040 // NearBy discovery +#define WIFI_FEATURE_D2D_RTT 0x0080 // Device-to-device RTT +#define WIFI_FEATURE_D2AP_RTT 0x0100 // Device-to-AP RTT +#define WIFI_FEATURE_BATCH_GSCAN 0x0200 // Batched G-Scan +#define WIFI_FEATURE_PNO 0x0400 // Preferred network offload +#define WIFI_FEATURE_ADDITIONAL_STA 0x0800 // Support for two STAs +#define WIFI_FEATURE_TDLS 0x1000 // Tunnel directed link setup + +// Add more features here + +typedef int feature_set; + +#define IS_MASK_SET(mask, flags) ((flags & mask) == mask) +#define IS_MASK_RESET(mask, flags) ((flags & mask) == 0) + +#define IS_SUPPORTED_FEATURE(feature, featureSet) IS_MASK_SET(feature, fetureSet) +#define IS_UNSUPPORTED_FEATURE(feature, featureSet) IS_MASK_RESET(feature, fetureSet) + +/* Feature set */ +wifi_error wifi_get_supported_feature_set(wifi_handle handle, feature_set *set); + +/* + * Each row represents a valid feature combination; + * all other combinations are invalid! + */ +wifi_error wifi_get_concurrency_matrix(wifi_handle handle, int *size, feature_set **matrix); + +/* List of all supported channels, including 5GHz channels */ +wifi_error wifi_get_supported_channels(wifi_handle handle, int *size, wifi_channel *list); + +/* Enhanced power reporting */ +wifi_error wifi_is_epr_supported(wifi_handle handle); + +/* multiple interface support */ + +wifi_error wifi_get_ifaces(wifi_handle handle, int *num_ifaces, wifi_interface_handle **ifaces); +wifi_error wifi_get_iface_name(wifi_interface_handle iface, char *name, size_t size); + +/* Configuration events */ + +typedef struct { + void (*on_country_code_changed)(char code[2]); // We can get this from supplicant too + + // More event handlers +} wifi_event_handler; + +wifi_error wifi_set_iface_event_handler(wifi_request_id id, wifi_interface_handle iface, wifi_event_handler eh); +wifi_error wifi_reset_iface_event_handler(wifi_request_id id, wifi_interface_handle iface); + +/* include various feature headers */ + +#include "gscan.h" +#include "nbd.h" +#include "rtt.h" + +#endif + |