blob: e05e4073d405f0fddd080d577eec9c225f1310b2 [file] [log] [blame]
/*
* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* Changes from Qualcomm Innovation Center are provided under the following license:
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include "sync.h"
#define LOG_TAG "WifiHAL"
#include <utils/Log.h>
#include "wifi_hal.h"
#include "common.h"
#include "cpp_bindings.h"
#include "wifihal_vendorcommand.h"
#define MAX_INFO 1
//Singleton Static Instance
NUDStatsCommand* NUDStatsCommand::mNUDStatsCommandInstance = NULL;
// This function implements creation of Vendor command
// For NUDStats just call base Vendor command create
wifi_error NUDStatsCommand::create() {
wifi_error ret = mMsg.create(NL80211_CMD_VENDOR, 0, 0);
if (ret != WIFI_SUCCESS) {
return ret;
}
// insert the oui in the msg
ret = mMsg.put_u32(NL80211_ATTR_VENDOR_ID, mVendor_id);
if (ret != WIFI_SUCCESS)
goto out;
// insert the subcmd in the msg
ret = mMsg.put_u32(NL80211_ATTR_VENDOR_SUBCMD, mSubcmd);
if (ret != WIFI_SUCCESS)
goto out;
out:
return ret;
}
NUDStatsCommand::NUDStatsCommand(wifi_handle handle, int id, u32 vendor_id, u32 subcmd)
: WifiVendorCommand(handle, id, vendor_id, subcmd)
{
memset(&mStats, 0,sizeof(nud_stats));
mpktInfo = NULL;
mnumStats = 0;
memset(&mHandler, 0, sizeof(pkt_stats_result_handler));
}
NUDStatsCommand::~NUDStatsCommand()
{
mNUDStatsCommandInstance = NULL;
}
NUDStatsCommand* NUDStatsCommand::instance(wifi_handle handle)
{
if (handle == NULL) {
ALOGE("Interface Handle is invalid");
return NULL;
}
if (mNUDStatsCommandInstance == NULL) {
mNUDStatsCommandInstance = new NUDStatsCommand(handle, 0,
OUI_QCA,
QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET);
return mNUDStatsCommandInstance;
}
else
{
if (handle != getWifiHandle(mNUDStatsCommandInstance->mInfo))
{
/* upper layer must have cleaned up the handle and reinitialized,
so we need to update the same */
ALOGE("Handle different, update the handle");
mNUDStatsCommandInstance->mInfo = (hal_info *)handle;
}
}
return mNUDStatsCommandInstance;
}
void NUDStatsCommand::setSubCmd(u32 subcmd)
{
mSubcmd = subcmd;
}
void NUDStatsCommand::setHandler(pkt_stats_result_handler handler)
{
mHandler = handler;
}
wifi_error NUDStatsCommand::requestResponse()
{
return WifiCommand::requestResponse(mMsg);
}
wifi_error NUDStatsCommand::notifyResponse()
{
wifi_error ret = WIFI_SUCCESS;
if (mHandler.on_pkt_stats_results) {
mHandler.on_pkt_stats_results(&mStats, mnumStats,
mpktInfo);
} else {
ret = WIFI_ERROR_INVALID_ARGS;
}
return ret;
}
int NUDStatsCommand::handleResponse(WifiEvent &reply)
{
wifi_error status = WIFI_ERROR_NONE;
WifiVendorCommand::handleResponse(reply);
// Parse the vendordata and get the attribute
switch(mSubcmd)
{
case QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET:
{
struct nlattr *tb_vendor[QCA_ATTR_NUD_STATS_GET_MAX + 1];
nud_stats *stats = &mStats;
memset(stats, 0, sizeof(nud_stats));
nla_parse(tb_vendor, QCA_ATTR_NUD_STATS_GET_MAX,
(struct nlattr *)mVendorData, mDataLen, NULL);
if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV])
{
ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV"
" not found", __FUNCTION__);
status = WIFI_ERROR_INVALID_ARGS;
goto cleanup;
}
stats->arp_req_count_from_netdev = nla_get_u16(tb_vendor[
QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV]);
if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC])
{
ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC"
" not found", __FUNCTION__);
status = WIFI_ERROR_INVALID_ARGS;
goto cleanup;
}
stats->arp_req_count_to_lower_mac = nla_get_u16(tb_vendor[
QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC]);
if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC])
{
ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC"
" not found", __FUNCTION__);
status = WIFI_ERROR_INVALID_ARGS;
goto cleanup;
}
stats->arp_req_rx_count_by_lower_mac = nla_get_u16(tb_vendor[
QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC]);
if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS])
{
ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS"
" not found", __FUNCTION__);
status = WIFI_ERROR_INVALID_ARGS;
goto cleanup;
}
stats->arp_req_count_tx_success = nla_get_u16(tb_vendor[
QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS]);
if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC])
{
ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC"
" not found", __FUNCTION__);
status = WIFI_ERROR_INVALID_ARGS;
goto cleanup;
}
stats->arp_rsp_rx_count_by_lower_mac = nla_get_u16(tb_vendor[
QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC]);
if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC])
{
ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC"
" not found", __FUNCTION__);
status = WIFI_ERROR_INVALID_ARGS;
goto cleanup;
}
stats->arp_rsp_rx_count_by_upper_mac = nla_get_u16(tb_vendor[
QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC]);
if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV])
{
ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV"
" not found", __FUNCTION__);
status = WIFI_ERROR_INVALID_ARGS;
goto cleanup;
}
stats->arp_rsp_count_to_netdev = nla_get_u16(tb_vendor[
QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV]);
if (!tb_vendor[QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP])
{
ALOGE("%s: QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP"
" not found", __FUNCTION__);
status = WIFI_ERROR_INVALID_ARGS;
goto cleanup;
}
stats->arp_rsp_count_out_of_order_drop = nla_get_u16(tb_vendor[
QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP]);
if (tb_vendor[QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE])
stats->ap_link_active = 1;
if (tb_vendor[QCA_ATTR_NUD_STATS_IS_DAD])
stats->is_duplicate_addr_detection = 1;
ALOGV(" req_from_netdev %d count_to_lower :%d"
" count_by_lower :%d"
" count_tx_succ :%d rsp_count_lower :%d"
" rsp_count_upper :%d rsp_count_netdev :%d"
" out_of_order_drop :%d active_aplink %d"
" DAD %d ",
stats->arp_req_count_from_netdev,
stats->arp_req_count_to_lower_mac,
stats->arp_req_rx_count_by_lower_mac,
stats->arp_req_count_tx_success,
stats->arp_rsp_rx_count_by_lower_mac,
stats->arp_rsp_rx_count_by_upper_mac,
stats->arp_rsp_count_to_netdev,
stats->arp_rsp_count_out_of_order_drop,
stats->ap_link_active,
stats->is_duplicate_addr_detection);
if (tb_vendor[QCA_ATTR_NUD_STATS_DATA_PKT_STATS]) {
mNUDStatsCommandInstance->GetPktInfo(tb_vendor);
}
}
}
cleanup:
if (status == WIFI_ERROR_INVALID_ARGS)
memset(&mStats,0,sizeof(nud_stats));
if(mpktInfo != NULL)
free(mpktInfo);
return status;
}
void NUDStatsCommand::GetPktInfo(struct nlattr **tbvendor)
{
struct nlattr *tb;
int rem;
cmdData *pkt_stats;
char ipv6_address[INET6_ADDRSTRLEN];
cmdData pktstats;
int nbuff = 0;
for (tb = (struct nlattr *) nla_data(tbvendor[QCA_ATTR_NUD_STATS_DATA_PKT_STATS]),
rem = nla_len(tbvendor[QCA_ATTR_NUD_STATS_DATA_PKT_STATS]);
nla_ok(tb, rem); tb = nla_next(tb, &(rem)))
{
struct nlattr *tb2[QCA_ATTR_NUD_DATA_STATS_MAX + 1];
nla_parse(tb2, QCA_ATTR_NUD_DATA_STATS_MAX,
(struct nlattr *) nla_data(tb), nla_len(tb), NULL);
memset(&pktstats, 0, sizeof(cmdData));
if (tb2[QCA_ATTR_NUD_STATS_PKT_TYPE])
{
pktstats.pkt_Type = nla_get_u32(tb2[QCA_ATTR_NUD_STATS_PKT_TYPE]);
}
if (tb2[QCA_ATTR_NUD_STATS_PKT_DNS_DOMAIN_NAME])
{
pktstats.domain_name = nla_get_string(tb2[QCA_ATTR_NUD_STATS_PKT_DNS_DOMAIN_NAME]);
}
if (tb2[QCA_ATTR_NUD_STATS_PKT_SRC_PORT])
{
pktstats.src_port = nla_get_u32(tb2[QCA_ATTR_NUD_STATS_PKT_SRC_PORT]);
}
if (tb2[QCA_ATTR_NUD_STATS_PKT_DEST_PORT])
{
pktstats.dst_port = nla_get_u32(tb2[QCA_ATTR_NUD_STATS_PKT_DEST_PORT]);
}
if (tb2[QCA_ATTR_NUD_STATS_PKT_DEST_IPV4])
{
pktstats.ipv4_addr.s_addr = nla_get_u32(tb2[QCA_ATTR_NUD_STATS_PKT_DEST_IPV4]);
}
if (tb2[QCA_ATTR_NUD_STATS_PKT_DEST_IPV6])
{
memcpy(pktstats.ipv6_addr, nla_data(tb2[QCA_ATTR_NUD_STATS_PKT_DEST_IPV6]),
sizeof(pktstats.ipv6_addr));
}
if (tb2[QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_FROM_NETDEV])
{
pktstats.stats.pkt_req_count_from_netdev = nla_get_u16(tb2[
QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_FROM_NETDEV]);
}
if (tb2[QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TO_LOWER_MAC])
{
pktstats.stats.pkt_req_count_to_lower_mac = nla_get_u16(tb2[
QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TO_LOWER_MAC]);
}
if (tb2[QCA_ATTR_NUD_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC])
{
pktstats.stats.pkt_req_rx_count_by_lower_mac = nla_get_u16(tb2[
QCA_ATTR_NUD_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC]);
}
if (tb2[QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TX_SUCCESS])
{
pktstats.stats.pkt_req_count_tx_success = nla_get_u16(tb2[
QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TX_SUCCESS]);
}
if (tb2[QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC])
{
pktstats.stats.pkt_rsp_rx_count_by_lower_mac = nla_get_u16(tb2[
QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC]);
}
if (tb2[QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC])
{
pktstats.stats.pkt_rsp_rx_count_by_upper_mac = nla_get_u16(tb2[
QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC]);
}
if (tb2[QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_TO_NETDEV])
{
pktstats.stats.pkt_rsp_count_to_netdev = nla_get_u16(tb2[
QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_TO_NETDEV]);
}
if (tb2[QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP])
{
pktstats.stats.pkt_rsp_count_out_of_order_drop = nla_get_u16(tb2[
QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP]);
}
if (inet_ntop(AF_INET6, pktstats.ipv6_addr, ipv6_address,
INET6_ADDRSTRLEN) == NULL) {
ALOGE("%s: failed to convert ipv6 address format", __FUNCTION__);
}
ALOGV(" pkt_type %d domain_name :%s"
" src_port %d dst_port :%d"
" ipv4_address :%x ipv6_address %s"
" req_from_netdev %d count_to_lower :%d"
" count_by_lower :%d"
" count_tx_succ :%d rsp_count_lower :%d"
" rsp_count_upper :%d rsp_count_netdev :%d"
" out_of_order_drop :%d ",
pktstats.pkt_Type, pktstats.domain_name,
pktstats.src_port, pktstats.dst_port,
pktstats.ipv4_addr.s_addr, ipv6_address,
pktstats.stats.pkt_req_count_from_netdev,
pktstats.stats.pkt_req_count_to_lower_mac,
pktstats.stats.pkt_req_rx_count_by_lower_mac,
pktstats.stats.pkt_req_count_tx_success,
pktstats.stats.pkt_rsp_rx_count_by_lower_mac,
pktstats.stats.pkt_rsp_rx_count_by_upper_mac,
pktstats.stats.pkt_rsp_count_to_netdev,
pktstats.stats.pkt_rsp_count_out_of_order_drop);
if (nbuff == 0)
pkt_stats = (cmdData *)malloc(sizeof(cmdData));
else
pkt_stats = (cmdData *)realloc(pkt_stats,sizeof(cmdData) * (nbuff + 1));
mpktInfo = pkt_stats;
if (pkt_stats != NULL) {
memcpy(&pkt_stats[nbuff], &pktstats,sizeof(cmdData));
nbuff++;
mnumStats = nbuff;
}
}
}
void NUDStatsCommand::copyStats(nud_stats *stats, cmdData *pktstats)
{
memcpy(stats, &mStats, sizeof(nud_stats));
pktstats = mpktInfo;
}
wifi_error wifi_set_nud_stats(wifi_interface_handle iface,
u32 gw_addr, cmdData Data)
{
wifi_error ret;
NUDStatsCommand *NUDCommand;
struct nlattr *nl_data,*nl_pktInfo;
interface_info *iinfo = getIfaceInfo(iface);
wifi_handle handle = getWifiHandle(iface);
cmdData mData = Data;
cmdData pktstats = Data;
ALOGV("gw_addr : %x", gw_addr);
NUDCommand = NUDStatsCommand::instance(handle);
if (NUDCommand == NULL) {
ALOGE("%s: Error NUDStatsCommand NULL", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
NUDCommand->setSubCmd(QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET);
/* create the message */
ret = NUDCommand->create();
if (ret != WIFI_SUCCESS)
goto cleanup;
ret = NUDCommand->set_iface_id(iinfo->name);
if (ret != WIFI_SUCCESS)
goto cleanup;
/*add the attributes*/
nl_data = NUDCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
if (!nl_data)
goto cleanup;
/**/
ret = NUDCommand->put_flag(QCA_ATTR_NUD_STATS_SET_START);
ret = NUDCommand->put_u32(QCA_ATTR_NUD_STATS_GW_IPV4, gw_addr);
if (ret != WIFI_SUCCESS)
goto cleanup;
if (mData.pkt_Type) {
/*start the packet info attributes in nested*/
nl_pktInfo = NUDCommand->attr_start(QCA_ATTR_NUD_STATS_SET_DATA_PKT_INFO);
if (!nl_pktInfo)
goto cleanup;
else {
ALOGV(" pkt_type %d domain_name :%s"
" src_port %d dst_port :%d"
" ipv4_address :%x ipv6_address %s",
pktstats.pkt_Type, pktstats.domain_name,
pktstats.src_port, pktstats.dst_port,
pktstats.ipv4_addr.s_addr,pktstats.ipv6_addr);
for (int i=0; i < MAX_INFO ; i++) {
/*add the packet type attributes*/
struct nlattr *tb_tmp;
tb_tmp = NUDCommand->attr_start(i);
ret = NUDCommand->put_u32(QCA_ATTR_NUD_STATS_DATA_PKT_INFO_TYPE,mData.pkt_Type);
if (ret != WIFI_SUCCESS)
goto cleanup;
if (mData.domain_name) {
/*add the domain name attributes*/
ret = NUDCommand->put_string(QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DNS_DOMAIN_NAME,
mData.domain_name);
if (ret != WIFI_SUCCESS)
goto cleanup;
}
/*add the source port attributes*/
ret = NUDCommand->put_u32(QCA_ATTR_NUD_STATS_DATA_PKT_INFO_SRC_PORT,
mData.src_port);
if (ret != WIFI_SUCCESS)
goto cleanup;
/*add the dest port attributes*/
ret = NUDCommand->put_u32(QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_PORT,
mData.dst_port);
if (ret != WIFI_SUCCESS)
goto cleanup;
/*add the ipv4 address attributes*/
ret = NUDCommand->put_u32(QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_IPV4,
mData.ipv4_addr.s_addr);
if (ret != WIFI_SUCCESS)
goto cleanup;
/*add the ipv6 address attributes*/
ret = NUDCommand->put_ipv6_addr(QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_IPV6,
mData.ipv6_addr);
if (ret != WIFI_SUCCESS)
goto cleanup;
NUDCommand->attr_end(tb_tmp);
}
}
NUDCommand->attr_end(nl_pktInfo);
}
NUDCommand->attr_end(nl_data);
ret = NUDCommand->requestResponse();
if (ret != WIFI_SUCCESS) {
ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret);
}
cleanup:
return ret;
}
wifi_error wifi_get_nud_stats(wifi_interface_handle iface,
pkt_stats_result_handler handler)
{
wifi_error ret;
NUDStatsCommand *NUDCommand;
struct nlattr *nl_data;
interface_info *iinfo = getIfaceInfo(iface);
wifi_handle handle = getWifiHandle(iface);
NUDCommand = NUDStatsCommand::instance(handle);
if (NUDCommand == NULL) {
ALOGE("%s: Error NUDStatsCommand NULL", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
NUDCommand->setSubCmd(QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET);
NUDCommand->setHandler(handler);
/* create the message */
ret = NUDCommand->create();
if (ret != WIFI_SUCCESS)
goto cleanup;
ret = NUDCommand->set_iface_id(iinfo->name);
if (ret != WIFI_SUCCESS)
goto cleanup;
/*add the attributes*/
nl_data = NUDCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
if (!nl_data){
ret = WIFI_ERROR_UNKNOWN;
goto cleanup;
}
/**/
NUDCommand->attr_end(nl_data);
ret = NUDCommand->requestResponse();
if (ret != WIFI_SUCCESS) {
ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret);
goto cleanup;
}
ret = NUDCommand->notifyResponse();
if (ret != WIFI_SUCCESS) {
ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret);
goto cleanup;
}
cleanup:
return ret;
}
wifi_error wifi_clear_nud_stats(wifi_interface_handle iface,
cmdData Data)
{
wifi_error ret;
NUDStatsCommand *NUDCommand;
struct nlattr *nl_data,*nl_pktInfo;
interface_info *iinfo = getIfaceInfo(iface);
wifi_handle handle = getWifiHandle(iface);
cmdData mData = Data;
NUDCommand = NUDStatsCommand::instance(handle);
if (NUDCommand == NULL) {
ALOGE("%s: Error NUDStatsCommand NULL", __FUNCTION__);
return WIFI_ERROR_INVALID_ARGS;
}
NUDCommand->setSubCmd(QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET);
/* create the message */
ret = NUDCommand->create();
if (ret != WIFI_SUCCESS)
goto cleanup;
ret = NUDCommand->set_iface_id(iinfo->name);
if (ret != WIFI_SUCCESS)
goto cleanup;
/*add the attributes*/
nl_data = NUDCommand->attr_start(NL80211_ATTR_VENDOR_DATA);
if (!nl_data){
ret = WIFI_ERROR_UNKNOWN;
goto cleanup;
}
if (mData.pkt_Type) {
/*set the packet info attributes in nested*/
nl_pktInfo = NUDCommand->attr_start(QCA_ATTR_NUD_STATS_SET_DATA_PKT_INFO);
if (!nl_pktInfo){
ret = WIFI_ERROR_UNKNOWN;
goto cleanup;
}
else {
ALOGV(" %s: pkt_type %d domain_name :%s"
" src_port %d dst_port :%d"
" ipv4_address :%x ipv6_address %s",
__FUNCTION__,mData.pkt_Type, mData.domain_name,
mData.src_port, mData.dst_port,
mData.ipv4_addr.s_addr,mData.ipv6_addr);
for (int i=0; i < MAX_INFO ; i++) {
/*add the packet type attributes*/
struct nlattr *tb_tmp;
tb_tmp = NUDCommand->attr_start(i);
ret = NUDCommand->put_u32(QCA_ATTR_NUD_STATS_DATA_PKT_INFO_TYPE,mData.pkt_Type);
if (ret != WIFI_SUCCESS)
goto cleanup;
NUDCommand->attr_end(tb_tmp);
}
}
NUDCommand->attr_end(nl_pktInfo);
}
NUDCommand->attr_end(nl_data);
ret = NUDCommand->requestResponse();
if (ret != WIFI_SUCCESS)
ALOGE("%s: requestResponse Error:%d",__FUNCTION__, ret);
cleanup:
return ret;
}