/* * Copyright (C) 2014 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. */ #include "sync.h" #include #include "wifi_hal.h" #include "nan_i.h" #include "nancommand.h" int NanCommand::putNanEnable(transaction_id id, const NanEnableRequest *pReq) { ALOGV("NAN_ENABLE"); size_t message_len = NAN_MAX_ENABLE_REQ_SIZE; if (pReq == NULL) { cleanup(); return WIFI_ERROR_INVALID_ARGS; } message_len += \ ( pReq->config_support_5g ? (SIZEOF_TLV_HDR + \ sizeof(pReq->support_5g_val)) : 0 \ ) + \ ( pReq->config_sid_beacon ? (SIZEOF_TLV_HDR + \ sizeof(pReq->sid_beacon_val)) : 0 \ ) + \ ( pReq->config_2dot4g_rssi_close ? (SIZEOF_TLV_HDR + \ sizeof(pReq->rssi_close_2dot4g_val)) : 0 \ ) + \ ( pReq->config_2dot4g_rssi_middle ? (SIZEOF_TLV_HDR + \ sizeof(pReq->rssi_middle_2dot4g_val)) : 0 \ ) + \ ( pReq->config_hop_count_limit ? (SIZEOF_TLV_HDR + \ sizeof(pReq->hop_count_limit_val)) : 0 \ ) + \ ( pReq->config_2dot4g_support ? (SIZEOF_TLV_HDR + \ sizeof(pReq->support_2dot4g_val)) : 0 \ ) + \ ( pReq->config_2dot4g_beacons ? (SIZEOF_TLV_HDR + \ sizeof(pReq->beacon_2dot4g_val)) : 0 \ ) + \ ( pReq->config_2dot4g_sdf ? (SIZEOF_TLV_HDR + \ sizeof(pReq->sdf_2dot4g_val)) : 0 \ ) + \ ( pReq->config_5g_beacons ? (SIZEOF_TLV_HDR + \ sizeof(pReq->beacon_5g_val)) : 0 \ ) + \ ( pReq->config_5g_sdf ? (SIZEOF_TLV_HDR + \ sizeof(pReq->sdf_5g_val)) : 0 \ ) + \ ( pReq->config_5g_rssi_close ? (SIZEOF_TLV_HDR + \ sizeof(pReq->rssi_close_5g_val)) : 0 \ ) + \ ( pReq->config_5g_rssi_middle ? (SIZEOF_TLV_HDR + \ sizeof(pReq->rssi_middle_5g_val)) : 0 \ ) + \ ( pReq->config_2dot4g_rssi_proximity ? (SIZEOF_TLV_HDR + \ sizeof(pReq->rssi_proximity_2dot4g_val)) : 0 \ ) + \ ( pReq->config_5g_rssi_close_proximity ? (SIZEOF_TLV_HDR + \ sizeof(pReq->rssi_close_proximity_5g_val)) : 0 \ ) + \ ( pReq->config_rssi_window_size ? (SIZEOF_TLV_HDR + \ sizeof(pReq->rssi_window_size_val)) : 0 \ ) + \ ( pReq->config_oui ? (SIZEOF_TLV_HDR + \ sizeof(pReq->oui_val)) : 0 \ ) + \ ( pReq->config_intf_addr ? (SIZEOF_TLV_HDR + \ sizeof(pReq->intf_addr_val)) : 0 \ ) + \ ( pReq->config_cluster_attribute_val ? (SIZEOF_TLV_HDR + \ sizeof(pReq->config_cluster_attribute_val)) : 0 \ ) + \ ( pReq->config_scan_params ? (SIZEOF_TLV_HDR + \ NAN_MAX_SOCIAL_CHANNELS * sizeof(u32)) : 0 \ ) + \ ( pReq->config_random_factor_force ? (SIZEOF_TLV_HDR + \ sizeof(pReq->random_factor_force_val)) : 0 \ ) + \ ( pReq->config_hop_count_force ? (SIZEOF_TLV_HDR + \ sizeof(pReq->hop_count_force_val)) : 0 \ ) + \ ( pReq->config_24g_channel ? (SIZEOF_TLV_HDR + \ sizeof(u32)) : 0 \ ) + \ ( pReq->config_5g_channel ? (SIZEOF_TLV_HDR + \ sizeof(u32)) : 0 \ ); pNanEnableReqMsg pFwReq = (pNanEnableReqMsg)malloc(message_len); if (pFwReq == NULL) { cleanup(); return WIFI_ERROR_OUT_OF_MEMORY; } ALOGV("Message Len %zu", message_len); memset (pFwReq, 0, message_len); pFwReq->fwHeader.msgVersion = (u16)NAN_MSG_VERSION1; pFwReq->fwHeader.msgId = NAN_MSG_ID_ENABLE_REQ; pFwReq->fwHeader.msgLen = message_len; pFwReq->fwHeader.transactionId = id; u8* tlvs = pFwReq->ptlv; /* Write the TLVs to the message. */ tlvs = addTlv(NAN_TLV_TYPE_CLUSTER_ID_LOW, sizeof(pReq->cluster_low), (const u8*)&pReq->cluster_low, tlvs); tlvs = addTlv(NAN_TLV_TYPE_CLUSTER_ID_HIGH, sizeof(pReq->cluster_high), (const u8*)&pReq->cluster_high, tlvs); tlvs = addTlv(NAN_TLV_TYPE_MASTER_PREFERENCE, sizeof(pReq->master_pref), (const u8*)&pReq->master_pref, tlvs); if (pReq->config_support_5g) { tlvs = addTlv(NAN_TLV_TYPE_5G_SUPPORT, sizeof(pReq->support_5g_val), (const u8*)&pReq->support_5g_val, tlvs); } if (pReq->config_sid_beacon) { tlvs = addTlv(NAN_TLV_TYPE_SID_BEACON, sizeof(pReq->sid_beacon_val), (const u8*)&pReq->sid_beacon_val, tlvs); } if (pReq->config_2dot4g_rssi_close) { tlvs = addTlv(NAN_TLV_TYPE_24G_RSSI_CLOSE, sizeof(pReq->rssi_close_2dot4g_val), (const u8*)&pReq->rssi_close_2dot4g_val, tlvs); } if (pReq->config_2dot4g_rssi_middle) { tlvs = addTlv(NAN_TLV_TYPE_24G_RSSI_MIDDLE, sizeof(pReq->rssi_middle_2dot4g_val), (const u8*)&pReq->rssi_middle_2dot4g_val, tlvs); } if (pReq->config_hop_count_limit) { tlvs = addTlv(NAN_TLV_TYPE_HOP_COUNT_LIMIT, sizeof(pReq->hop_count_limit_val), (const u8*)&pReq->hop_count_limit_val, tlvs); } if (pReq->config_2dot4g_support) { tlvs = addTlv(NAN_TLV_TYPE_24G_SUPPORT, sizeof(pReq->support_2dot4g_val), (const u8*)&pReq->support_2dot4g_val, tlvs); } if (pReq->config_2dot4g_beacons) { tlvs = addTlv(NAN_TLV_TYPE_24G_BEACON, sizeof(pReq->beacon_2dot4g_val), (const u8*)&pReq->beacon_2dot4g_val, tlvs); } if (pReq->config_2dot4g_sdf) { tlvs = addTlv(NAN_TLV_TYPE_24G_SDF, sizeof(pReq->sdf_2dot4g_val), (const u8*)&pReq->sdf_2dot4g_val, tlvs); } if (pReq->config_5g_beacons) { tlvs = addTlv(NAN_TLV_TYPE_5G_BEACON, sizeof(pReq->beacon_5g_val), (const u8*)&pReq->beacon_5g_val, tlvs); } if (pReq->config_5g_sdf) { tlvs = addTlv(NAN_TLV_TYPE_5G_SDF, sizeof(pReq->sdf_5g_val), (const u8*)&pReq->sdf_5g_val, tlvs); } if (pReq->config_2dot4g_rssi_proximity) { tlvs = addTlv(NAN_TLV_TYPE_24G_RSSI_CLOSE_PROXIMITY, sizeof(pReq->rssi_proximity_2dot4g_val), (const u8*)&pReq->rssi_proximity_2dot4g_val, tlvs); } /* Add the support of sending 5G RSSI values */ if (pReq->config_5g_rssi_close) { tlvs = addTlv(NAN_TLV_TYPE_5G_RSSI_CLOSE, sizeof(pReq->rssi_close_5g_val), (const u8*)&pReq->rssi_close_5g_val, tlvs); } if (pReq->config_5g_rssi_middle) { tlvs = addTlv(NAN_TLV_TYPE_5G_RSSI_MIDDLE, sizeof(pReq->rssi_middle_5g_val), (const u8*)&pReq->rssi_middle_5g_val, tlvs); } if (pReq->config_5g_rssi_close_proximity) { tlvs = addTlv(NAN_TLV_TYPE_5G_RSSI_CLOSE_PROXIMITY, sizeof(pReq->rssi_close_proximity_5g_val), (const u8*)&pReq->rssi_close_proximity_5g_val, tlvs); } if (pReq->config_rssi_window_size) { tlvs = addTlv(NAN_TLV_TYPE_RSSI_AVERAGING_WINDOW_SIZE, sizeof(pReq->rssi_window_size_val), (const u8*)&pReq->rssi_window_size_val, tlvs); } if (pReq->config_oui) { tlvs = addTlv(NAN_TLV_TYPE_CLUSTER_OUI_NETWORK_ID, sizeof(pReq->oui_val), (const u8*)&pReq->oui_val, tlvs); } if (pReq->config_intf_addr) { tlvs = addTlv(NAN_TLV_TYPE_SOURCE_MAC_ADDRESS, sizeof(pReq->intf_addr_val), (const u8*)&pReq->intf_addr_val[0], tlvs); } if (pReq->config_cluster_attribute_val) { tlvs = addTlv(NAN_TLV_TYPE_CLUSTER_ATTRIBUTE_IN_SDF, sizeof(pReq->config_cluster_attribute_val), (const u8*)&pReq->config_cluster_attribute_val, tlvs); } if (pReq->config_scan_params) { u32 socialChannelParamVal[NAN_MAX_SOCIAL_CHANNELS]; /* Fill the social channel param */ fillNanSocialChannelParamVal(&pReq->scan_params_val, socialChannelParamVal); int i; for (i = 0; i < NAN_MAX_SOCIAL_CHANNELS; i++) { tlvs = addTlv(NAN_TLV_TYPE_SOCIAL_CHANNEL_SCAN_PARAMS, sizeof(socialChannelParamVal[i]), (const u8*)&socialChannelParamVal[i], tlvs); } } if (pReq->config_random_factor_force) { tlvs = addTlv(NAN_TLV_TYPE_RANDOM_FACTOR_FORCE, sizeof(pReq->random_factor_force_val), (const u8*)&pReq->random_factor_force_val, tlvs); } if (pReq->config_hop_count_force) { tlvs = addTlv(NAN_TLV_TYPE_HOP_COUNT_FORCE, sizeof(pReq->hop_count_force_val), (const u8*)&pReq->hop_count_force_val, tlvs); } if (pReq->config_24g_channel) { tlvs = addTlv(NAN_TLV_TYPE_24G_CHANNEL, sizeof(u32), (const u8*)&pReq->channel_24g_val, tlvs); } if (pReq->config_5g_channel) { tlvs = addTlv(NAN_TLV_TYPE_5G_CHANNEL, sizeof(u32), (const u8*)&pReq->channel_5g_val, tlvs); } mVendorData = (char*)pFwReq; mDataLen = message_len; //Insert the vendor specific data int ret = mMsg.put_bytes(NL80211_ATTR_VENDOR_DATA, mVendorData, mDataLen); if (ret < 0) { ALOGE("%s: put_bytes Error:%d",__func__, ret); cleanup(); return ret; } hexdump(mVendorData, mDataLen); return ret; } int NanCommand::putNanDisable(transaction_id id) { ALOGV("NAN_DISABLE"); size_t message_len = sizeof(NanDisableReqMsg); pNanDisableReqMsg pFwReq = (pNanDisableReqMsg)malloc(message_len); if (pFwReq == NULL) { cleanup(); return WIFI_ERROR_OUT_OF_MEMORY; } ALOGV("Message Len %zu", message_len); memset (pFwReq, 0, message_len); pFwReq->fwHeader.msgVersion = (u16)NAN_MSG_VERSION1; pFwReq->fwHeader.msgId = NAN_MSG_ID_DISABLE_REQ; pFwReq->fwHeader.msgLen = message_len; pFwReq->fwHeader.transactionId = id; mVendorData = (char*)pFwReq; mDataLen = message_len; int ret = mMsg.put_bytes(NL80211_ATTR_VENDOR_DATA, mVendorData, mDataLen); if (ret < 0) { ALOGE("%s: put_bytes Error:%d",__func__, ret); cleanup(); return ret; } hexdump(mVendorData, mDataLen); return ret; } int NanCommand::putNanConfig(transaction_id id, const NanConfigRequest *pReq) { ALOGV("NAN_CONFIG"); size_t message_len = NAN_MAX_CONFIGURATION_REQ_SIZE; int idx = 0; if (pReq == NULL || pReq->num_config_discovery_attr > NAN_MAX_POSTDISCOVERY_LEN) { cleanup(); return WIFI_ERROR_INVALID_ARGS; } message_len = sizeof(NanMsgHeader); message_len += \ ( pReq->config_sid_beacon ? (SIZEOF_TLV_HDR + \ sizeof(pReq->sid_beacon)) : 0 \ ) + \ ( pReq->config_master_pref ? (SIZEOF_TLV_HDR + \ sizeof(pReq->master_pref)) : 0 \ ) + \ ( pReq->config_rssi_proximity ? (SIZEOF_TLV_HDR + \ sizeof(pReq->rssi_proximity)) : 0 \ ) + \ ( pReq->config_5g_rssi_close_proximity ? (SIZEOF_TLV_HDR + \ sizeof(pReq->rssi_close_proximity_5g_val)) : 0 \ ) + \ ( pReq->config_rssi_window_size ? (SIZEOF_TLV_HDR + \ sizeof(pReq->rssi_window_size_val)) : 0 \ ) + \ ( pReq->config_cluster_attribute_val ? (SIZEOF_TLV_HDR + \ sizeof(pReq->config_cluster_attribute_val)) : 0 \ ) + \ ( pReq->config_scan_params ? (SIZEOF_TLV_HDR + \ NAN_MAX_SOCIAL_CHANNELS * sizeof(u32)) : 0 \ ) + \ ( pReq->config_random_factor_force ? (SIZEOF_TLV_HDR + \ sizeof(pReq->random_factor_force_val)) : 0 \ ) + \ ( pReq->config_hop_count_force ? (SIZEOF_TLV_HDR + \ sizeof(pReq->hop_count_force_val)) : 0 \ ) + \ ( pReq->config_conn_capability ? (SIZEOF_TLV_HDR + \ sizeof(u32)) : 0 \ ); if (pReq->num_config_discovery_attr) { for (idx = 0; idx < pReq->num_config_discovery_attr; idx ++) { message_len += SIZEOF_TLV_HDR +\ calcNanTransmitPostDiscoverySize(&pReq->discovery_attr_val[idx]); } } if (pReq->config_fam && \ calcNanFurtherAvailabilityMapSize(&pReq->fam_val)) { message_len += (SIZEOF_TLV_HDR + \ calcNanFurtherAvailabilityMapSize(&pReq->fam_val)); } pNanConfigurationReqMsg pFwReq = (pNanConfigurationReqMsg)malloc(message_len); if (pFwReq == NULL) { cleanup(); return WIFI_ERROR_OUT_OF_MEMORY; } ALOGV("Message Len %zu", message_len); memset (pFwReq, 0, message_len); pFwReq->fwHeader.msgVersion = (u16)NAN_MSG_VERSION1; pFwReq->fwHeader.msgId = NAN_MSG_ID_CONFIGURATION_REQ; pFwReq->fwHeader.msgLen = message_len; pFwReq->fwHeader.transactionId = id; u8* tlvs = pFwReq->ptlv; if (pReq->config_sid_beacon) { tlvs = addTlv(NAN_TLV_TYPE_SID_BEACON, sizeof(pReq->sid_beacon), (const u8*)&pReq->sid_beacon, tlvs); } if (pReq->config_master_pref) { tlvs = addTlv(NAN_TLV_TYPE_MASTER_PREFERENCE, sizeof(pReq->master_pref), (const u8*)&pReq->master_pref, tlvs); } if (pReq->config_rssi_window_size) { tlvs = addTlv(NAN_TLV_TYPE_RSSI_AVERAGING_WINDOW_SIZE, sizeof(pReq->rssi_window_size_val), (const u8*)&pReq->rssi_window_size_val, tlvs); } if (pReq->config_rssi_proximity) { tlvs = addTlv(NAN_TLV_TYPE_24G_RSSI_CLOSE_PROXIMITY, sizeof(pReq->rssi_proximity), (const u8*)&pReq->rssi_proximity, tlvs); } if (pReq->config_scan_params) { u32 socialChannelParamVal[NAN_MAX_SOCIAL_CHANNELS]; /* Fill the social channel param */ fillNanSocialChannelParamVal(&pReq->scan_params_val, socialChannelParamVal); int i; for (i = 0; i < NAN_MAX_SOCIAL_CHANNELS; i++) { tlvs = addTlv(NAN_TLV_TYPE_SOCIAL_CHANNEL_SCAN_PARAMS, sizeof(socialChannelParamVal[i]), (const u8*)&socialChannelParamVal[i], tlvs); } } if (pReq->config_random_factor_force) { tlvs = addTlv(NAN_TLV_TYPE_RANDOM_FACTOR_FORCE, sizeof(pReq->random_factor_force_val), (const u8*)&pReq->random_factor_force_val, tlvs); } if (pReq->config_hop_count_force) { tlvs = addTlv(NAN_TLV_TYPE_HOP_COUNT_FORCE, sizeof(pReq->hop_count_force_val), (const u8*)&pReq->hop_count_force_val, tlvs); } if (pReq->config_conn_capability) { u32 val = \ getNanTransmitPostConnectivityCapabilityVal(&pReq->conn_capability_val); tlvs = addTlv(NAN_TLV_TYPE_POST_NAN_CONNECTIVITY_CAPABILITIES_TRANSMIT, sizeof(val), (const u8*)&val, tlvs); } if (pReq->num_config_discovery_attr) { for (idx = 0; idx < pReq->num_config_discovery_attr; idx ++) { fillNanTransmitPostDiscoveryVal(&pReq->discovery_attr_val[idx], (u8*)(tlvs + SIZEOF_TLV_HDR)); tlvs = addTlv(NAN_TLV_TYPE_POST_NAN_DISCOVERY_ATTRIBUTE_TRANSMIT, calcNanTransmitPostDiscoverySize( &pReq->discovery_attr_val[idx]), (const u8*)(tlvs + SIZEOF_TLV_HDR), tlvs); } } if (pReq->config_fam && \ calcNanFurtherAvailabilityMapSize(&pReq->fam_val)) { fillNanFurtherAvailabilityMapVal(&pReq->fam_val, (u8*)(tlvs + SIZEOF_TLV_HDR)); tlvs = addTlv(NAN_TLV_TYPE_FURTHER_AVAILABILITY_MAP, calcNanFurtherAvailabilityMapSize(&pReq->fam_val), (const u8*)(tlvs + SIZEOF_TLV_HDR), tlvs); } mVendorData = (char*)pFwReq; mDataLen = message_len; int ret = mMsg.put_bytes(NL80211_ATTR_VENDOR_DATA, mVendorData, mDataLen); if (ret < 0) { ALOGE("%s: put_bytes Error:%d",__func__, ret); cleanup(); return ret; } hexdump(mVendorData, mDataLen); return ret; } int NanCommand::putNanPublish(transaction_id id, const NanPublishRequest *pReq) { ALOGV("NAN_PUBLISH"); if (pReq == NULL) { cleanup(); return WIFI_ERROR_INVALID_ARGS; } size_t message_len = sizeof(NanMsgHeader) + sizeof(NanPublishServiceReqParams) + (pReq->service_name_len ? SIZEOF_TLV_HDR + pReq->service_name_len : 0) + (pReq->service_specific_info_len ? SIZEOF_TLV_HDR + pReq->service_specific_info_len : 0) + (pReq->rx_match_filter_len ? SIZEOF_TLV_HDR + pReq->rx_match_filter_len : 0) + (pReq->tx_match_filter_len ? SIZEOF_TLV_HDR + pReq->tx_match_filter_len : 0); pNanPublishServiceReqMsg pFwReq = (pNanPublishServiceReqMsg)malloc(message_len); if (pFwReq == NULL) { cleanup(); return WIFI_ERROR_OUT_OF_MEMORY; } ALOGV("Message Len %zu", message_len); memset(pFwReq, 0, message_len); pFwReq->fwHeader.msgVersion = (u16)NAN_MSG_VERSION1; pFwReq->fwHeader.msgId = NAN_MSG_ID_PUBLISH_SERVICE_REQ; pFwReq->fwHeader.msgLen = message_len; if (pReq->publish_id == 0) { pFwReq->fwHeader.handle = 0xFFFF; } else { pFwReq->fwHeader.handle = pReq->publish_id; } pFwReq->fwHeader.transactionId = id; pFwReq->publishServiceReqParams.ttl = pReq->ttl; pFwReq->publishServiceReqParams.period = pReq->period; pFwReq->publishServiceReqParams.reserved = 0; pFwReq->publishServiceReqParams.publishType = pReq->publish_type; pFwReq->publishServiceReqParams.txType = pReq->tx_type; pFwReq->publishServiceReqParams.rssiThresholdFlag = pReq->rssi_threshold_flag; pFwReq->publishServiceReqParams.matchAlg = pReq->publish_match_indicator; pFwReq->publishServiceReqParams.count = pReq->publish_count; pFwReq->publishServiceReqParams.connmap = pReq->connmap; pFwReq->publishServiceReqParams.pubTerminatedIndDisableFlag = (pReq->recv_indication_cfg & BIT_0) ? 1 : 0; pFwReq->publishServiceReqParams.pubMatchExpiredIndDisableFlag = (pReq->recv_indication_cfg & BIT_1) ? 1 : 0; pFwReq->publishServiceReqParams.followupRxIndDisableFlag = (pReq->recv_indication_cfg & BIT_2) ? 1 : 0; pFwReq->publishServiceReqParams.reserved2 = 0; u8* tlvs = pFwReq->ptlv; if (pReq->service_name_len) { tlvs = addTlv(NAN_TLV_TYPE_SERVICE_NAME, pReq->service_name_len, (const u8*)&pReq->service_name[0], tlvs); } if (pReq->service_specific_info_len) { tlvs = addTlv(NAN_TLV_TYPE_SERVICE_SPECIFIC_INFO, pReq->service_specific_info_len, (const u8*)&pReq->service_specific_info[0], tlvs); } if (pReq->rx_match_filter_len) { tlvs = addTlv(NAN_TLV_TYPE_RX_MATCH_FILTER, pReq->rx_match_filter_len, (const u8*)&pReq->rx_match_filter[0], tlvs); } if (pReq->tx_match_filter_len) { tlvs = addTlv(NAN_TLV_TYPE_TX_MATCH_FILTER, pReq->tx_match_filter_len, (const u8*)&pReq->tx_match_filter[0], tlvs); } mVendorData = (char *)pFwReq; mDataLen = message_len; int ret = mMsg.put_bytes(NL80211_ATTR_VENDOR_DATA, mVendorData, mDataLen); if (ret < 0) { ALOGE("%s: put_bytes Error:%d",__func__, ret); cleanup(); return ret; } hexdump(mVendorData, mDataLen); return ret; } int NanCommand::putNanPublishCancel(transaction_id id, const NanPublishCancelRequest *pReq) { ALOGV("NAN_PUBLISH_CANCEL"); if (pReq == NULL) { cleanup(); return WIFI_ERROR_INVALID_ARGS; } size_t message_len = sizeof(NanPublishServiceCancelReqMsg); pNanPublishServiceCancelReqMsg pFwReq = (pNanPublishServiceCancelReqMsg)malloc(message_len); if (pFwReq == NULL) { cleanup(); return WIFI_ERROR_OUT_OF_MEMORY; } ALOGV("Message Len %zu", message_len); memset(pFwReq, 0, message_len); pFwReq->fwHeader.msgVersion = (u16)NAN_MSG_VERSION1; pFwReq->fwHeader.msgId = NAN_MSG_ID_PUBLISH_SERVICE_CANCEL_REQ; pFwReq->fwHeader.msgLen = message_len; pFwReq->fwHeader.handle = pReq->publish_id; pFwReq->fwHeader.transactionId = id; mVendorData = (char *)pFwReq; mDataLen = message_len; int ret = mMsg.put_bytes(NL80211_ATTR_VENDOR_DATA, mVendorData, mDataLen); if (ret < 0) { ALOGE("%s: put_bytes Error:%d",__func__, ret); cleanup(); return ret; } hexdump(mVendorData, mDataLen); return ret; } int NanCommand::putNanSubscribe(transaction_id id, const NanSubscribeRequest *pReq) { ALOGV("NAN_SUBSCRIBE"); if (pReq == NULL) { cleanup(); return WIFI_ERROR_INVALID_ARGS; } size_t message_len = sizeof(NanMsgHeader) + sizeof(NanSubscribeServiceReqParams) + (pReq->service_name_len ? SIZEOF_TLV_HDR + pReq->service_name_len : 0) + (pReq->service_specific_info_len ? SIZEOF_TLV_HDR + pReq->service_specific_info_len : 0) + (pReq->rx_match_filter_len ? SIZEOF_TLV_HDR + pReq->rx_match_filter_len : 0) + (pReq->tx_match_filter_len ? SIZEOF_TLV_HDR + pReq->tx_match_filter_len : 0); message_len += \ (pReq->num_intf_addr_present * (SIZEOF_TLV_HDR + NAN_MAC_ADDR_LEN)); pNanSubscribeServiceReqMsg pFwReq = (pNanSubscribeServiceReqMsg)malloc(message_len); if (pFwReq == NULL) { cleanup(); return WIFI_ERROR_OUT_OF_MEMORY; } ALOGV("Message Len %zu", message_len); memset(pFwReq, 0, message_len); pFwReq->fwHeader.msgVersion = (u16)NAN_MSG_VERSION1; pFwReq->fwHeader.msgId = NAN_MSG_ID_SUBSCRIBE_SERVICE_REQ; pFwReq->fwHeader.msgLen = message_len; if (pReq->subscribe_id == 0) { pFwReq->fwHeader.handle = 0xFFFF; } else { pFwReq->fwHeader.handle = pReq->subscribe_id; } pFwReq->fwHeader.transactionId = id; pFwReq->subscribeServiceReqParams.ttl = pReq->ttl; pFwReq->subscribeServiceReqParams.period = pReq->period; pFwReq->subscribeServiceReqParams.subscribeType = pReq->subscribe_type; pFwReq->subscribeServiceReqParams.srfAttr = pReq->serviceResponseFilter; pFwReq->subscribeServiceReqParams.srfInclude = pReq->serviceResponseInclude; pFwReq->subscribeServiceReqParams.srfSend = pReq->useServiceResponseFilter; pFwReq->subscribeServiceReqParams.ssiRequired = pReq->ssiRequiredForMatchIndication; pFwReq->subscribeServiceReqParams.matchAlg = pReq->subscribe_match_indicator; pFwReq->subscribeServiceReqParams.count = pReq->subscribe_count; pFwReq->subscribeServiceReqParams.rssiThresholdFlag = pReq->rssi_threshold_flag; pFwReq->subscribeServiceReqParams.subTerminatedIndDisableFlag = (pReq->recv_indication_cfg & BIT_0) ? 1 : 0; pFwReq->subscribeServiceReqParams.subMatchExpiredIndDisableFlag = (pReq->recv_indication_cfg & BIT_1) ? 1 : 0; pFwReq->subscribeServiceReqParams.followupRxIndDisableFlag = (pReq->recv_indication_cfg & BIT_2) ? 1 : 0; pFwReq->subscribeServiceReqParams.connmap = pReq->connmap; pFwReq->subscribeServiceReqParams.reserved = 0; u8* tlvs = pFwReq->ptlv; if (pReq->service_name_len) { tlvs = addTlv(NAN_TLV_TYPE_SERVICE_NAME, pReq->service_name_len, (const u8*)&pReq->service_name[0], tlvs); } if (pReq->service_specific_info_len) { tlvs = addTlv(NAN_TLV_TYPE_SERVICE_SPECIFIC_INFO, pReq->service_specific_info_len, (const u8*)&pReq->service_specific_info[0], tlvs); } if (pReq->rx_match_filter_len) { tlvs = addTlv(NAN_TLV_TYPE_RX_MATCH_FILTER, pReq->rx_match_filter_len, (const u8*)&pReq->rx_match_filter[0], tlvs); } if (pReq->tx_match_filter_len) { tlvs = addTlv(NAN_TLV_TYPE_TX_MATCH_FILTER, pReq->tx_match_filter_len, (const u8*)&pReq->tx_match_filter[0], tlvs); } int i = 0; for (i = 0; i < pReq->num_intf_addr_present; i++) { tlvs = addTlv(NAN_TLV_TYPE_MAC_ADDRESS, NAN_MAC_ADDR_LEN, (const u8*)&pReq->intf_addr[i][0], tlvs); } mVendorData = (char *)pFwReq; mDataLen = message_len; int ret = mMsg.put_bytes(NL80211_ATTR_VENDOR_DATA, mVendorData, mDataLen); if (ret < 0) { ALOGE("%s: put_bytes Error:%d",__func__, ret); cleanup(); return ret; } hexdump(mVendorData, mDataLen); return ret; } int NanCommand::putNanSubscribeCancel(transaction_id id, const NanSubscribeCancelRequest *pReq) { ALOGV("NAN_SUBSCRIBE_CANCEL"); if (pReq == NULL) { cleanup(); return WIFI_ERROR_INVALID_ARGS; } size_t message_len = sizeof(NanSubscribeServiceCancelReqMsg); pNanSubscribeServiceCancelReqMsg pFwReq = (pNanSubscribeServiceCancelReqMsg)malloc(message_len); if (pFwReq == NULL) { cleanup(); return WIFI_ERROR_OUT_OF_MEMORY; } ALOGV("Message Len %zu", message_len); memset(pFwReq, 0, message_len); pFwReq->fwHeader.msgVersion = (u16)NAN_MSG_VERSION1; pFwReq->fwHeader.msgId = NAN_MSG_ID_SUBSCRIBE_SERVICE_CANCEL_REQ; pFwReq->fwHeader.msgLen = message_len; pFwReq->fwHeader.handle = pReq->subscribe_id; pFwReq->fwHeader.transactionId = id; mVendorData = (char *)pFwReq; mDataLen = message_len; int ret = mMsg.put_bytes(NL80211_ATTR_VENDOR_DATA, mVendorData, mDataLen); if (ret < 0) { ALOGE("%s: put_bytes Error:%d",__func__, ret); cleanup(); return ret; } hexdump(mVendorData, mDataLen); return ret; } int NanCommand::putNanTransmitFollowup(transaction_id id, const NanTransmitFollowupRequest *pReq) { ALOGV("TRANSMIT_FOLLOWUP"); if (pReq == NULL) { cleanup(); return WIFI_ERROR_INVALID_ARGS; } size_t message_len = sizeof(NanMsgHeader) + sizeof(NanTransmitFollowupReqParams) + (pReq->service_specific_info_len ? SIZEOF_TLV_HDR + pReq->service_specific_info_len : 0); /* Mac address needs to be added in TLV */ message_len += (SIZEOF_TLV_HDR + sizeof(pReq->addr)); pNanTransmitFollowupReqMsg pFwReq = (pNanTransmitFollowupReqMsg)malloc(message_len); if (pFwReq == NULL) { cleanup(); return WIFI_ERROR_OUT_OF_MEMORY; } ALOGV("Message Len %zu", message_len); memset (pFwReq, 0, message_len); pFwReq->fwHeader.msgVersion = (u16)NAN_MSG_VERSION1; pFwReq->fwHeader.msgId = NAN_MSG_ID_TRANSMIT_FOLLOWUP_REQ; pFwReq->fwHeader.msgLen = message_len; pFwReq->fwHeader.handle = pReq->publish_subscribe_id; pFwReq->fwHeader.transactionId = id; pFwReq->transmitFollowupReqParams.matchHandle = pReq->requestor_instance_id; if (pReq->priority != NAN_TX_PRIORITY_HIGH) { pFwReq->transmitFollowupReqParams.priority = 1; } else { pFwReq->transmitFollowupReqParams.priority = 2; } pFwReq->transmitFollowupReqParams.window = pReq->dw_or_faw; pFwReq->transmitFollowupReqParams.followupTxRspDisableFlag = (pReq->recv_indication_cfg & BIT_0) ? 1 : 0; pFwReq->transmitFollowupReqParams.reserved = 0; u8* tlvs = pFwReq->ptlv; /* Mac address needs to be added in TLV */ tlvs = addTlv(NAN_TLV_TYPE_MAC_ADDRESS, sizeof(pReq->addr), (const u8*)&pReq->addr[0], tlvs); u16 tlv_type = NAN_TLV_TYPE_SERVICE_SPECIFIC_INFO; if (pReq->service_specific_info_len) { tlvs = addTlv(tlv_type, pReq->service_specific_info_len, (const u8*)&pReq->service_specific_info[0], tlvs); } mVendorData = (char *)pFwReq; mDataLen = message_len; int ret = mMsg.put_bytes(NL80211_ATTR_VENDOR_DATA, mVendorData, mDataLen); if (ret < 0) { ALOGE("%s: put_bytes Error:%d",__func__, ret); cleanup(); return ret; } hexdump(mVendorData, mDataLen); return ret; } int NanCommand::putNanStats(transaction_id id, const NanStatsRequest *pReq) { ALOGV("NAN_STATS"); if (pReq == NULL) { cleanup(); return WIFI_ERROR_INVALID_ARGS; } size_t message_len = sizeof(NanStatsReqMsg); pNanStatsReqMsg pFwReq = (pNanStatsReqMsg)malloc(message_len); if (pFwReq == NULL) { cleanup(); return WIFI_ERROR_OUT_OF_MEMORY; } ALOGV("Message Len %zu", message_len); memset(pFwReq, 0, message_len); pFwReq->fwHeader.msgVersion = (u16)NAN_MSG_VERSION1; pFwReq->fwHeader.msgId = NAN_MSG_ID_STATS_REQ; pFwReq->fwHeader.msgLen = message_len; pFwReq->fwHeader.transactionId = id; pFwReq->statsReqParams.statsType = pReq->stats_type; pFwReq->statsReqParams.clear = pReq->clear; pFwReq->statsReqParams.reserved = 0; mVendorData = (char *)pFwReq; mDataLen = message_len; int ret = mMsg.put_bytes(NL80211_ATTR_VENDOR_DATA, mVendorData, mDataLen); if (ret < 0) { ALOGE("%s: put_bytes Error:%d",__func__, ret); cleanup(); return ret; } hexdump(mVendorData, mDataLen); return ret; } int NanCommand::putNanTCA(transaction_id id, const NanTCARequest *pReq) { ALOGV("NAN_TCA"); if (pReq == NULL) { cleanup(); return WIFI_ERROR_INVALID_ARGS; } size_t message_len = sizeof(NanTcaReqMsg); message_len += (SIZEOF_TLV_HDR + 2 * sizeof(u32)); pNanTcaReqMsg pFwReq = (pNanTcaReqMsg)malloc(message_len); if (pFwReq == NULL) { cleanup(); return WIFI_ERROR_OUT_OF_MEMORY; } ALOGV("Message Len %zu", message_len); memset(pFwReq, 0, message_len); pFwReq->fwHeader.msgVersion = (u16)NAN_MSG_VERSION1; pFwReq->fwHeader.msgId = NAN_MSG_ID_TCA_REQ; pFwReq->fwHeader.msgLen = message_len; pFwReq->fwHeader.transactionId = id; u32 tcaReqParams[2]; memset (tcaReqParams, 0, sizeof(tcaReqParams)); tcaReqParams[0] = (pReq->rising_direction_evt_flag & 0x01); tcaReqParams[0] |= (pReq->falling_direction_evt_flag & 0x01) << 1; tcaReqParams[0] |= (pReq->clear & 0x01) << 2; tcaReqParams[1] = pReq->threshold; u8* tlvs = pFwReq->ptlv; if (pReq->tca_type == NAN_TCA_ID_CLUSTER_SIZE) { tlvs = addTlv(NAN_TLV_TYPE_CLUSTER_SIZE_REQ, sizeof(tcaReqParams), (const u8*)&tcaReqParams[0], tlvs); } else { ALOGE("%s: Unrecognized tca_type:%u", __FUNCTION__, pReq->tca_type); cleanup(); return WIFI_ERROR_INVALID_ARGS; } mVendorData = (char *)pFwReq; mDataLen = message_len; int ret = mMsg.put_bytes(NL80211_ATTR_VENDOR_DATA, mVendorData, mDataLen); if (ret < 0) { ALOGE("%s: put_bytes Error:%d",__func__, ret); cleanup(); return ret; } hexdump(mVendorData, mDataLen); return ret; } int NanCommand::putNanBeaconSdfPayload(transaction_id id, const NanBeaconSdfPayloadRequest *pReq) { ALOGV("NAN_BEACON_SDF_PAYLAOD"); if (pReq == NULL) { cleanup(); return WIFI_ERROR_INVALID_ARGS; } size_t message_len = sizeof(NanMsgHeader) + \ SIZEOF_TLV_HDR + sizeof(u32) + \ pReq->vsa.vsa_len; pNanBeaconSdfPayloadReqMsg pFwReq = (pNanBeaconSdfPayloadReqMsg)malloc(message_len); if (pFwReq == NULL) { cleanup(); return WIFI_ERROR_OUT_OF_MEMORY; } ALOGV("Message Len %zu", message_len); memset(pFwReq, 0, message_len); pFwReq->fwHeader.msgVersion = (u16)NAN_MSG_VERSION1; pFwReq->fwHeader.msgId = NAN_MSG_ID_BEACON_SDF_REQ; pFwReq->fwHeader.msgLen = message_len; pFwReq->fwHeader.transactionId = id; /* Construct First 4 bytes of NanBeaconSdfPayloadReqMsg */ u32 temp = 0; temp = pReq->vsa.payload_transmit_flag & 0x01; temp |= (pReq->vsa.tx_in_discovery_beacon & 0x01) << 1; temp |= (pReq->vsa.tx_in_sync_beacon & 0x01) << 2; temp |= (pReq->vsa.tx_in_service_discovery & 0x01) << 3; temp |= (pReq->vsa.vendor_oui & 0x00FFFFFF) << 8; int tlv_len = sizeof(u32) + pReq->vsa.vsa_len; u8* tempBuf = (u8*)malloc(tlv_len); if (tempBuf == NULL) { ALOGE("%s: Malloc failed", __func__); free(pFwReq); cleanup(); return WIFI_ERROR_OUT_OF_MEMORY; } memset(tempBuf, 0, tlv_len); memcpy(tempBuf, &temp, sizeof(u32)); memcpy((tempBuf + sizeof(u32)), pReq->vsa.vsa, pReq->vsa.vsa_len); u8* tlvs = pFwReq->ptlv; /* Write the TLVs to the message. */ tlvs = addTlv(NAN_TLV_TYPE_VENDOR_SPECIFIC_ATTRIBUTE_TRANSMIT, tlv_len, (const u8*)tempBuf, tlvs); free(tempBuf); mVendorData = (char *)pFwReq; mDataLen = message_len; int ret = mMsg.put_bytes(NL80211_ATTR_VENDOR_DATA, mVendorData, mDataLen); if (ret < 0) { ALOGE("%s: put_bytes Error:%d",__func__, ret); cleanup(); return ret; } hexdump(mVendorData, mDataLen); return ret; } //callback handlers registered for nl message send static int error_handler_nan(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { struct sockaddr_nl * tmp; int *ret = (int *)arg; tmp = nla; *ret = err->error; ALOGE("%s: Error code:%d (%s)", __func__, *ret, strerror(-(*ret))); return NL_STOP; } //callback handlers registered for nl message send static int ack_handler_nan(struct nl_msg *msg, void *arg) { int *ret = (int *)arg; struct nl_msg * a; ALOGE("%s: called", __func__); a = msg; *ret = 0; return NL_STOP; } //callback handlers registered for nl message send static int finish_handler_nan(struct nl_msg *msg, void *arg) { int *ret = (int *)arg; struct nl_msg * a; ALOGE("%s: called", __func__); a = msg; *ret = 0; return NL_SKIP; } //Override base class requestEvent and implement little differently here //This will send the request message //We dont wait for any response back in case of Nan as it is asynchronous //thus no wait for condition. int NanCommand::requestEvent() { int res; struct nl_cb * cb; cb = nl_cb_alloc(NL_CB_DEFAULT); if (!cb) { ALOGE("%s: Callback allocation failed",__func__); res = -1; goto out; } /* send message */ ALOGV("%s:Handle:%p Socket Value:%p", __func__, mInfo, mInfo->cmd_sock); res = nl_send_auto_complete(mInfo->cmd_sock, mMsg.getMessage()); if (res < 0) goto out; res = 1; nl_cb_err(cb, NL_CB_CUSTOM, error_handler_nan, &res); nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler_nan, &res); nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler_nan, &res); // err is populated as part of finish_handler while (res > 0) nl_recvmsgs(mInfo->cmd_sock, cb); out: //free the VendorData if (mVendorData) { free(mVendorData); } mVendorData = NULL; //cleanup the mMsg mMsg.destroy(); return res; } int NanCommand::calcNanTransmitPostDiscoverySize( const NanTransmitPostDiscovery *pPostDiscovery) { /* Fixed size of u32 for Conn Type, Device Role and R flag + Dur + Rsvd*/ int ret = sizeof(u32); /* size of availability interval bit map is 4 bytes */ ret += sizeof(u32); /* size of mac address is 6 bytes*/ ret += (SIZEOF_TLV_HDR + NAN_MAC_ADDR_LEN); if (pPostDiscovery && pPostDiscovery->type == NAN_CONN_WLAN_MESH) { /* size of WLAN_MESH_ID */ ret += (SIZEOF_TLV_HDR + \ pPostDiscovery->mesh_id_len); } if (pPostDiscovery && pPostDiscovery->type == NAN_CONN_WLAN_INFRA) { /* size of Infrastructure ssid */ ret += (SIZEOF_TLV_HDR + \ pPostDiscovery->infrastructure_ssid_len); } ALOGV("%s:size:%d", __func__, ret); return ret; } void NanCommand::fillNanSocialChannelParamVal( const NanSocialChannelScanParams *pScanParams, u32* pChannelParamArr) { int i; if (pChannelParamArr) { memset(pChannelParamArr, 0, NAN_MAX_SOCIAL_CHANNELS * sizeof(u32)); for (i= 0; i < NAN_MAX_SOCIAL_CHANNELS; i++) { pChannelParamArr[i] = pScanParams->scan_period[i] << 16; pChannelParamArr[i] |= pScanParams->dwell_time[i] << 8; } pChannelParamArr[NAN_CHANNEL_24G_BAND] |= 6; pChannelParamArr[NAN_CHANNEL_5G_BAND_LOW]|= 44; pChannelParamArr[NAN_CHANNEL_5G_BAND_HIGH]|= 149; ALOGV("%s: Filled SocialChannelParamVal", __func__); hexdump((char*)pChannelParamArr, NAN_MAX_SOCIAL_CHANNELS * sizeof(u32)); } return; } u32 NanCommand::getNanTransmitPostConnectivityCapabilityVal( const NanTransmitPostConnectivityCapability *pCapab) { u32 ret = 0; ret |= (pCapab->payload_transmit_flag? 1:0) << 16; ret |= (pCapab->is_mesh_supported? 1:0) << 5; ret |= (pCapab->is_ibss_supported? 1:0) << 4; ret |= (pCapab->wlan_infra_field? 1:0) << 3; ret |= (pCapab->is_tdls_supported? 1:0) << 2; ret |= (pCapab->is_wfds_supported? 1:0) << 1; ret |= (pCapab->is_wfd_supported? 1:0); ALOGV("%s: val:%d", __func__, ret); return ret; } void NanCommand::fillNanTransmitPostDiscoveryVal( const NanTransmitPostDiscovery *pTxDisc, u8 *pOutValue) { if (pTxDisc && pOutValue) { u8 *tlvs = &pOutValue[8]; pOutValue[0] = pTxDisc->type; pOutValue[1] = pTxDisc->role; pOutValue[2] = (pTxDisc->transmit_freq? 1:0); pOutValue[2] |= ((pTxDisc->duration & 0x03) << 1); memcpy(&pOutValue[4], &pTxDisc->avail_interval_bitmap, sizeof(pTxDisc->avail_interval_bitmap)); tlvs = addTlv(NAN_TLV_TYPE_MAC_ADDRESS, NAN_MAC_ADDR_LEN, (const u8*)&pTxDisc->addr[0], tlvs); if (pTxDisc->type == NAN_CONN_WLAN_MESH) { tlvs = addTlv(NAN_TLV_TYPE_WLAN_MESH_ID, pTxDisc->mesh_id_len, (const u8*)&pTxDisc->mesh_id[0], tlvs); } if (pTxDisc->type == NAN_CONN_WLAN_INFRA) { tlvs = addTlv(NAN_TLV_TYPE_WLAN_INFRA_SSID, pTxDisc->infrastructure_ssid_len, (const u8*)&pTxDisc->infrastructure_ssid_val[0], tlvs); } ALOGV("%s: Filled TransmitPostDiscoveryVal", __func__); hexdump((char*)pOutValue, calcNanTransmitPostDiscoverySize(pTxDisc)); } return; } void NanCommand::fillNanFurtherAvailabilityMapVal( const NanFurtherAvailabilityMap *pFam, u8 *pOutValue) { int idx = 0; if (pFam && pOutValue) { u32 famsize = calcNanFurtherAvailabilityMapSize(pFam); pNanFurtherAvailabilityMapAttrTlv pFwReq = \ (pNanFurtherAvailabilityMapAttrTlv)pOutValue; memset(pOutValue, 0, famsize); pFwReq->numChan = pFam->numchans; for (idx = 0; idx < pFam->numchans; idx++) { const NanFurtherAvailabilityChannel *pFamChan = \ &pFam->famchan[idx]; pNanFurtherAvailabilityChan pFwFamChan = \ (pNanFurtherAvailabilityChan)((u8*)&pFwReq->pFaChan[0] + \ (idx * sizeof(NanFurtherAvailabilityChan))); pFwFamChan->entryCtrl.availIntDuration = \ pFamChan->entry_control; pFwFamChan->entryCtrl.mapId = \ pFamChan->mapid; pFwFamChan->opClass = pFamChan->class_val; pFwFamChan->channel = pFamChan->channel; memcpy(&pFwFamChan->availIntBitmap, &pFamChan->avail_interval_bitmap, sizeof(pFwFamChan->availIntBitmap)); } ALOGV("%s: Filled FurtherAvailabilityMapVal", __func__); hexdump((char*)pOutValue, famsize); } return; } int NanCommand::calcNanFurtherAvailabilityMapSize( const NanFurtherAvailabilityMap *pFam) { int ret = 0; if (pFam && pFam->numchans && pFam->numchans <= NAN_MAX_FAM_CHANNELS) { /* Fixed size of u8 for numchans*/ ret = sizeof(u8); /* numchans * sizeof(FamChannels) */ ret += (pFam->numchans * sizeof(NanFurtherAvailabilityChan)); } ALOGV("%s:size:%d", __func__, ret); return ret; } int NanCommand::putNanCapabilities(transaction_id id) { size_t message_len = sizeof(NanCapabilitiesReqMsg); pNanCapabilitiesReqMsg pFwReq = (pNanCapabilitiesReqMsg)malloc(message_len); if (pFwReq == NULL) { cleanup(); return WIFI_ERROR_OUT_OF_MEMORY; } memset (pFwReq, 0, message_len); pFwReq->fwHeader.msgVersion = (u16)NAN_MSG_VERSION1; pFwReq->fwHeader.msgId = NAN_MSG_ID_CAPABILITIES_REQ; pFwReq->fwHeader.msgLen = message_len; pFwReq->fwHeader.transactionId = id; mVendorData = (char*)pFwReq; mDataLen = message_len; int ret = mMsg.put_bytes(NL80211_ATTR_VENDOR_DATA, mVendorData, mDataLen); if (ret < 0) { ALOGE("%s: put_bytes Error:%d",__func__, ret); cleanup(); return ret; } hexdump(mVendorData, mDataLen); return ret; }