| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Portions copyright (C) 2020 Broadcom Limited |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <stdint.h> |
| #include <fcntl.h> |
| #include <sys/socket.h> |
| #include <netlink/genl/genl.h> |
| #include <netlink/genl/family.h> |
| #include <netlink/genl/ctrl.h> |
| #include <linux/rtnetlink.h> |
| #include <netpacket/packet.h> |
| #include <linux/filter.h> |
| #include <linux/errqueue.h> |
| |
| #include <linux/pkt_sched.h> |
| #include <netlink/object-api.h> |
| #include <netlink/netlink.h> |
| #include <netlink/socket.h> |
| #include <netlink/handlers.h> |
| |
| #include "sync.h" |
| |
| #define LOG_TAG "WifiHAL" |
| |
| #include <utils/Log.h> |
| |
| #include "wifi_hal.h" |
| #include "common.h" |
| #include "cpp_bindings.h" |
| |
| static const char *TwtCmdToString(int cmd); |
| static void EventGetAttributeData(u8 sub_event_type, nlattr *vendor_data); |
| typedef void *TwtRequest; |
| |
| #define C2S(x) case x: return #x; |
| |
| typedef struct _twt_hal_info { |
| void *twt_handle; |
| void *twt_feature_request; |
| } twt_hal_info_t; |
| |
| twt_hal_info_t twt_info; |
| |
| #define TWT_HANDLE(twt_info) ((twt_info).twt_handle) |
| #define GET_TWT_HANDLE(twt_info) ((TwtHandle *)twt_info.twt_handle) |
| |
| #define WL_TWT_CAP_FLAGS_REQ_SUPPORT (1u << 0u) |
| #define WL_TWT_CAP_FLAGS_RESP_SUPPORT (1u << 1u) |
| #define WL_TWT_CAP_FLAGS_BTWT_SUPPORT (1u << 2u) |
| #define WL_TWT_CAP_FLAGS_FLEX_SUPPORT (1u << 3u) |
| |
| class TwtHandle |
| { |
| public: |
| TwtCallbackHandler mHandlers; |
| TwtHandle(wifi_handle handle, TwtCallbackHandler handlers):mHandlers(handlers) |
| {} |
| |
| }; |
| |
| |
| static const char *TwtCmdToString(int cmd) |
| { |
| switch (cmd) { |
| C2S(TWT_SETUP_REQUEST); |
| C2S(TWT_INFO_FRAME_REQUEST); |
| C2S(TWT_TEAR_DOWN_REQUEST); |
| default: |
| return "UNKNOWN_NAN_CMD"; |
| } |
| } |
| |
| static bool is_twt_sub_event(int sub_event_type) |
| { |
| bool is_twt_event = false; |
| switch (sub_event_type) { |
| case TWT_SETUP_RESPONSE: |
| case TWT_TEARDOWN_COMPLETION: |
| case TWT_INFORM_FRAME: |
| case TWT_NOTIFY: |
| is_twt_event = true; |
| } |
| return is_twt_event; |
| } |
| |
| void EventGetAttributeData(u8 sub_event_type, nlattr *vendor_data) |
| { |
| u8 attr_type = 0; |
| |
| switch (sub_event_type) { |
| case TWT_SETUP_RESPONSE: |
| TwtSetupResponse setup_response; |
| for (nl_iterator it(vendor_data); it.has_next(); it.next()) { |
| attr_type = it.get_type(); |
| switch (attr_type) { |
| case TWT_ATTRIBUTE_CONFIG_ID: |
| ALOGI("config_id = %u\n", it.get_u8()); |
| setup_response.config_id = it.get_u8(); |
| break; |
| case TWT_ATTRIBUTE_NEG_TYPE: |
| ALOGI("neg type = %u\n", it.get_u8()); |
| setup_response.negotiation_type = it.get_u8(); |
| break; |
| case TWT_ATTRIBUTE_REASON_CODE: |
| setup_response.reason_code = (TwtSetupReasonCode)it.get_u8(); |
| ALOGI("reason code = %u\n", setup_response.reason_code); |
| break; |
| case TWT_ATTRIBUTE_STATUS: |
| setup_response.status = it.get_u8(); |
| ALOGI("status = %u\n", setup_response.status); |
| break; |
| case TWT_ATTRIBUTE_TRIGGER_TYPE: |
| setup_response.trigger_type = it.get_u8(); |
| ALOGI("trigger type = %u\n", setup_response.trigger_type); |
| break; |
| case TWT_ATTRIBUTE_WAKE_DUR_US: |
| setup_response.wake_dur_us = it.get_u32(); |
| ALOGI("wake_dur_us = %d\n", setup_response.wake_dur_us); |
| break; |
| case TWT_ATTRIBUTE_WAKE_INT_US: |
| setup_response.wake_int_us = it.get_u32(); |
| ALOGI("wake_int_us = %d\n", setup_response.wake_int_us); |
| break; |
| case TWT_ATTRIBUTE_WAKE_TIME_OFF_US: |
| setup_response.wake_time_off_us = it.get_u32(); |
| ALOGI("wake_time_off_us = %d\n", setup_response.wake_time_off_us); |
| break; |
| default: |
| if (attr_type != TWT_ATTRIBUTE_SUB_EVENT) { |
| ALOGE("Unknown attr_type: %d\n", attr_type); |
| } |
| break; |
| } |
| } |
| GET_TWT_HANDLE(twt_info)->mHandlers.EventTwtSetupResponse(&setup_response); |
| break; |
| case TWT_TEARDOWN_COMPLETION: |
| TwtTeardownCompletion teardown_event; |
| for (nl_iterator it(vendor_data); it.has_next(); it.next()) { |
| attr_type = it.get_type(); |
| switch (attr_type) { |
| case TWT_ATTRIBUTE_CONFIG_ID: |
| ALOGI("config_id = %u\n", it.get_u8()); |
| teardown_event.config_id = it.get_u8(); |
| break; |
| case TWT_ATTRIBUTE_STATUS: |
| teardown_event.status = it.get_u8(); |
| ALOGI("status = %u\n", teardown_event.status); |
| break; |
| case TWT_ATTRIBUTE_ALL_TWT: |
| teardown_event.all_twt = it.get_u8(); |
| ALOGI("all_twt = %d\n", teardown_event.all_twt); |
| break; |
| case TWT_ATTRIBUTE_REASON_CODE: |
| teardown_event.reason = (TwtTeardownReason)it.get_u8(); |
| ALOGI("reason = %u\n", teardown_event.reason); |
| break; |
| default: |
| if (attr_type != TWT_ATTRIBUTE_SUB_EVENT) { |
| ALOGE("Unknown attr_type: %d\n", attr_type); |
| } |
| break; |
| } |
| } |
| GET_TWT_HANDLE(twt_info)->mHandlers.EventTwtTeardownCompletion(&teardown_event); |
| break; |
| case TWT_INFORM_FRAME: |
| TwtInfoFrameReceived info_frame_event; |
| for (nl_iterator it(vendor_data); it.has_next(); it.next()) { |
| attr_type = it.get_type(); |
| switch (attr_type) { |
| case TWT_ATTRIBUTE_CONFIG_ID: |
| ALOGI("config_id = %u\n", it.get_u8()); |
| info_frame_event.config_id = it.get_u8(); |
| break; |
| case TWT_ATTRIBUTE_REASON_CODE: |
| info_frame_event.reason = (TwtInfoFrameReason)it.get_u8(); |
| ALOGI("reason = %u\n", info_frame_event.reason); |
| break; |
| case TWT_ATTRIBUTE_STATUS: |
| info_frame_event.status = it.get_u8(); |
| ALOGI("status = %u\n", info_frame_event.status); |
| break; |
| case TWT_ATTRIBUTE_ALL_TWT: |
| info_frame_event.all_twt = it.get_u8(); |
| ALOGI("all_twt = %d\n", info_frame_event.all_twt); |
| break; |
| case TWT_ATTRIBUTE_RESUMED: |
| info_frame_event.twt_resumed = it.get_u8(); |
| ALOGI("twt_resumed = %u\n", info_frame_event.twt_resumed); |
| break; |
| default: |
| if (attr_type != TWT_ATTRIBUTE_SUB_EVENT) { |
| ALOGE("Unknown attr_type: %d\n", attr_type); |
| } |
| break; |
| } |
| } |
| GET_TWT_HANDLE(twt_info)->mHandlers.EventTwtInfoFrameReceived(&info_frame_event); |
| break; |
| case TWT_NOTIFY: |
| TwtDeviceNotify notif_event; |
| for (nl_iterator it(vendor_data); it.has_next(); it.next()) { |
| attr_type = it.get_type(); |
| switch (attr_type) { |
| case TWT_ATTRIBUTE_NOTIFICATION: |
| notif_event.notification = (TwtNotification)it.get_u8(); |
| ALOGI("notification = %u\n", notif_event.notification); |
| break; |
| default: |
| if (attr_type != TWT_ATTRIBUTE_SUB_EVENT) { |
| ALOGE("Unknown attr_type: %d\n", attr_type); |
| } |
| break; |
| } |
| } |
| GET_TWT_HANDLE(twt_info)->mHandlers.EventTwtDeviceNotify(¬if_event); |
| break; |
| default: |
| ALOGE("Unknown event_type: %d\n", sub_event_type); |
| break; |
| } |
| return; |
| } |
| |
| void HandleTwtEvent(nlattr *vendor_data) { |
| u8 sub_event_type = 0; |
| u8 event_type = 0; |
| |
| for (nl_iterator it(vendor_data); it.has_next(); it.next()) { |
| event_type = it.get_type(); |
| if (event_type == TWT_ATTRIBUTE_SUB_EVENT) { |
| sub_event_type = it.get_u8(); |
| if (is_twt_sub_event(sub_event_type)) { |
| EventGetAttributeData(sub_event_type, vendor_data); |
| } |
| } |
| } |
| return; |
| } |
| |
| class TwtEventCap : public WifiCommand |
| { |
| public: |
| TwtEventCap(wifi_interface_handle iface, int id) |
| : WifiCommand("TwtCommand", iface, id) |
| {} |
| |
| int start() |
| { |
| registerTwtVendorEvents(); |
| return WIFI_SUCCESS; |
| } |
| |
| int handleResponse(WifiEvent& reply) { |
| return NL_SKIP; |
| } |
| |
| void registerTwtVendorEvents() |
| { |
| registerVendorHandler(BRCM_OUI, BRCM_VENDOR_EVENT_TWT); |
| } |
| |
| void unregisterTwtVendorEvents() |
| { |
| unregisterVendorHandler(BRCM_OUI, BRCM_VENDOR_EVENT_TWT); |
| } |
| |
| int handleEvent(WifiEvent& event) { |
| u16 attr_type; |
| TwtEventType twt_event; |
| |
| nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); |
| int len = event.get_vendor_data_len(); |
| int event_id = event.get_vendor_subcmd(); |
| |
| ALOGI("EventCapture: Received TWT event: %d\n", event_id); |
| if (!vendor_data || len == 0) { |
| ALOGE("No event data found"); |
| return NL_SKIP; |
| } |
| |
| switch (event_id) { |
| case BRCM_VENDOR_EVENT_TWT: { |
| ALOGE("Handle TWT event: %d\n", event_id); |
| HandleTwtEvent(vendor_data); |
| break; |
| } |
| default: |
| break; |
| } |
| return NL_SKIP; |
| } |
| }; |
| |
| /* To see event prints in console */ |
| wifi_error twt_event_check_request(transaction_id id, wifi_interface_handle iface) |
| { |
| TwtEventCap *cmd = new TwtEventCap(iface, id); |
| if (cmd == NULL) { |
| return WIFI_ERROR_NOT_SUPPORTED; |
| } |
| return (wifi_error)cmd->start(); |
| } |
| |
| ////////////////////////////////////////////////////////////////////////// |
| class GetTwtCapabilitiesCommand : public WifiCommand |
| { |
| TwtCapabilitySet *mCapabilities; |
| public: |
| GetTwtCapabilitiesCommand(wifi_interface_handle iface, TwtCapabilitySet *capabilities) |
| : WifiCommand("GetTwtCapabilitiesCommand", iface, 0), mCapabilities(capabilities) |
| { |
| memset(mCapabilities, 0, sizeof(*mCapabilities)); |
| } |
| |
| virtual int create() { |
| ALOGD("Creating message to get twt capabilities; iface\n"); |
| |
| int ret = mMsg.create(GOOGLE_OUI, TWT_SUBCMD_GETCAPABILITY); |
| if (ret < 0) { |
| ALOGE("Failed to send the twt cap cmd, err = %d\n", ret); |
| } |
| ALOGD("Success to send twt cap cmd, err = %d\n", ret); |
| return ret; |
| } |
| |
| private: |
| TwtCapability parseTwtCap(uint32_t twt_peer_cap) { |
| TwtCapability cap; |
| cap.requester_supported = (twt_peer_cap & WL_TWT_CAP_FLAGS_REQ_SUPPORT) ? 1 : 0; |
| cap.responder_supported = (twt_peer_cap & WL_TWT_CAP_FLAGS_RESP_SUPPORT) ? 1 : 0; |
| cap.broadcast_twt_supported = (twt_peer_cap & WL_TWT_CAP_FLAGS_BTWT_SUPPORT) ? 1 : 0; |
| cap.flexibile_twt_supported = (twt_peer_cap & WL_TWT_CAP_FLAGS_FLEX_SUPPORT) ? 1 : 0; |
| return cap; |
| } |
| |
| protected: |
| virtual int handleResponse(WifiEvent& reply) { |
| |
| ALOGI("In GetTwtCapabilitiesCommand::handleResponse"); |
| |
| if (reply.get_cmd() != NL80211_CMD_VENDOR) { |
| ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); |
| return NL_SKIP; |
| } |
| |
| int id = reply.get_vendor_id(); |
| int subcmd = reply.get_vendor_subcmd(); |
| uint32_t twt_device_cap, twt_peer_cap; |
| |
| nlattr *data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); |
| int len = reply.get_vendor_data_len(); |
| |
| ALOGD("Id = %0x, subcmd = %d, len = %d, expected len = %d", id, subcmd, len); |
| if (data == NULL || len == 0) { |
| ALOGE("no vendor data in GetTwtCapabilitiesCommand response; ignoring it\n"); |
| return NL_SKIP; |
| } |
| |
| for (nl_iterator it(data); it.has_next(); it.next()) { |
| switch (it.get_type()) { |
| case TWT_ATTRIBUTE_DEVICE_CAP: |
| twt_device_cap = it.get_u32(); |
| mCapabilities->device_capability = parseTwtCap(twt_device_cap); |
| break; |
| case TWT_ATTRIBUTE_PEER_CAP: |
| twt_peer_cap = it.get_u32(); |
| mCapabilities->peer_capability = parseTwtCap(twt_peer_cap); |
| break; |
| default: |
| ALOGE("Ignoring invalid attribute type = %d, size = %d\n", |
| it.get_type(), it.get_len()); |
| break; |
| } |
| } |
| |
| ALOGE("Out GetTwtCapabilitiesCommand::handleResponse\n"); |
| return NL_OK; |
| } |
| }; |
| |
| /* API to get TWT capability */ |
| wifi_error twt_get_capability(wifi_interface_handle iface, |
| TwtCapabilitySet *twt_cap_set) |
| { |
| if (iface == NULL) { |
| ALOGE("twt_get_capability: NULL iface pointer provided." |
| " Exit."); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| |
| if (twt_cap_set == NULL) { |
| ALOGE("twt_get_capability: NULL capabilities pointer provided." |
| " Exit."); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| |
| GetTwtCapabilitiesCommand command(iface, twt_cap_set); |
| return (wifi_error) command.requestResponse(); |
| } |
| |
| ////////////////////////////////////////////////////////////////////////// |
| class GetTwtStatsCommand : public WifiCommand |
| { |
| TwtStats* mStats; |
| u8 mConfig_id; |
| public: |
| GetTwtStatsCommand(wifi_interface_handle iface, u8 config_id, TwtStats *stats) |
| : WifiCommand("GetTwtStatsCommand", iface, 0), mConfig_id(config_id), mStats(stats) |
| { |
| memset(mStats, 0, sizeof(*mStats)); |
| } |
| |
| virtual int create() { |
| ALOGD("Creating message to get twt stats; iface = %d", mIfaceInfo->id); |
| |
| int ret = mMsg.create(GOOGLE_OUI, TWT_SUBCMD_GETSTATS); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); |
| ret = mMsg.put_u8(TWT_ATTRIBUTE_CONFIG_ID, mConfig_id); |
| if (ret < 0) { |
| ALOGE("Failed to set mConfig_id %d\n", mConfig_id); |
| return ret; |
| } |
| |
| ALOGI("Successfully configured config id %d\n", mConfig_id); |
| mMsg.attr_end(data); |
| return WIFI_SUCCESS; |
| } |
| |
| protected: |
| virtual int handleResponse(WifiEvent& reply) { |
| |
| ALOGI("In GetTwtStatsCommand::handleResponse"); |
| |
| if (reply.get_cmd() != NL80211_CMD_VENDOR) { |
| ALOGD("Ignoring reply with cmd = %d", reply.get_cmd()); |
| return NL_SKIP; |
| } |
| |
| int id = reply.get_vendor_id(); |
| int subcmd = reply.get_vendor_subcmd(); |
| |
| nlattr *data = reply.get_attribute(NL80211_ATTR_VENDOR_DATA); |
| int len = reply.get_vendor_data_len(); |
| |
| ALOGD("Id = %0x, subcmd = %d, len = %d, expected len = %d", id, subcmd, len); |
| if (data == NULL || len == 0) { |
| ALOGE("no vendor data in GetTwtStatsCommand response; ignoring it\n"); |
| return NL_SKIP; |
| } |
| |
| for (nl_iterator it(data); it.has_next(); it.next()) { |
| switch (it.get_type()) { |
| case TWT_ATTRIBUTE_CONFIG_ID: |
| mStats->config_id = it.get_u8(); |
| break; |
| case TWT_ATTRIBUTE_AVG_PKT_NUM_TX: |
| mStats->avg_pkt_num_tx = it.get_u32(); |
| break; |
| case TWT_ATTRIBUTE_AVG_PKT_NUM_RX: |
| mStats->avg_pkt_num_rx = it.get_u32(); |
| break; |
| case TWT_ATTRIBUTE_AVG_PKT_SIZE_TX: |
| mStats->avg_tx_pkt_size = it.get_u32(); |
| break; |
| case TWT_ATTRIBUTE_AVG_PKT_SIZE_RX: |
| mStats->avg_rx_pkt_size = it.get_u32(); |
| break; |
| case TWT_ATTRIBUTE_AVG_EOSP_DUR: |
| mStats->avg_eosp_dur_us = it.get_u32(); |
| break; |
| case TWT_ATTRIBUTE_EOSP_COUNT: |
| mStats->eosp_count = it.get_u32(); |
| break; |
| case TWT_ATTRIBUTE_NUM_SP: |
| mStats->num_sp = it.get_u32(); |
| break; |
| default: |
| ALOGE("Ignoring invalid attribute type = %d, size = %d\n", |
| it.get_type(), it.get_len()); |
| break; |
| } |
| } |
| |
| return NL_OK; |
| } |
| }; |
| |
| /* API to get TWT stats */ |
| wifi_error twt_get_stats(wifi_interface_handle iface, u8 config_id, TwtStats* stats) |
| { |
| if (iface == NULL) { |
| ALOGE("twt_get_stats: NULL iface pointer provided." |
| " Exit."); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| |
| if (stats == NULL) { |
| ALOGE("TwtCapabilitySet: NULL capabilities pointer provided." |
| " Exit."); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| |
| GetTwtStatsCommand command(iface, config_id, stats); |
| return (wifi_error) command.requestResponse(); |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////////////// |
| class ClearTwtStatsCommand : public WifiCommand |
| { |
| u8 mConfig_id; |
| public: |
| ClearTwtStatsCommand(wifi_interface_handle iface, u8 config_id) |
| : WifiCommand("ClearTwtStatsCommand", iface, 0), mConfig_id(config_id) |
| { |
| } |
| |
| virtual int create() { |
| ALOGD("Creating message to clear twt stats; config_id = %d\n", mConfig_id); |
| |
| int ret = mMsg.create(GOOGLE_OUI, TWT_SUBCMD_CLR_STATS); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| nlattr *data = mMsg.attr_start(NL80211_ATTR_VENDOR_DATA); |
| ret = mMsg.put_u8(TWT_ATTRIBUTE_CONFIG_ID, mConfig_id); |
| if (ret < 0) { |
| ALOGE("Failed to set mConfig_id %d\n", mConfig_id); |
| return ret; |
| } |
| |
| ALOGI("Successfully configured config id %d\n", mConfig_id); |
| mMsg.attr_end(data); |
| return WIFI_SUCCESS; |
| } |
| |
| protected: |
| virtual int handleResponse(WifiEvent& reply) { |
| ALOGD("In ClearTwtStatsCommand::handleResponse"); |
| /* Nothing to do on response! */ |
| return NL_SKIP; |
| } |
| }; |
| |
| /* API to clear TWT stats */ |
| wifi_error twt_clear_stats(wifi_interface_handle iface, u8 config_id) |
| { |
| if (iface == NULL || !config_id) { |
| ALOGE("twt_clear_stats: NULL iface pointer provided." |
| " Exit."); |
| return WIFI_ERROR_INVALID_ARGS; |
| } |
| ALOGE("twt_clear_stats: config id: %d\n", config_id); |
| |
| ClearTwtStatsCommand command(iface, config_id); |
| return (wifi_error) command.requestResponse(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| class TwtFeatureRequest : public WifiCommand |
| { |
| TwtRequest reqContext; |
| TwtRequestType mType; |
| |
| public: |
| TwtFeatureRequest(wifi_interface_handle iface, |
| TwtRequest params, TwtRequestType cmdType) |
| : WifiCommand("TwtFeatureRequest", iface, 0), reqContext(params), mType(cmdType) |
| { |
| } |
| |
| int createRequest(WifiRequest& request) |
| { |
| ALOGI("TWT CMD: %s\n", TwtCmdToString(mType)); |
| if (mType == TWT_SETUP_REQUEST) { |
| return createTwtSetupRequest(request, (TwtSetupRequest *)reqContext); |
| } else if (mType == TWT_INFO_FRAME_REQUEST) { |
| return createInfoFrameRequest(request, (TwtInfoFrameRequest *)reqContext); |
| } else if (mType == TWT_TEAR_DOWN_REQUEST) { |
| return createTearDownRequest(request, (TwtTeardownRequest *)reqContext); |
| } else { |
| ALOGE("%s: Unknown TWT request: %d\n", __func__, mType); |
| return WIFI_ERROR_UNKNOWN; |
| } |
| |
| return WIFI_SUCCESS; |
| } |
| |
| int createTwtSetupRequest(WifiRequest& request, TwtSetupRequest *mParams) |
| { |
| int result = request.create(GOOGLE_OUI, TWT_SUBCMD_SETUP_REQUEST); |
| if (result < 0) { |
| ALOGE("%s Failed to create request, result = %d\n", __func__, result); |
| return result; |
| } |
| |
| /* If handle is 0xFFFF, then update instance_id in response of this request |
| * otherwise, update not needed |
| */ |
| nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); |
| if (mParams->config_id) { |
| result = request.put_u8(TWT_ATTRIBUTE_CONFIG_ID, mParams->config_id); |
| if (result < 0) { |
| ALOGE("%s: Failed to fill config_id = %d, result = %d\n", |
| __func__, mParams->config_id, result); |
| return result; |
| } |
| } |
| |
| if (mParams->negotiation_type) { |
| result = request.put_u8(TWT_ATTRIBUTE_NEG_TYPE, mParams->negotiation_type); |
| if (result < 0) { |
| ALOGE("%s: Failed to fill negotiation_type = %d, result = %d\n", |
| __func__, mParams->negotiation_type, result); |
| return result; |
| } |
| } |
| if (mParams->trigger_type) { |
| result = request.put_u8(TWT_ATTRIBUTE_TRIGGER_TYPE, mParams->trigger_type); |
| if (result < 0) { |
| ALOGE("%s: Failed to fill trigger_type = %d, result = %d\n", |
| __func__, mParams->trigger_type, result); |
| return result; |
| } |
| } |
| if (mParams->wake_dur_us) { |
| result = request.put_u32(TWT_ATTRIBUTE_WAKE_DUR_US, mParams->wake_dur_us); |
| if (result < 0) { |
| ALOGE("%s: Failed to fill wake_dur_us = %d, result = %d\n", |
| __func__, mParams->wake_dur_us, result); |
| return result; |
| } |
| } |
| if (mParams->wake_int_us) { |
| result = request.put_u32(TWT_ATTRIBUTE_WAKE_INT_US, mParams->wake_int_us); |
| if (result < 0) { |
| ALOGE("%s: Failed to fill wake_int_us = %d, result = %d\n", |
| __func__, mParams->wake_int_us, result); |
| return result; |
| } |
| } |
| if (mParams->wake_int_min_us) { |
| result = request.put_u32(TWT_ATTRIBUTE_WAKE_INT_MIN_US, mParams->wake_int_min_us); |
| if (result < 0) { |
| ALOGE("%s: Failed to fill wake_int_min_us = %d, result = %d\n", |
| __func__, mParams->wake_int_min_us, result); |
| return result; |
| } |
| } |
| if (mParams->wake_int_max_us) { |
| result = request.put_u32(TWT_ATTRIBUTE_WAKE_INT_MAX_US, mParams->wake_int_max_us); |
| if (result < 0) { |
| ALOGE("%s: Failed to fill wake_int_max_us = %d, result = %d\n", |
| __func__, mParams->wake_int_max_us, result); |
| return result; |
| } |
| } |
| if (mParams->wake_dur_min_us) { |
| result = request.put_u32(TWT_ATTRIBUTE_WAKE_DUR_MIN_US, mParams->wake_dur_min_us); |
| if (result < 0) { |
| ALOGE("%s: Failed to fill wake_dur_min_us = %d, result = %d\n", |
| __func__, mParams->wake_dur_min_us, result); |
| return result; |
| } |
| } |
| if (mParams->wake_dur_max_us) { |
| result = request.put_u32(TWT_ATTRIBUTE_WAKE_DUR_MAX_US, mParams->wake_dur_max_us); |
| if (result < 0) { |
| ALOGE("%s: Failed to fill wake_dur_max_us = %d, result = %d\n", |
| __func__, mParams->wake_dur_max_us, result); |
| return result; |
| } |
| } |
| if (mParams->avg_pkt_size) { |
| result = request.put_u32(TWT_ATTRIBUTE_AVG_PKT_SIZE, mParams->avg_pkt_size); |
| if (result < 0) { |
| ALOGE("%s: Failed to fill avg_pkt_size = %d, result = %d\n", |
| __func__, mParams->avg_pkt_size, result); |
| return result; |
| } |
| } |
| if (mParams->avg_pkt_num) { |
| result = request.put_u32(TWT_ATTRIBUTE_AVG_PKT_NUM, mParams->avg_pkt_num); |
| if (result < 0) { |
| ALOGE("%s: Failed to fill avg_pkt_num = %d, result = %d\n", |
| __func__, mParams->avg_pkt_num, result); |
| return result; |
| } |
| } |
| if (mParams->wake_time_off_us) { |
| result = request.put_u32(TWT_ATTRIBUTE_WAKE_TIME_OFF_US, mParams->wake_time_off_us); |
| if (result < 0) { |
| ALOGE("%s: Failed to fill wake_time_off_us = %d, result = %d\n", |
| __func__, mParams->wake_time_off_us, result); |
| return result; |
| } |
| } |
| request.attr_end(data); |
| |
| ALOGI("Returning successfully\n"); |
| return result; |
| } |
| |
| int createInfoFrameRequest(WifiRequest& request, TwtInfoFrameRequest *mParams) |
| { |
| int result = request.create(GOOGLE_OUI, TWT_SUBCMD_INFO_FRAME_REQUEST); |
| if (result < 0) { |
| ALOGE("%s: Failed to create request, result = %d\n", __func__, result); |
| return result; |
| } |
| |
| nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); |
| if (mParams->config_id) { |
| result = request.put_u8(TWT_ATTRIBUTE_CONFIG_ID, mParams->config_id); |
| if (result < 0) { |
| ALOGE("%s: Failed to fill config_id = %d, result = %d\n", |
| __func__, mParams->config_id, result); |
| return result; |
| } |
| } |
| if (mParams->resume_time_us) { |
| result = request.put_u32(TWT_ATTRIBUTE_RESUME_TIME_US, mParams->resume_time_us); |
| if (result < 0) { |
| ALOGE("%s: Failed to fill resume_time_us = %d, result = %d\n", |
| __func__, mParams->resume_time_us, result); |
| return result; |
| } |
| } |
| if (mParams->all_twt) { |
| result = request.put_u8(TWT_ATTRIBUTE_ALL_TWT, mParams->all_twt); |
| if (result < 0) { |
| ALOGE("%s: Failed to fill all_twt = %d, result = %d\n", |
| __func__, mParams->all_twt, result); |
| return result; |
| } |
| } |
| request.attr_end(data); |
| return WIFI_SUCCESS; |
| } |
| |
| int createTearDownRequest(WifiRequest& request, TwtTeardownRequest *mParams) |
| { |
| int result = request.create(GOOGLE_OUI, TWT_SUBCMD_TEAR_DOWN_REQUEST); |
| if (result < 0) { |
| ALOGE("%s: Failed to create request, result = %d\n", __func__, result); |
| return result; |
| } |
| |
| nlattr *data = request.attr_start(NL80211_ATTR_VENDOR_DATA); |
| if (mParams->config_id) { |
| result = request.put_u8(TWT_ATTRIBUTE_CONFIG_ID, mParams->config_id); |
| if (result < 0) { |
| ALOGE("%s: Failed to fill config_id = %d, result = %d\n", |
| __func__, mParams->config_id, result); |
| return result; |
| } |
| } |
| if (mParams->negotiation_type) { |
| result = request.put_u8(TWT_ATTRIBUTE_NEG_TYPE, mParams->negotiation_type); |
| if (result < 0) { |
| ALOGE("%s: Failed to fill negotiation_type = %d, result = %d\n", |
| __func__, mParams->negotiation_type, result); |
| return result; |
| } |
| } |
| if (mParams->all_twt) { |
| result = request.put_u8(TWT_ATTRIBUTE_ALL_TWT, mParams->all_twt); |
| if (result < 0) { |
| ALOGE("%s: Failed to fill all_twt = %d, result = %d\n", |
| __func__, mParams->all_twt, result); |
| return result; |
| } |
| } |
| request.attr_end(data); |
| return WIFI_SUCCESS; |
| } |
| |
| int open() |
| { |
| WifiRequest request(familyId(), ifaceId()); |
| int result = createRequest(request); |
| if (result != WIFI_SUCCESS) { |
| ALOGE("%s: failed to create setup request; result = %d", __func__, result); |
| return result; |
| } |
| |
| result = requestResponse(request); |
| if (result != WIFI_SUCCESS) { |
| ALOGE("%s: failed to configure setup; result = %d", __func__, result); |
| return result; |
| } |
| |
| request.destroy(); |
| return WIFI_SUCCESS; |
| } |
| |
| void registerTwtVendorEvents() |
| { |
| registerVendorHandler(BRCM_OUI, BRCM_VENDOR_EVENT_TWT); |
| } |
| |
| void unregisterTwtVendorEvents() |
| { |
| unregisterVendorHandler(BRCM_OUI, BRCM_VENDOR_EVENT_TWT); |
| } |
| |
| virtual int handleResponse(WifiEvent& reply) { |
| ALOGD("Request complete!"); |
| /* Nothing to do on response! */ |
| return NL_SKIP; |
| } |
| |
| int handleEvent(WifiEvent& event) { |
| u16 attr_type; |
| TwtEventType twt_event; |
| |
| nlattr *vendor_data = event.get_attribute(NL80211_ATTR_VENDOR_DATA); |
| int len = event.get_vendor_data_len(); |
| int event_id = event.get_vendor_subcmd(); |
| ALOGI("Received TWT event: %d\n", event_id); |
| |
| if (!vendor_data || len == 0) { |
| ALOGE("No event data found"); |
| return NL_SKIP; |
| } |
| |
| switch (event_id) { |
| case BRCM_VENDOR_EVENT_TWT: { |
| HandleTwtEvent(vendor_data); |
| break; |
| } |
| default: |
| ALOGE("Unknown event: %d\n", event_id); |
| break; |
| } |
| return NL_SKIP; |
| } |
| |
| }; |
| |
| void twt_deinit_handler() |
| { |
| if (twt_info.twt_feature_request) { |
| /* register for Twt vendor events with info mac class*/ |
| TwtFeatureRequest *cmd_event = (TwtFeatureRequest*)(twt_info.twt_feature_request); |
| cmd_event->unregisterTwtVendorEvents(); |
| delete (TwtFeatureRequest*)twt_info.twt_feature_request; |
| twt_info.twt_feature_request = NULL; |
| } |
| if (TWT_HANDLE(twt_info)) { |
| delete GET_TWT_HANDLE(twt_info); |
| TWT_HANDLE(twt_info) = NULL; |
| } |
| ALOGI("wifi twt internal clean up done"); |
| return; |
| } |
| |
| wifi_error twt_register_handler(wifi_interface_handle iface, |
| TwtCallbackHandler handlers) |
| { |
| wifi_handle handle = getWifiHandle(iface); |
| if (TWT_HANDLE(twt_info)) { |
| /* cleanup and re-register */ |
| twt_deinit_handler(); |
| } |
| memset(&twt_info, 0, sizeof(twt_info)); |
| TWT_HANDLE(twt_info) = new TwtHandle(handle, handlers); |
| twt_info.twt_feature_request = |
| (void*)new TwtFeatureRequest(iface, NULL, TWT_LAST); |
| NULL_CHECK_RETURN(twt_info.twt_feature_request, |
| "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); |
| TwtFeatureRequest *cmd_event = (TwtFeatureRequest*)(twt_info.twt_feature_request); |
| cmd_event->registerTwtVendorEvents(); |
| return WIFI_SUCCESS; |
| } |
| |
| wifi_error twt_setup_request(wifi_interface_handle iface, TwtSetupRequest* msg) |
| { |
| wifi_error ret = WIFI_SUCCESS; |
| TwtFeatureRequest *cmd; |
| TwtRequestType cmdType = TWT_SETUP_REQUEST; |
| |
| cmd = new TwtFeatureRequest(iface, (void *)msg, cmdType); |
| NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); |
| |
| ret = (wifi_error)cmd->open(); |
| if (ret != WIFI_SUCCESS) { |
| ALOGE("%s : failed in open, error = %d\n", __func__, ret); |
| } |
| cmd->releaseRef(); |
| return ret; |
| } |
| |
| wifi_error twt_info_frame_request(wifi_interface_handle iface, TwtInfoFrameRequest* msg) |
| { |
| wifi_error ret = WIFI_SUCCESS; |
| TwtFeatureRequest *cmd; |
| TwtRequestType cmdType = TWT_INFO_FRAME_REQUEST; |
| |
| cmd = new TwtFeatureRequest(iface, (void *)msg, cmdType); |
| NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); |
| |
| ret = (wifi_error)cmd->open(); |
| if (ret != WIFI_SUCCESS) { |
| ALOGE("%s : failed in open, error = %d\n", __func__, ret); |
| } |
| cmd->releaseRef(); |
| return ret; |
| } |
| |
| wifi_error twt_teardown_request(wifi_interface_handle iface, TwtTeardownRequest* msg) |
| { |
| wifi_error ret = WIFI_SUCCESS; |
| TwtFeatureRequest *cmd; |
| TwtRequestType cmdType = TWT_TEAR_DOWN_REQUEST; |
| |
| cmd = new TwtFeatureRequest(iface, (void *)msg, cmdType); |
| NULL_CHECK_RETURN(cmd, "memory allocation failure", WIFI_ERROR_OUT_OF_MEMORY); |
| |
| ret = (wifi_error)cmd->open(); |
| if (ret != WIFI_SUCCESS) { |
| ALOGE("%s : failed in open, error = %d\n", __func__, ret); |
| } |
| cmd->releaseRef(); |
| return ret; |
| } |