|
|
|
@ -10537,3 +10537,875 @@ int wlan_hdd_cfg80211_get_roam_stats(struct wiphy *wiphy,
|
|
|
|
|
return errno;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef WLAN_FEATURE_TX_LATENCY_STATS
|
|
|
|
|
#define TX_LATENCY_BUCKET_DISTRIBUTION_LEN \
|
|
|
|
|
(sizeof(uint32_t) * CDP_TX_LATENCY_TYPE_MAX)
|
|
|
|
|
|
|
|
|
|
#define TX_LATENCY_ATTR(_name) QCA_WLAN_VENDOR_ATTR_TX_LATENCY_ ## _name
|
|
|
|
|
|
|
|
|
|
static const struct nla_policy
|
|
|
|
|
tx_latency_bucket_policy[TX_LATENCY_ATTR(BUCKET_MAX) + 1] = {
|
|
|
|
|
[TX_LATENCY_ATTR(BUCKET_TYPE)] = {.type = NLA_U8},
|
|
|
|
|
[TX_LATENCY_ATTR(BUCKET_GRANULARITY)] = {.type = NLA_U32},
|
|
|
|
|
[TX_LATENCY_ATTR(BUCKET_AVERAGE)] = {.type = NLA_U32},
|
|
|
|
|
[TX_LATENCY_ATTR(BUCKET_DISTRIBUTION)] = {
|
|
|
|
|
.type = NLA_BINARY, .len = TX_LATENCY_BUCKET_DISTRIBUTION_LEN},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct nla_policy
|
|
|
|
|
tx_latency_link_policy[TX_LATENCY_ATTR(LINK_MAX) + 1] = {
|
|
|
|
|
[TX_LATENCY_ATTR(LINK_MAC_REMOTE)] = {
|
|
|
|
|
.type = NLA_BINARY, .len = QDF_MAC_ADDR_SIZE},
|
|
|
|
|
[TX_LATENCY_ATTR(LINK_STAT_BUCKETS)] =
|
|
|
|
|
VENDOR_NLA_POLICY_NESTED_ARRAY(tx_latency_bucket_policy),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const struct nla_policy
|
|
|
|
|
tx_latency_policy[TX_LATENCY_ATTR(MAX) + 1] = {
|
|
|
|
|
[TX_LATENCY_ATTR(ACTION)] = {.type = NLA_U32},
|
|
|
|
|
[TX_LATENCY_ATTR(PERIODIC_REPORT)] = {.type = NLA_FLAG},
|
|
|
|
|
[TX_LATENCY_ATTR(PERIOD)] = {.type = NLA_U32 },
|
|
|
|
|
[TX_LATENCY_ATTR(BUCKETS)] =
|
|
|
|
|
VENDOR_NLA_POLICY_NESTED_ARRAY(tx_latency_bucket_policy),
|
|
|
|
|
[TX_LATENCY_ATTR(LINKS)] =
|
|
|
|
|
VENDOR_NLA_POLICY_NESTED_ARRAY(tx_latency_link_policy),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* struct tx_latency_link_node - Link info of remote peer
|
|
|
|
|
* @node: list node for membership in the link list
|
|
|
|
|
* @vdev_id: Unique value to identify VDEV
|
|
|
|
|
* @mac_remote: link MAC address of the remote peer
|
|
|
|
|
*/
|
|
|
|
|
struct tx_latency_link_node {
|
|
|
|
|
qdf_list_node_t node;
|
|
|
|
|
uint8_t vdev_id;
|
|
|
|
|
struct qdf_mac_addr mac_remote;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hdd_tx_latency_set_for_link() - set tx latency stats config for a link
|
|
|
|
|
* @link_info: link specific information
|
|
|
|
|
* @config: pointer to tx latency stats config
|
|
|
|
|
*
|
|
|
|
|
* Return: QDF_STATUS
|
|
|
|
|
*/
|
|
|
|
|
static inline QDF_STATUS
|
|
|
|
|
hdd_tx_latency_set_for_link(struct wlan_hdd_link_info *link_info,
|
|
|
|
|
struct cdp_tx_latency_config *config)
|
|
|
|
|
{
|
|
|
|
|
QDF_STATUS status;
|
|
|
|
|
void *soc = cds_get_context(QDF_MODULE_ID_SOC);
|
|
|
|
|
|
|
|
|
|
if (!soc)
|
|
|
|
|
return QDF_STATUS_E_INVAL;
|
|
|
|
|
|
|
|
|
|
if (wlan_hdd_validate_vdev_id(link_info->vdev_id))
|
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
status = cdp_host_tx_latency_stats_config(soc,
|
|
|
|
|
link_info->vdev_id,
|
|
|
|
|
config);
|
|
|
|
|
if (QDF_IS_STATUS_ERROR(status)) {
|
|
|
|
|
hdd_err_rl("failed to %s for vdev id %d, status %d",
|
|
|
|
|
config->enable ? "enable" : "disable",
|
|
|
|
|
link_info->vdev_id, status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hdd_tx_latency_restore_config() - restore tx latency stats config for a link
|
|
|
|
|
* @link_info: link specific information
|
|
|
|
|
*
|
|
|
|
|
* Return: QDF_STATUS
|
|
|
|
|
*/
|
|
|
|
|
QDF_STATUS
|
|
|
|
|
hdd_tx_latency_restore_config(struct wlan_hdd_link_info *link_info)
|
|
|
|
|
{
|
|
|
|
|
QDF_STATUS status;
|
|
|
|
|
void *soc = cds_get_context(QDF_MODULE_ID_SOC);
|
|
|
|
|
struct cdp_tx_latency_config *config;
|
|
|
|
|
|
|
|
|
|
if (!soc)
|
|
|
|
|
return QDF_STATUS_E_INVAL;
|
|
|
|
|
|
|
|
|
|
if (wlan_hdd_validate_vdev_id(link_info->vdev_id))
|
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
config = &link_info->adapter->tx_latency_cfg;
|
|
|
|
|
status = cdp_host_tx_latency_stats_config(soc,
|
|
|
|
|
link_info->vdev_id,
|
|
|
|
|
config);
|
|
|
|
|
if (QDF_IS_STATUS_ERROR(status)) {
|
|
|
|
|
hdd_err_rl("failed to %s for vdev id %d, status %d",
|
|
|
|
|
config->enable ? "enable" : "disable",
|
|
|
|
|
link_info->vdev_id, status);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hdd_tx_latency_set() - restore tx latency stats config for a link
|
|
|
|
|
* @adapter: pointer to hdd vdev/net_device context
|
|
|
|
|
* @config: pointer to tx latency stats config
|
|
|
|
|
*
|
|
|
|
|
* Return: 0 on success; error number otherwise.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
hdd_tx_latency_set(struct hdd_adapter *adapter,
|
|
|
|
|
struct cdp_tx_latency_config *config)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
struct wlan_hdd_link_info *link_info;
|
|
|
|
|
QDF_STATUS status = QDF_STATUS_E_NOENT;
|
|
|
|
|
|
|
|
|
|
ret = hdd_set_tsf_auto_report(adapter, config->enable,
|
|
|
|
|
HDD_TSF_AUTO_RPT_SOURCE_TX_LATENCY);
|
|
|
|
|
if (ret) {
|
|
|
|
|
hdd_err_rl("failed to %s tsf auto report, ret %d",
|
|
|
|
|
config->enable ? "enable" : "disable", ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hdd_adapter_for_each_link_info(adapter, link_info) {
|
|
|
|
|
status = hdd_tx_latency_set_for_link(link_info, config);
|
|
|
|
|
if (QDF_IS_STATUS_ERROR(status))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* restore TSF auto report config on failure */
|
|
|
|
|
if (QDF_IS_STATUS_ERROR(status))
|
|
|
|
|
hdd_set_tsf_auto_report(adapter, !config->enable,
|
|
|
|
|
HDD_TSF_AUTO_RPT_SOURCE_TX_LATENCY);
|
|
|
|
|
else
|
|
|
|
|
qdf_mem_copy(&adapter->tx_latency_cfg, config,
|
|
|
|
|
sizeof(*config));
|
|
|
|
|
hdd_debug("enable %d status %d", config->enable, status);
|
|
|
|
|
return qdf_status_to_os_return(status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hdd_tx_latency_fill_link_stats() - fill tx latency statistics info skb
|
|
|
|
|
* @skb: skb to be filled
|
|
|
|
|
* @latency: per link tx latency statistics
|
|
|
|
|
* @idx: index of the nested attribute
|
|
|
|
|
*
|
|
|
|
|
* Return: 0 on success; error number otherwise.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
hdd_tx_latency_fill_link_stats(struct sk_buff *skb,
|
|
|
|
|
struct cdp_tx_latency *latency, int idx)
|
|
|
|
|
{
|
|
|
|
|
struct nlattr *link, *link_stat_buckets, *link_stat_bucket;
|
|
|
|
|
uint32_t type;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
link = nla_nest_start(skb, idx);
|
|
|
|
|
if (!link) {
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nla_put(skb, TX_LATENCY_ATTR(LINK_MAC_REMOTE),
|
|
|
|
|
QDF_MAC_ADDR_SIZE, latency->mac_remote.bytes)) {
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hdd_debug_rl("idx %d link mac " QDF_MAC_ADDR_FMT,
|
|
|
|
|
idx, QDF_MAC_ADDR_REF(latency->mac_remote.bytes));
|
|
|
|
|
link_stat_buckets =
|
|
|
|
|
nla_nest_start(skb, TX_LATENCY_ATTR(LINK_STAT_BUCKETS));
|
|
|
|
|
for (type = 0; type < CDP_TX_LATENCY_TYPE_MAX; type++) {
|
|
|
|
|
link_stat_bucket = nla_nest_start(skb, type);
|
|
|
|
|
if (!link_stat_bucket) {
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nla_put_u8(skb, TX_LATENCY_ATTR(BUCKET_TYPE), type)) {
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nla_put_u32(skb, TX_LATENCY_ATTR(BUCKET_GRANULARITY),
|
|
|
|
|
latency->stats[type].granularity)) {
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nla_put_u32(skb, TX_LATENCY_ATTR(BUCKET_AVERAGE),
|
|
|
|
|
latency->stats[type].average)) {
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nla_put(skb, TX_LATENCY_ATTR(BUCKET_DISTRIBUTION),
|
|
|
|
|
TX_LATENCY_BUCKET_DISTRIBUTION_LEN,
|
|
|
|
|
latency->stats[type].distribution)) {
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nla_nest_end(skb, link_stat_bucket);
|
|
|
|
|
hdd_debug_rl(" type %u granularity %u average %u",
|
|
|
|
|
type, latency->stats[type].granularity,
|
|
|
|
|
latency->stats[type].average);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nla_nest_end(skb, link_stat_buckets);
|
|
|
|
|
nla_nest_end(skb, link);
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
if (ret)
|
|
|
|
|
hdd_err("failed for link " QDF_MAC_ADDR_FMT " ret: %d",
|
|
|
|
|
QDF_MAC_ADDR_REF(latency->mac_remote.bytes), ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hdd_tx_latency_get_skb_len() - get required skb length for vendor command
|
|
|
|
|
* response/async event
|
|
|
|
|
* @num: required number of entries
|
|
|
|
|
*
|
|
|
|
|
* Return: the required skb length
|
|
|
|
|
*/
|
|
|
|
|
static uint32_t hdd_tx_latency_get_skb_len(uint32_t num)
|
|
|
|
|
{
|
|
|
|
|
int32_t peer_stat_sz = 0, per_bucket_len = 0, len;
|
|
|
|
|
|
|
|
|
|
if (!num)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_TYPE */
|
|
|
|
|
per_bucket_len += nla_total_size(sizeof(uint8_t));
|
|
|
|
|
/* QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_GRANULARITY */
|
|
|
|
|
per_bucket_len += nla_total_size(sizeof(uint32_t));
|
|
|
|
|
/* QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKET_DISTRIBUTION */
|
|
|
|
|
per_bucket_len += nla_total_size(TX_LATENCY_BUCKET_DISTRIBUTION_LEN);
|
|
|
|
|
/* Nested attr */
|
|
|
|
|
per_bucket_len = nla_total_size(per_bucket_len);
|
|
|
|
|
|
|
|
|
|
/* QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_MAC_REMOTE */
|
|
|
|
|
peer_stat_sz += nla_total_size(QDF_MAC_ADDR_SIZE);
|
|
|
|
|
/* QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINK_STAT_BUCKETS */
|
|
|
|
|
peer_stat_sz +=
|
|
|
|
|
nla_total_size(per_bucket_len * CDP_TX_LATENCY_TYPE_MAX);
|
|
|
|
|
/* Nested attr */
|
|
|
|
|
peer_stat_sz = nla_total_size(peer_stat_sz);
|
|
|
|
|
|
|
|
|
|
/* QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS */
|
|
|
|
|
len = nla_total_size(peer_stat_sz * num);
|
|
|
|
|
len += NLMSG_HDRLEN;
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hdd_tx_latency_link_list_free() - free all the nodes in the list
|
|
|
|
|
* @list: list of the nodes for link info
|
|
|
|
|
*
|
|
|
|
|
* Return: None
|
|
|
|
|
*/
|
|
|
|
|
static void hdd_tx_latency_link_list_free(qdf_list_t *list)
|
|
|
|
|
{
|
|
|
|
|
struct tx_latency_link_node *entry, *next;
|
|
|
|
|
|
|
|
|
|
qdf_list_for_each_del(list, entry, next, node) {
|
|
|
|
|
qdf_list_remove_node(list, &entry->node);
|
|
|
|
|
qdf_mem_free(entry);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hdd_tx_latency_link_list_add() - add a new node to the list for tx latency
|
|
|
|
|
* links
|
|
|
|
|
* @list: list of the nodes for link info
|
|
|
|
|
* @vdev_id: Unique value to identify VDEV
|
|
|
|
|
* @mac: link mac address of the remote peer
|
|
|
|
|
*
|
|
|
|
|
* Return: 0 on success; error number otherwise.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
hdd_tx_latency_link_list_add(qdf_list_t *list, uint8_t vdev_id, uint8_t *mac)
|
|
|
|
|
{
|
|
|
|
|
struct tx_latency_link_node *link;
|
|
|
|
|
|
|
|
|
|
link = (struct tx_latency_link_node *)qdf_mem_malloc(sizeof(*link));
|
|
|
|
|
if (!link)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
qdf_mem_copy(link->mac_remote.bytes, mac, QDF_MAC_ADDR_SIZE);
|
|
|
|
|
link->vdev_id = vdev_id;
|
|
|
|
|
qdf_list_insert_back(list, &link->node);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hdd_tx_latency_get_links_from_attr() - parse information of the links from
|
|
|
|
|
* attribute QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS
|
|
|
|
|
* @adapter: pointer to hdd vdev/net_device context
|
|
|
|
|
* @links_attr: pointer to attribute QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS
|
|
|
|
|
* @list: list of the nodes for link info
|
|
|
|
|
*
|
|
|
|
|
* Return: 0 on success; error number otherwise.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
hdd_tx_latency_get_links_from_attr(struct hdd_adapter *adapter,
|
|
|
|
|
struct nlattr *links_attr,
|
|
|
|
|
qdf_list_t *list)
|
|
|
|
|
{
|
|
|
|
|
struct nlattr *attr, *link_mac_remote_attr;
|
|
|
|
|
struct nlattr *tb[TX_LATENCY_ATTR(LINK_MAX) + 1];
|
|
|
|
|
int ret = 0, rem;
|
|
|
|
|
uint8_t vdev_id, *mac;
|
|
|
|
|
|
|
|
|
|
if (!links_attr || !list)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
/* links for MLO STA are attached to different vdevs */
|
|
|
|
|
vdev_id = (adapter->device_mode == QDF_STA_MODE ?
|
|
|
|
|
CDP_VDEV_ALL : adapter->deflink->vdev_id);
|
|
|
|
|
|
|
|
|
|
nla_for_each_nested(attr, links_attr, rem) {
|
|
|
|
|
ret = wlan_cfg80211_nla_parse(tb, TX_LATENCY_ATTR(LINK_MAX),
|
|
|
|
|
nla_data(attr), nla_len(attr),
|
|
|
|
|
tx_latency_link_policy);
|
|
|
|
|
if (ret) {
|
|
|
|
|
hdd_err("Attribute parse failed, ret %d", ret);
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
link_mac_remote_attr = tb[TX_LATENCY_ATTR(LINK_MAC_REMOTE)];
|
|
|
|
|
if (!link_mac_remote_attr) {
|
|
|
|
|
hdd_err("Missing link mac remote attribute");
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nla_len(link_mac_remote_attr) < QDF_MAC_ADDR_SIZE) {
|
|
|
|
|
hdd_err("Attribute link mac remote is invalid");
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mac = (uint8_t *)nla_data(link_mac_remote_attr);
|
|
|
|
|
ret = hdd_tx_latency_link_list_add(list, vdev_id, mac);
|
|
|
|
|
if (ret)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
if (ret)
|
|
|
|
|
hdd_tx_latency_link_list_free(list);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hdd_tx_latency_get_links_for_sap() - get all the active links for SAP mode
|
|
|
|
|
* @adapter: pointer to hdd vdev/net_device context
|
|
|
|
|
* @list: list of the nodes for link info
|
|
|
|
|
*
|
|
|
|
|
* Return: 0 on success; error number otherwise.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
hdd_tx_latency_get_links_for_sap(struct hdd_adapter *adapter, qdf_list_t *list)
|
|
|
|
|
{
|
|
|
|
|
struct hdd_station_info *sta, *tmp = NULL;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
hdd_for_each_sta_ref_safe(adapter->sta_info_list, sta, tmp,
|
|
|
|
|
STA_INFO_SOFTAP_GET_STA_INFO) {
|
|
|
|
|
if (QDF_IS_ADDR_BROADCAST(sta->sta_mac.bytes)) {
|
|
|
|
|
hdd_put_sta_info_ref(&adapter->sta_info_list,
|
|
|
|
|
&sta, true,
|
|
|
|
|
STA_INFO_SOFTAP_GET_STA_INFO);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = hdd_tx_latency_link_list_add(list,
|
|
|
|
|
adapter->deflink->vdev_id,
|
|
|
|
|
sta->sta_mac.bytes);
|
|
|
|
|
hdd_put_sta_info_ref(&adapter->sta_info_list, &sta, true,
|
|
|
|
|
STA_INFO_SOFTAP_GET_STA_INFO);
|
|
|
|
|
if (ret)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
if (ret)
|
|
|
|
|
hdd_tx_latency_link_list_free(list);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hdd_tx_latency_get_links_for_sta() - get all the active links for station
|
|
|
|
|
* mode
|
|
|
|
|
* @adapter: pointer to hdd vdev/net_device context
|
|
|
|
|
* @list: list of the nodes for link info
|
|
|
|
|
*
|
|
|
|
|
* Return: 0 on success; error number otherwise.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
hdd_tx_latency_get_links_for_sta(struct hdd_adapter *adapter, qdf_list_t *list)
|
|
|
|
|
{
|
|
|
|
|
struct wlan_hdd_link_info *link_info;
|
|
|
|
|
struct hdd_station_ctx *ctx;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
hdd_adapter_for_each_active_link_info(adapter, link_info) {
|
|
|
|
|
if (wlan_hdd_validate_vdev_id(link_info->vdev_id))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info);
|
|
|
|
|
if (!hdd_cm_is_vdev_associated(link_info))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
ret = hdd_tx_latency_link_list_add(list, link_info->vdev_id,
|
|
|
|
|
ctx->conn_info.bssid.bytes);
|
|
|
|
|
if (ret)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
if (ret)
|
|
|
|
|
hdd_tx_latency_link_list_free(list);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hdd_tx_latency_get_links() - get all the active links
|
|
|
|
|
* @adapter: pointer to hdd vdev/net_device context
|
|
|
|
|
* @links_attr: pointer to attribute QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS
|
|
|
|
|
* @list: list of the nodes for link info
|
|
|
|
|
*
|
|
|
|
|
* Return: 0 on success; error number otherwise.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
hdd_tx_latency_get_links(struct hdd_adapter *adapter,
|
|
|
|
|
struct nlattr *links_attr, qdf_list_t *list)
|
|
|
|
|
{
|
|
|
|
|
if (!list)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
if (links_attr)
|
|
|
|
|
return hdd_tx_latency_get_links_from_attr(adapter,
|
|
|
|
|
links_attr, list);
|
|
|
|
|
|
|
|
|
|
if (adapter->device_mode == QDF_SAP_MODE ||
|
|
|
|
|
adapter->device_mode == QDF_P2P_GO_MODE)
|
|
|
|
|
return hdd_tx_latency_get_links_for_sap(adapter, list);
|
|
|
|
|
else if (adapter->device_mode == QDF_STA_MODE ||
|
|
|
|
|
adapter->device_mode == QDF_P2P_CLIENT_MODE)
|
|
|
|
|
return hdd_tx_latency_get_links_for_sta(adapter, list);
|
|
|
|
|
else
|
|
|
|
|
return -ENOTSUPP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hdd_tx_latency_populate_links() - get per link tx latency stats and fill
|
|
|
|
|
* into skb
|
|
|
|
|
* @soc: pointer to soc context
|
|
|
|
|
* @skb: skb for vendor command response/async event
|
|
|
|
|
* @list: list of the nodes for link info
|
|
|
|
|
*
|
|
|
|
|
* Return: 0 on success; error number otherwise.
|
|
|
|
|
*/
|
|
|
|
|
static inline int
|
|
|
|
|
hdd_tx_latency_populate_links(void *soc, struct sk_buff *skb, qdf_list_t *list)
|
|
|
|
|
{
|
|
|
|
|
struct nlattr *links;
|
|
|
|
|
struct tx_latency_link_node *entry, *next;
|
|
|
|
|
struct cdp_tx_latency latency = {0};
|
|
|
|
|
int ret, idx = 0;
|
|
|
|
|
uint8_t *mac;
|
|
|
|
|
QDF_STATUS status;
|
|
|
|
|
|
|
|
|
|
links = nla_nest_start(skb, TX_LATENCY_ATTR(LINKS));
|
|
|
|
|
if (!links)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
qdf_list_for_each_del(list, entry, next, node) {
|
|
|
|
|
qdf_list_remove_node(list, &entry->node);
|
|
|
|
|
mac = entry->mac_remote.bytes;
|
|
|
|
|
status = cdp_host_tx_latency_stats_fetch(soc, entry->vdev_id,
|
|
|
|
|
mac, &latency);
|
|
|
|
|
if (QDF_IS_STATUS_ERROR(status)) {
|
|
|
|
|
qdf_mem_free(entry);
|
|
|
|
|
return qdf_status_to_os_return(status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = hdd_tx_latency_fill_link_stats(skb, &latency, idx);
|
|
|
|
|
qdf_mem_free(entry);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
idx++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nla_nest_end(skb, links);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hdd_tx_latency_get() - get per link tx latency stats
|
|
|
|
|
* @wiphy: pointer to wiphy
|
|
|
|
|
* @adapter: pointer to hdd vdev/net_device context
|
|
|
|
|
* @links_attr: pointer to attribute QCA_WLAN_VENDOR_ATTR_TX_LATENCY_LINKS
|
|
|
|
|
*
|
|
|
|
|
* Return: 0 on success; error number otherwise.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
hdd_tx_latency_get(struct wiphy *wiphy,
|
|
|
|
|
struct hdd_adapter *adapter, struct nlattr *links_attr)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
void *soc = cds_get_context(QDF_MODULE_ID_SOC);
|
|
|
|
|
struct sk_buff *reply_skb = NULL;
|
|
|
|
|
uint32_t skb_len, links_num = 0;
|
|
|
|
|
qdf_list_t links_list;
|
|
|
|
|
|
|
|
|
|
if (!soc)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
qdf_list_create(&links_list, 0);
|
|
|
|
|
ret = hdd_tx_latency_get_links(adapter, links_attr, &links_list);
|
|
|
|
|
if (ret)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
links_num = qdf_list_size(&links_list);
|
|
|
|
|
if (!links_num) {
|
|
|
|
|
hdd_err_rl("no valid peers");
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
skb_len = hdd_tx_latency_get_skb_len(links_num);
|
|
|
|
|
reply_skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(wiphy, skb_len);
|
|
|
|
|
if (!reply_skb) {
|
|
|
|
|
ret = -ENOMEM;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = hdd_tx_latency_populate_links(soc, reply_skb, &links_list);
|
|
|
|
|
if (ret)
|
|
|
|
|
goto free_skb;
|
|
|
|
|
|
|
|
|
|
ret = wlan_cfg80211_vendor_cmd_reply(reply_skb);
|
|
|
|
|
/* skb has been consumed regardless of the return value */
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
free_skb:
|
|
|
|
|
wlan_cfg80211_vendor_free_skb(reply_skb);
|
|
|
|
|
hdd_tx_latency_link_list_free(&links_list);
|
|
|
|
|
out:
|
|
|
|
|
qdf_list_destroy(&links_list);
|
|
|
|
|
hdd_debug_rl("get stats with ret %d", ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hdd_tx_latency_enable() - enable per link tx latency stats
|
|
|
|
|
* @adapter: pointer to hdd vdev/net_device context
|
|
|
|
|
* @period: statistical period for transmit latency
|
|
|
|
|
* @periodic_report: whether driver needs to report transmit latency
|
|
|
|
|
* statistics at the end of each period
|
|
|
|
|
* @buckets_attr: pointer to attribute QCA_WLAN_VENDOR_ATTR_TX_LATENCY_BUCKETS
|
|
|
|
|
*
|
|
|
|
|
* Return: 0 on success; error number otherwise.
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
hdd_tx_latency_enable(struct hdd_adapter *adapter, uint32_t period,
|
|
|
|
|
bool periodic_report, struct nlattr *buckets_attr)
|
|
|
|
|
{
|
|
|
|
|
struct nlattr *tb[TX_LATENCY_ATTR(BUCKET_MAX) + 1];
|
|
|
|
|
struct nlattr *attr, *bucket_type_attr, *bucket_granularity_attr;
|
|
|
|
|
int rem, ret;
|
|
|
|
|
uint8_t bucket_type;
|
|
|
|
|
struct cdp_tx_latency_config config = {0};
|
|
|
|
|
|
|
|
|
|
nla_for_each_nested(attr, buckets_attr, rem) {
|
|
|
|
|
ret = wlan_cfg80211_nla_parse(tb, TX_LATENCY_ATTR(BUCKET_MAX),
|
|
|
|
|
nla_data(attr), nla_len(attr),
|
|
|
|
|
tx_latency_bucket_policy);
|
|
|
|
|
if (ret) {
|
|
|
|
|
hdd_err_rl("Attribute parse failed, ret %d", ret);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bucket_type_attr = tb[TX_LATENCY_ATTR(BUCKET_TYPE)];
|
|
|
|
|
if (!bucket_type_attr) {
|
|
|
|
|
hdd_err_rl("Missing bucket type attribute");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bucket_granularity_attr =
|
|
|
|
|
tb[TX_LATENCY_ATTR(BUCKET_GRANULARITY)];
|
|
|
|
|
if (!bucket_granularity_attr) {
|
|
|
|
|
hdd_err_rl("Missing bucket granularity attribute");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bucket_type = nla_get_u8(bucket_type_attr);
|
|
|
|
|
if (bucket_type >= CDP_TX_LATENCY_TYPE_MAX) {
|
|
|
|
|
hdd_err_rl("Invalid bucket type %u", bucket_type);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
config.granularity[bucket_type] =
|
|
|
|
|
nla_get_u32(bucket_granularity_attr);
|
|
|
|
|
if (!config.granularity[bucket_type]) {
|
|
|
|
|
hdd_err_rl("Invalid granularity for type %d",
|
|
|
|
|
bucket_type);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (rem = 0; rem < CDP_TX_LATENCY_TYPE_MAX; rem++) {
|
|
|
|
|
if (config.granularity[rem])
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
hdd_err_rl("Invalid granularity for type %d", rem);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
config.enable = true;
|
|
|
|
|
config.report = periodic_report;
|
|
|
|
|
config.period = period;
|
|
|
|
|
return hdd_tx_latency_set(adapter, &config);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hdd_tx_latency_disable() - disable per link tx latency stats
|
|
|
|
|
* @adapter: pointer to hdd vdev/net_device context
|
|
|
|
|
*
|
|
|
|
|
* Return: 0 on success; error number otherwise.
|
|
|
|
|
*/
|
|
|
|
|
static int hdd_tx_latency_disable(struct hdd_adapter *adapter)
|
|
|
|
|
{
|
|
|
|
|
struct cdp_tx_latency_config config = {0};
|
|
|
|
|
|
|
|
|
|
return hdd_tx_latency_set(adapter, &config);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* __wlan_hdd_cfg80211_tx_latency - configure/retrieve per-link transmit
|
|
|
|
|
* latency statistics
|
|
|
|
|
* @wiphy: wiphy handle
|
|
|
|
|
* @wdev: wdev handle
|
|
|
|
|
* @data: user layer input
|
|
|
|
|
* @data_len: length of user layer input
|
|
|
|
|
*
|
|
|
|
|
* this function is called in ssr protected environment.
|
|
|
|
|
*
|
|
|
|
|
* return: 0 success, none zero for failure
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
__wlan_hdd_cfg80211_tx_latency(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|
|
|
|
const void *data, int data_len)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
uint32_t action, period;
|
|
|
|
|
struct nlattr *period_attr, *buckets_attr, *links_attr;
|
|
|
|
|
|
|
|
|
|
struct net_device *dev = wdev->netdev;
|
|
|
|
|
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
|
|
|
|
|
struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
|
|
|
|
|
struct nlattr *tb[TX_LATENCY_ATTR(MAX) + 1];
|
|
|
|
|
bool periodic_report;
|
|
|
|
|
|
|
|
|
|
if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
|
|
|
|
|
hdd_warn("command not allowed in ftm mode");
|
|
|
|
|
return -EPERM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = wlan_hdd_validate_context(hdd_ctx);
|
|
|
|
|
if (ret)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
if (wlan_cfg80211_nla_parse(tb, TX_LATENCY_ATTR(MAX),
|
|
|
|
|
data, data_len,
|
|
|
|
|
tx_latency_policy)) {
|
|
|
|
|
hdd_err_rl("invalid attribute");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!tb[TX_LATENCY_ATTR(ACTION)]) {
|
|
|
|
|
hdd_err_rl("no attr action");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
action = nla_get_u32(tb[TX_LATENCY_ATTR(ACTION)]);
|
|
|
|
|
switch (action) {
|
|
|
|
|
case QCA_WLAN_VENDOR_TX_LATENCY_ACTION_DISABLE:
|
|
|
|
|
if (!adapter->tx_latency_cfg.enable) {
|
|
|
|
|
ret = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = hdd_tx_latency_disable(adapter);
|
|
|
|
|
break;
|
|
|
|
|
case QCA_WLAN_VENDOR_TX_LATENCY_ACTION_ENABLE:
|
|
|
|
|
period_attr = tb[TX_LATENCY_ATTR(PERIOD)];
|
|
|
|
|
if (!period_attr) {
|
|
|
|
|
hdd_err_rl("no attr period");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buckets_attr = tb[TX_LATENCY_ATTR(BUCKETS)];
|
|
|
|
|
if (!buckets_attr) {
|
|
|
|
|
hdd_err_rl("no attr buckets");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
period = nla_get_u32(period_attr);
|
|
|
|
|
if (!period) {
|
|
|
|
|
hdd_err_rl("invalid period");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
periodic_report =
|
|
|
|
|
nla_get_flag(tb[TX_LATENCY_ATTR(PERIODIC_REPORT)]);
|
|
|
|
|
ret = hdd_tx_latency_enable(adapter, period,
|
|
|
|
|
periodic_report, buckets_attr);
|
|
|
|
|
break;
|
|
|
|
|
case QCA_WLAN_VENDOR_TX_LATENCY_ACTION_GET:
|
|
|
|
|
if (!adapter->tx_latency_cfg.enable) {
|
|
|
|
|
hdd_err_rl("please enable the feature first");
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
links_attr = tb[TX_LATENCY_ATTR(LINKS)];
|
|
|
|
|
ret = hdd_tx_latency_get(wiphy, adapter, links_attr);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* wlan_hdd_cfg80211_tx_latency - configure/retrieve per-link transmit latency
|
|
|
|
|
* statistics
|
|
|
|
|
* @wiphy: wiphy handle
|
|
|
|
|
* @wdev: wdev handle
|
|
|
|
|
* @data: user layer input
|
|
|
|
|
* @data_len: length of user layer input
|
|
|
|
|
*
|
|
|
|
|
* return: 0 success, einval failure
|
|
|
|
|
*/
|
|
|
|
|
int wlan_hdd_cfg80211_tx_latency(struct wiphy *wiphy,
|
|
|
|
|
struct wireless_dev *wdev,
|
|
|
|
|
const void *data, int data_len)
|
|
|
|
|
{
|
|
|
|
|
int errno;
|
|
|
|
|
struct osif_vdev_sync *vdev_sync;
|
|
|
|
|
|
|
|
|
|
errno = osif_vdev_sync_op_start(wdev->netdev, &vdev_sync);
|
|
|
|
|
if (errno)
|
|
|
|
|
return errno;
|
|
|
|
|
|
|
|
|
|
errno = __wlan_hdd_cfg80211_tx_latency(wiphy, wdev, data, data_len);
|
|
|
|
|
osif_vdev_sync_op_stop(vdev_sync);
|
|
|
|
|
return errno;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hdd_tx_latency_stats_cb() - callback function for transmit latency stats
|
|
|
|
|
* @vdev_id: Unique value to identify VDEV
|
|
|
|
|
* @stats_list: list of the nodes for per-link transmit latency statistics
|
|
|
|
|
*
|
|
|
|
|
* Return: QDF_STATUS
|
|
|
|
|
*/
|
|
|
|
|
static QDF_STATUS
|
|
|
|
|
hdd_tx_latency_stats_cb(uint8_t vdev_id, qdf_list_t *stats_list)
|
|
|
|
|
{
|
|
|
|
|
uint32_t len, stats_cnt;
|
|
|
|
|
struct sk_buff *vendor_event;
|
|
|
|
|
struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
|
|
|
|
|
struct wlan_hdd_link_info *link_info;
|
|
|
|
|
struct cdp_tx_latency *entry, *next;
|
|
|
|
|
struct nlattr *links;
|
|
|
|
|
int ret, idx = 0, flags = cds_get_gfp_flags();
|
|
|
|
|
int event_idx = QCA_NL80211_VENDOR_SUBCMD_TX_LATENCY_INDEX;
|
|
|
|
|
|
|
|
|
|
if (!hdd_ctx) {
|
|
|
|
|
hdd_err("HDD context is NULL");
|
|
|
|
|
return QDF_STATUS_E_FAULT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!stats_list || qdf_list_empty(stats_list)) {
|
|
|
|
|
hdd_err("invalid stats list");
|
|
|
|
|
return QDF_STATUS_E_INVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
link_info = hdd_get_link_info_by_vdev(hdd_ctx, vdev_id);
|
|
|
|
|
if (!link_info) {
|
|
|
|
|
hdd_err("adapter NULL for vdev id %d", vdev_id);
|
|
|
|
|
return QDF_STATUS_E_INVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stats_cnt = qdf_list_size(stats_list);
|
|
|
|
|
len = hdd_tx_latency_get_skb_len(stats_cnt);
|
|
|
|
|
hdd_debug_rl("vdev id %d stats cnt %d", vdev_id, stats_cnt);
|
|
|
|
|
vendor_event =
|
|
|
|
|
wlan_cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
|
|
|
|
|
&link_info->adapter->wdev,
|
|
|
|
|
len, event_idx, flags);
|
|
|
|
|
if (!vendor_event) {
|
|
|
|
|
hdd_err("event alloc failed vdev id %d, len %d",
|
|
|
|
|
vdev_id, len);
|
|
|
|
|
return QDF_STATUS_E_NOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
links = nla_nest_start(vendor_event, TX_LATENCY_ATTR(LINKS));
|
|
|
|
|
if (!links) {
|
|
|
|
|
wlan_cfg80211_vendor_free_skb(vendor_event);
|
|
|
|
|
hdd_err("failed to put peers");
|
|
|
|
|
return QDF_STATUS_E_NOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
qdf_list_for_each_del(stats_list, entry, next, node) {
|
|
|
|
|
qdf_list_remove_node(stats_list, &entry->node);
|
|
|
|
|
ret = hdd_tx_latency_fill_link_stats(vendor_event, entry, idx);
|
|
|
|
|
qdf_mem_free(entry);
|
|
|
|
|
if (ret) {
|
|
|
|
|
hdd_err("failed to populate stats for idx %d", idx);
|
|
|
|
|
wlan_cfg80211_vendor_free_skb(vendor_event);
|
|
|
|
|
return QDF_STATUS_E_NOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idx++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nla_nest_end(vendor_event, links);
|
|
|
|
|
wlan_cfg80211_vendor_event(vendor_event, flags);
|
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hdd_tx_latency_register_cb() - register callback function for transmit
|
|
|
|
|
* latency stats
|
|
|
|
|
* @soc: pointer to soc context
|
|
|
|
|
*
|
|
|
|
|
* Return: QDF_STATUS
|
|
|
|
|
*/
|
|
|
|
|
QDF_STATUS hdd_tx_latency_register_cb(void *soc)
|
|
|
|
|
{
|
|
|
|
|
hdd_debug("Register tx latency callback");
|
|
|
|
|
return cdp_host_tx_latency_stats_register_cb(soc,
|
|
|
|
|
hdd_tx_latency_stats_cb);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|