qcacld-3.0: Implement iwpriv cmd to get WLM stats from FW
As per new requirements to debug game latency related issues, implement an iwpriv command which: 1) sends a bitmask to FW's Wireless Latency Manager module (WLM). 2) receives from WLM a measurement header along with measurement data. 3) converts both the header and data to a hexadecimal encoded string. 4) returns the hexadecimal encoded string to userspace Change-Id: Ic79c4b757fe2d4e806306750250e3c102745c486 CRs-Fixed: 2388920
This commit is contained in:
parent
85e1ac663c
commit
f9ba53dbbc
1
Kbuild
1
Kbuild
@ -1991,6 +1991,7 @@ endif
|
||||
cppflags-$(CONFIG_UNIT_TEST) += -DWLAN_UNIT_TEST
|
||||
cppflags-$(CONFIG_WLAN_DEBUG_CRASH_INJECT) += -DCONFIG_WLAN_DEBUG_CRASH_INJECT
|
||||
cppflags-$(CONFIG_FEATURE_UNIT_TEST_SUSPEND) += -DWLAN_SUSPEND_RESUME_TEST
|
||||
cppflags-$(CONFIG_FEATURE_WLM_STATS) += -DFEATURE_WLM_STATS
|
||||
|
||||
ifeq ($(CONFIG_LEAK_DETECTION), y)
|
||||
cppflags-y += \
|
||||
|
@ -668,6 +668,7 @@ endif
|
||||
ifeq ($(CONFIG_UNIT_TEST), y)
|
||||
CONFIG_DSC_TEST := y
|
||||
CONFIG_QDF_TEST := y
|
||||
CONFIG_FEATURE_WLM_STATS := y
|
||||
endif
|
||||
|
||||
# enable unit-test suspend for napier builds
|
||||
|
@ -2685,7 +2685,28 @@
|
||||
#define WLAN_PRIV_SET_NONE_GET_THREE_INT (SIOCIWFIRSTPRIV + 15)
|
||||
#define WE_GET_TSF 1
|
||||
/* (SIOCIWFIRSTPRIV + 16) is currently unused */
|
||||
/* (SIOCIWFIRSTPRIV + 17) is currently unused */
|
||||
|
||||
#ifdef FEATURE_WLM_STATS
|
||||
/*
|
||||
* <ioctl>
|
||||
*
|
||||
* get_wlm_stats - Get stats from FW for game latency
|
||||
*
|
||||
* @INPUT: BITMASK inform of decimal number
|
||||
*
|
||||
* @OUTPUT: HEX string given by FW
|
||||
*
|
||||
* This IOCTL is used to get game latency related STATS from FW
|
||||
*
|
||||
* @E.g.: iwpriv wlan0 get_wlm_stats 1
|
||||
*
|
||||
* Usage: internal
|
||||
*
|
||||
* </ioctl>
|
||||
*/
|
||||
#define WLAN_GET_WLM_STATS (SIOCIWFIRSTPRIV + 17)
|
||||
#endif
|
||||
|
||||
/* (SIOCIWFIRSTPRIV + 19) is currently unused */
|
||||
|
||||
#define WLAN_PRIV_SET_FTIES (SIOCIWFIRSTPRIV + 20)
|
||||
@ -3688,6 +3709,166 @@ static int iw_get_linkspeed(struct net_device *dev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef FEATURE_WLM_STATS
|
||||
static void wlan_get_wlm_stats_cb(void *cookie, const char *data)
|
||||
{
|
||||
struct osif_request *request;
|
||||
char *priv;
|
||||
|
||||
request = osif_request_get(cookie);
|
||||
if (!request) {
|
||||
hdd_err("Obsolete request");
|
||||
return;
|
||||
}
|
||||
priv = osif_request_priv(request);
|
||||
strlcpy(priv, data, WE_MAX_STR_LEN);
|
||||
osif_request_complete(request);
|
||||
osif_request_put(request);
|
||||
}
|
||||
|
||||
static int wlan_get_wlm_stats(struct hdd_adapter *adapter, uint32_t bitmask,
|
||||
char *response)
|
||||
{
|
||||
struct osif_request *request;
|
||||
void *cookie;
|
||||
int errno;
|
||||
char *priv;
|
||||
static const struct osif_request_params params = {
|
||||
.priv_size = WE_MAX_STR_LEN,
|
||||
.timeout_ms = 2000,
|
||||
};
|
||||
|
||||
if (!adapter) {
|
||||
hdd_err("NULL argument");
|
||||
return -EINVAL;
|
||||
}
|
||||
request = osif_request_alloc(¶ms);
|
||||
if (!request) {
|
||||
hdd_err("Request allocation failure");
|
||||
return -ENOMEM;
|
||||
}
|
||||
cookie = osif_request_cookie(request);
|
||||
errno = wma_wlm_stats_req(adapter->session_id, bitmask,
|
||||
params.priv_size,
|
||||
wlan_get_wlm_stats_cb, cookie);
|
||||
if (errno) {
|
||||
hdd_err("Request failed be sent, %d", errno);
|
||||
goto cleanup;
|
||||
}
|
||||
errno = osif_request_wait_for_response(request);
|
||||
if (errno) {
|
||||
hdd_err("Timeout happened, can't complete the req");
|
||||
goto cleanup;
|
||||
}
|
||||
priv = osif_request_priv(request);
|
||||
strlcpy(response, priv, params.priv_size);
|
||||
|
||||
cleanup:
|
||||
osif_request_put(request);
|
||||
|
||||
return errno;
|
||||
}
|
||||
|
||||
/*
|
||||
* Due to a limitation in iwpriv the "get_wlm_stats" ioctl is defined
|
||||
* to take as input a variable-length string as opposed to taking a
|
||||
* single integer "bitmask" value. Hence we must have a buffer large
|
||||
* enough to hold a string representing the largest possible
|
||||
* value. MAX_INT = 2,147,483,647 which can be fit in 10 chars.
|
||||
* Round up to 12 to hold the trailing NUL and be a multiple of 4.
|
||||
*/
|
||||
#define WLM_USER_DATA_SIZE 12
|
||||
|
||||
static int __iw_get_wlm_stats(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
struct iw_point priv_data;
|
||||
char user_data[WLM_USER_DATA_SIZE] = {0};
|
||||
uint32_t bitmask = 0;
|
||||
struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
|
||||
struct hdd_context *hdd_ctx;
|
||||
int errno;
|
||||
|
||||
hdd_enter_dev(dev);
|
||||
|
||||
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
|
||||
errno = wlan_hdd_validate_context(hdd_ctx);
|
||||
if (errno)
|
||||
return errno;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN)) {
|
||||
hdd_err("permission check failed");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since this is GETTER iwpriv ioctl, driver needs to
|
||||
* copy SET data from user space to kernel space.
|
||||
* Helper function to get iwreq_data with compat handling.
|
||||
*/
|
||||
if (hdd_priv_get_data(&priv_data, wrqu))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* priv_data.pointer should be pointing to data given
|
||||
* to iwpriv command.
|
||||
*
|
||||
* For example "iwpriv wlan0 get_wlm_stats 1234"
|
||||
*
|
||||
* priv_data.pointer should be pointing to "1234"
|
||||
* priv_data.length should be zero as this GETTER iwpriv ioctl
|
||||
*/
|
||||
if (!priv_data.pointer) {
|
||||
hdd_err("NULL data pointer");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* ideally driver should have used priv_data.length to copy
|
||||
* data from priv_data.pointer but this iwpriv IOCTL has been
|
||||
* declared as GETTER in nature which makes length field zero
|
||||
* for input arguments but priv_data.pointer still points to
|
||||
* user's input argument (just doesn't pass the length of the
|
||||
* argument)
|
||||
*/
|
||||
if (copy_from_user(user_data, priv_data.pointer,
|
||||
sizeof(user_data) - 1)) {
|
||||
hdd_err("failed to copy data from user buffer");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/*
|
||||
* user data is given in ascii, convert ascii to integer
|
||||
*/
|
||||
if (kstrtou32(user_data, 0, &bitmask)) {
|
||||
hdd_err("failed to parse input %s", user_data);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (wlan_get_wlm_stats(adapter, bitmask, extra)) {
|
||||
hdd_err("returning failure");
|
||||
return -EFAULT;
|
||||
}
|
||||
wrqu->data.length = strlen(extra) + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iw_get_wlm_stats(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
int ret;
|
||||
|
||||
cds_ssr_protect(__func__);
|
||||
ret = __iw_get_wlm_stats(dev, info, wrqu, extra);
|
||||
cds_ssr_unprotect(__func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* FEATURE_WLM_STATS */
|
||||
|
||||
int wlan_hdd_update_phymode(struct hdd_adapter *adapter, int new_phymode)
|
||||
{
|
||||
struct net_device *net = adapter->dev;
|
||||
@ -9601,6 +9782,9 @@ static const iw_handler we_private[] = {
|
||||
[WLAN_PRIV_SET_MCBC_FILTER - SIOCIWFIRSTPRIV] =
|
||||
iw_set_dynamic_mcbc_filter,
|
||||
[WLAN_GET_LINK_SPEED - SIOCIWFIRSTPRIV] = iw_get_linkspeed,
|
||||
#ifdef FEATURE_WLM_STATS
|
||||
[WLAN_GET_WLM_STATS - SIOCIWFIRSTPRIV] = iw_get_wlm_stats,
|
||||
#endif
|
||||
[WLAN_PRIV_SET_TWO_INT_GET_NONE - SIOCIWFIRSTPRIV] =
|
||||
iw_set_two_ints_getnone,
|
||||
[WLAN_SET_DOT11P_CHANNEL_SCHED - SIOCIWFIRSTPRIV] =
|
||||
@ -10672,6 +10856,13 @@ static const struct iw_priv_args we_private_args[] = {
|
||||
IW_PRIV_TYPE_CHAR | 5,
|
||||
"getLinkSpeed"},
|
||||
|
||||
#ifdef FEATURE_WLM_STATS
|
||||
{WLAN_GET_WLM_STATS,
|
||||
IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN,
|
||||
IW_PRIV_TYPE_CHAR | WE_MAX_STR_LEN,
|
||||
"get_wlm_stats"},
|
||||
#endif
|
||||
|
||||
/* handlers for main ioctl */
|
||||
{WLAN_PRIV_SET_TWO_INT_GET_NONE,
|
||||
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2,
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "wlan_objmgr_psoc_obj.h"
|
||||
#include <cdp_txrx_handle.h>
|
||||
#include <wlan_policy_mgr_api.h>
|
||||
#include "wma_api.h"
|
||||
|
||||
/* Platform specific configuration for max. no. of fragments */
|
||||
#define QCA_OL_11AC_TX_MAX_FRAGS 2
|
||||
@ -951,6 +952,20 @@ struct wma_valid_channels {
|
||||
uint8_t channel_list[MAX_NUM_CHAN];
|
||||
};
|
||||
|
||||
#ifdef FEATURE_WLM_STATS
|
||||
/**
|
||||
* struct wma_wlm_stats_data - Data required to be used to send WLM req
|
||||
* @wlm_stats_max_size: Buffer size provided by userspace
|
||||
* @wlm_stats_cookie: Cookie to retrieve WLM req data
|
||||
* @wlm_stats_callback: Callback to be used to send WLM response
|
||||
*/
|
||||
struct wma_wlm_stats_data {
|
||||
uint32_t wlm_stats_max_size;
|
||||
void *wlm_stats_cookie;
|
||||
wma_wlm_stats_cb wlm_stats_callback;
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct t_wma_handle - wma context
|
||||
* @wmi_handle: wmi handle
|
||||
@ -1075,6 +1090,7 @@ struct wma_valid_channels {
|
||||
* @rcpi_enabled: Is RCPI enabled?
|
||||
* @link_stats_results: Structure for handing link stats from firmware
|
||||
* @tx_fail_cnt: Number of TX failures
|
||||
* @wlm_data: Data required for WLM req and resp handling
|
||||
* @he_cap: 802.11ax capabilities
|
||||
* @bandcapability: band capability configured through ini
|
||||
* @tx_bfee_8ss_enabled: Is Tx Beamformee support for 8x8 enabled?
|
||||
@ -1208,6 +1224,9 @@ typedef struct {
|
||||
bool rcpi_enabled;
|
||||
tSirLLStatsResults *link_stats_results;
|
||||
uint64_t tx_fail_cnt;
|
||||
#ifdef FEATURE_WLM_STATS
|
||||
struct wma_wlm_stats_data wlm_data;
|
||||
#endif
|
||||
#ifdef WLAN_FEATURE_11AX
|
||||
struct he_capability he_cap;
|
||||
#endif
|
||||
|
@ -574,4 +574,46 @@ void wma_set_hidden_ssid_restart_in_progress(struct wma_txrx_node *iface,
|
||||
void wma_set_channel_switch_in_progress(struct wma_txrx_node *iface);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef FEATURE_WLM_STATS
|
||||
/**
|
||||
* typedef wma_wlm_stats_cb() - Callback function for WLM stats
|
||||
* @cookie: Cookie provided by client during callback registration
|
||||
* @data: Hex ASCII representation of the WLM stats
|
||||
*/
|
||||
typedef void (*wma_wlm_stats_cb)(void *cookie, const char *data);
|
||||
|
||||
/**
|
||||
* wma_wlm_stats_req() - Send a req to WLAN Latency Manager in FW
|
||||
* @vdev_id: vdev id to be sent to FW's WLM
|
||||
* @bitmask: A bitmask which is requested by user to be sent to FW's WLM
|
||||
* @max_size: Size of user's buffer to store the response
|
||||
* @cb: A callback to be called to once response is available
|
||||
* @cookie: A cookie to be used by callback to retrieve the context of req
|
||||
*
|
||||
* This API is used to send a message to WLAN latency manager component
|
||||
* in FW to retrieve some latency related data and send it to user space.
|
||||
* Driver is just a pass-through for user to interract with FW.
|
||||
*
|
||||
* Return: 0 on success and non-zero for error
|
||||
*/
|
||||
int wma_wlm_stats_req(int vdev_id, uint32_t bitmask, uint32_t max_size,
|
||||
wma_wlm_stats_cb cb, void *cookie);
|
||||
|
||||
/**
|
||||
* wma_wlm_stats_rsp() - Handler to handle the response from FW's WLM component
|
||||
* @wma_ctx: WMA context
|
||||
* @event: WMI TLV event data
|
||||
* @len: WMI TLV length
|
||||
*
|
||||
* This API is registered with WMI component in order to handle the response
|
||||
* coming from FW's WLM correspondence to WLM REQ to FW. This API takes the
|
||||
* data coming as HEX stream and write in to CHAR buffer as HEX CHAR stream
|
||||
* and send this char buffer to user space through callback.
|
||||
*
|
||||
* Return: 0 on success and non-zero for error
|
||||
*/
|
||||
int wma_wlm_stats_rsp(void *wma_ctx, uint8_t *event, uint32_t len);
|
||||
#endif /* FEATURE_WLM_STATS */
|
||||
|
||||
#endif /* WMA_API_H */
|
||||
|
@ -3107,6 +3107,20 @@ static void wma_register_md_events(tp_wma_handle wma_handle)
|
||||
}
|
||||
#endif /* WLAN_FEATURE_MOTION_DETECTION */
|
||||
|
||||
#ifdef FEATURE_WLM_STATS
|
||||
static void wma_register_wlm_stats_events(tp_wma_handle wma_handle)
|
||||
{
|
||||
wmi_unified_register_event_handler(wma_handle->wmi_handle,
|
||||
wmi_wlm_stats_event_id,
|
||||
wma_wlm_stats_rsp,
|
||||
WMA_RX_SERIALIZER_CTX);
|
||||
}
|
||||
#else /* FEATURE_WLM_STATS */
|
||||
static void wma_register_wlm_stats_events(tp_wma_handle wma_handle)
|
||||
{
|
||||
}
|
||||
#endif /* FEATURE_WLM_STATS */
|
||||
|
||||
struct wlan_objmgr_psoc *wma_get_psoc_from_scn_handle(void *scn_handle)
|
||||
{
|
||||
tp_wma_handle wma_handle;
|
||||
@ -3681,6 +3695,7 @@ QDF_STATUS wma_open(struct wlan_objmgr_psoc *psoc,
|
||||
|
||||
wma_register_apf_events(wma_handle);
|
||||
wma_register_md_events(wma_handle);
|
||||
wma_register_wlm_stats_events(wma_handle);
|
||||
|
||||
return QDF_STATUS_SUCCESS;
|
||||
|
||||
|
@ -5021,3 +5021,125 @@ void wma_set_channel_switch_in_progress(struct wma_txrx_node *iface)
|
||||
iface->is_channel_switch = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FEATURE_WLM_STATS
|
||||
int wma_wlm_stats_req(int vdev_id, uint32_t bitmask, uint32_t max_size,
|
||||
wma_wlm_stats_cb cb, void *cookie)
|
||||
{
|
||||
tp_wma_handle wma_handle = cds_get_context(QDF_MODULE_ID_WMA);
|
||||
wmi_unified_t wmi_handle;
|
||||
wmi_buf_t wmi_buf;
|
||||
uint32_t buf_len, tlv_tag, tlv_len;
|
||||
wmi_request_wlm_stats_cmd_fixed_param *cmd;
|
||||
QDF_STATUS status;
|
||||
|
||||
if (!wma_handle) {
|
||||
wma_err("Invalid wma handle");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
wmi_handle = wma_handle->wmi_handle;
|
||||
if (!wmi_handle) {
|
||||
wma_err("Invalid wmi handle for wlm_stats_event_handler");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!wmi_service_enabled(wmi_handle, wmi_service_wlm_stats_support)) {
|
||||
wma_err("Feature not supported by firmware");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
wma_handle->wlm_data.wlm_stats_cookie = cookie;
|
||||
wma_handle->wlm_data.wlm_stats_callback = cb;
|
||||
wma_handle->wlm_data.wlm_stats_max_size = max_size;
|
||||
|
||||
buf_len = sizeof(*cmd);
|
||||
wmi_buf = wmi_buf_alloc(wma_handle->wmi_handle, buf_len);
|
||||
if (!wmi_buf)
|
||||
return -EINVAL;
|
||||
|
||||
cmd = (void *)wmi_buf_data(wmi_buf);
|
||||
|
||||
tlv_tag = WMITLV_TAG_STRUC_wmi_request_wlm_stats_cmd_fixed_param;
|
||||
tlv_len =
|
||||
WMITLV_GET_STRUCT_TLVLEN(wmi_request_wlm_stats_cmd_fixed_param);
|
||||
WMITLV_SET_HDR(&cmd->tlv_header, tlv_tag, tlv_len);
|
||||
|
||||
cmd->vdev_id = vdev_id;
|
||||
cmd->request_bitmask = bitmask;
|
||||
status = wmi_unified_cmd_send(wma_handle->wmi_handle, wmi_buf, buf_len,
|
||||
WMI_REQUEST_WLM_STATS_CMDID);
|
||||
if (QDF_IS_STATUS_ERROR(status)) {
|
||||
wmi_buf_free(wmi_buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* info logging per test team request */
|
||||
wma_info("---->sent request for vdev:%d", vdev_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wma_wlm_stats_rsp(void *wma_ctx, uint8_t *event, uint32_t evt_len)
|
||||
{
|
||||
WMI_WLM_STATS_EVENTID_param_tlvs *param_tlvs;
|
||||
wmi_wlm_stats_event_fixed_param *param;
|
||||
tp_wma_handle wma_handle = wma_ctx;
|
||||
char *data;
|
||||
void *cookie;
|
||||
uint32_t *raw_data;
|
||||
uint32_t len, buffer_size, raw_data_num, i;
|
||||
|
||||
if (!wma_handle) {
|
||||
wma_err("Invalid wma handle");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!wma_handle->wlm_data.wlm_stats_callback) {
|
||||
wma_err("No callback registered");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
param_tlvs = (WMI_WLM_STATS_EVENTID_param_tlvs *)event;
|
||||
param = param_tlvs->fixed_param;
|
||||
if (!param) {
|
||||
wma_err("Fix size param is not present, something is wrong");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* info logging per test team request */
|
||||
wma_info("---->Received response for vdev:%d", param->vdev_id);
|
||||
|
||||
raw_data = param_tlvs->data;
|
||||
raw_data_num = param_tlvs->num_data;
|
||||
|
||||
len = 0;
|
||||
buffer_size = wma_handle->wlm_data.wlm_stats_max_size;
|
||||
data = qdf_mem_malloc(buffer_size);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
len += qdf_scnprintf(data + len, buffer_size - len, "\n%x ",
|
||||
param->request_bitmask);
|
||||
len += qdf_scnprintf(data + len, buffer_size - len, "%x ",
|
||||
param->vdev_id);
|
||||
len += qdf_scnprintf(data + len, buffer_size - len, "%x ",
|
||||
param->timestamp);
|
||||
len += qdf_scnprintf(data + len, buffer_size - len, "%x ",
|
||||
param->req_interval);
|
||||
if (!raw_data)
|
||||
goto send_data;
|
||||
|
||||
len += qdf_scnprintf(data + len, buffer_size - len, "\ndata:\n");
|
||||
|
||||
for (i = 0; i < raw_data_num; i++)
|
||||
len += qdf_scnprintf(data + len, buffer_size - len, "%x ",
|
||||
*raw_data++);
|
||||
|
||||
send_data:
|
||||
cookie = wma_handle->wlm_data.wlm_stats_cookie;
|
||||
wma_handle->wlm_data.wlm_stats_callback(cookie, data);
|
||||
|
||||
qdf_mem_free(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* FEATURE_WLM_STATS */
|
||||
|
Loading…
Reference in New Issue
Block a user