qcacld-3.0: Add support for get and set OEM capability

Add support for get and set OEM capability over private netlink
socket.

Change-Id: I8c4a8c19633845750ec9d2492632471c68ba410d
CRs-Fixed: 949469
This commit is contained in:
Krishna Kumaar Natarajan 2015-12-04 14:01:46 -08:00 • committed by Akash Patel
commit 53ca2905f8
6 changed files with 354 additions and 65 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014-2015 The Linux Foundation. All rights reserved.
* Copyright (c) 2014-2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@ -74,6 +74,9 @@ static inline int __cdf_status_to_os_return(CDF_STATUS status)
return -EALREADY;
case CDF_STATUS_E_NOMEM:
return -ENOMEM;
case CDF_STATUS_E_FAILURE:
case CDF_STATUS_E_INVAL:
return -EINVAL;
default:
return -EPERM;
}

View file

@ -154,6 +154,26 @@ typedef struct cdf_packed {
tHddChannelInfo peer_chan_info;
} tPeerStatusInfo;
/**
* enum oem_capability_mask - mask field for userspace client capabilities
* @OEM_CAP_RM_FTMRR: FTM range report mask bit
* @OEM_CAP_RM_LCI: LCI capability mask bit
*/
enum oem_capability_mask {
OEM_CAP_RM_FTMRR = (1 << (0)),
OEM_CAP_RM_LCI = (1 << (1)),
};
/**
* struct oem_get_capability_rsp - capabilites set by userspace and target.
* @target_cap: target capabilities
* @client_capabilities: capabilities set by userspace via set request
*/
struct oem_get_capability_rsp {
t_iw_oem_data_cap target_cap;
struct sme_oem_capability cap;
};
void hdd_send_peer_status_ind_to_oem_app(struct cdf_mac_addr *peerMac,
uint8_t peerStatus,
uint8_t peerTimingMeasCap,

View file

@ -44,9 +44,81 @@
#include "qwlan_version.h"
#include "cds_utils.h"
#include "wma.h"
#include "sme_api.h"
static struct hdd_context_s *p_hdd_ctx;
/**
* populate_oem_data_cap() - populate oem capabilities
* @adapter: device adapter
* @data_cap: pointer to populate the capabilities
*
* Return: error code
*/
static int populate_oem_data_cap(hdd_adapter_t *adapter,
t_iw_oem_data_cap *data_cap)
{
CDF_STATUS status = CDF_STATUS_E_FAILURE;
struct hdd_config *config;
uint32_t num_chan;
uint8_t *chan_list;
hdd_context_t *hdd_ctx = adapter->pHddCtx;
config = hdd_ctx->config;
if (!config) {
hdd_err("HDD configuration is null");
return -EINVAL;
}
chan_list = cdf_mem_malloc(sizeof(uint8_t) * OEM_CAP_MAX_NUM_CHANNELS);
if (NULL == chan_list) {
hdd_err("Memory allocation failed");
return -ENOMEM;
}
strlcpy(data_cap->oem_target_signature, OEM_TARGET_SIGNATURE,
OEM_TARGET_SIGNATURE_LEN);
data_cap->oem_target_type = hdd_ctx->target_type;
data_cap->oem_fw_version = hdd_ctx->target_fw_version;
data_cap->driver_version.major = QWLAN_VERSION_MAJOR;
data_cap->driver_version.minor = QWLAN_VERSION_MINOR;
data_cap->driver_version.patch = QWLAN_VERSION_PATCH;
data_cap->driver_version.build = QWLAN_VERSION_BUILD;
data_cap->allowed_dwell_time_min = config->nNeighborScanMinChanTime;
data_cap->allowed_dwell_time_max = config->nNeighborScanMaxChanTime;
data_cap->curr_dwell_time_min =
sme_get_neighbor_scan_min_chan_time(hdd_ctx->hHal,
adapter->sessionId);
data_cap->curr_dwell_time_max =
sme_get_neighbor_scan_max_chan_time(hdd_ctx->hHal,
adapter->sessionId);
data_cap->supported_bands = config->nBandCapability;
/* request for max num of channels */
num_chan = WNI_CFG_VALID_CHANNEL_LIST_LEN;
status = sme_get_cfg_valid_channels(hdd_ctx->hHal,
&chan_list[0], &num_chan);
if (CDF_STATUS_SUCCESS != status) {
hdd_err("failed to get valid channel list, status: %d", status);
cdf_mem_free(chan_list);
return -EINVAL;
}
/* make sure num channels is not more than chan list array */
if (num_chan > OEM_CAP_MAX_NUM_CHANNELS) {
hdd_err("Num of channels-%d > length-%d of chan_list",
num_chan, OEM_CAP_MAX_NUM_CHANNELS);
cdf_mem_free(chan_list);
return -ENOMEM;
}
data_cap->num_channels = num_chan;
cdf_mem_copy(data_cap->channel_list, chan_list,
sizeof(uint8_t) * num_chan);
cdf_mem_free(chan_list);
return 0;
}
/**
* iw_get_oem_data_cap() - Get OEM Data Capabilities
* @dev: net device upon which the request was received
@ -63,15 +135,11 @@ int iw_get_oem_data_cap(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
CDF_STATUS status;
t_iw_oem_data_cap oemDataCap;
int status;
t_iw_oem_data_cap oemDataCap = { {0} };
t_iw_oem_data_cap *pHddOemDataCap;
hdd_adapter_t *pAdapter = (netdev_priv(dev));
hdd_context_t *pHddContext;
struct hdd_config *pConfig;
uint32_t numChannels;
uint8_t chanList[OEM_CAP_MAX_NUM_CHANNELS];
uint32_t i;
int ret;
ENTER();
@ -81,65 +149,12 @@ int iw_get_oem_data_cap(struct net_device *dev,
if (0 != ret)
return ret;
pConfig = pHddContext->config;
if (!pConfig) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s:HDD configuration is null", __func__);
return -ENOENT;
}
status = populate_oem_data_cap(pAdapter, &oemDataCap);
if (!status)
return status;
do {
cdf_mem_zero(&oemDataCap, sizeof(oemDataCap));
strlcpy(oemDataCap.oem_target_signature, OEM_TARGET_SIGNATURE,
OEM_TARGET_SIGNATURE_LEN);
oemDataCap.oem_target_type = pHddContext->target_type;
oemDataCap.oem_fw_version = pHddContext->target_fw_version;
oemDataCap.driver_version.major = QWLAN_VERSION_MAJOR;
oemDataCap.driver_version.minor = QWLAN_VERSION_MINOR;
oemDataCap.driver_version.patch = QWLAN_VERSION_PATCH;
oemDataCap.driver_version.build = QWLAN_VERSION_BUILD;
oemDataCap.allowed_dwell_time_min =
pConfig->nNeighborScanMinChanTime;
oemDataCap.allowed_dwell_time_max =
pConfig->nNeighborScanMaxChanTime;
oemDataCap.curr_dwell_time_min =
sme_get_neighbor_scan_min_chan_time(pHddContext->hHal,
pAdapter->sessionId);
oemDataCap.curr_dwell_time_max =
sme_get_neighbor_scan_max_chan_time(pHddContext->hHal,
pAdapter->sessionId);
oemDataCap.supported_bands = pConfig->nBandCapability;
/* request for max num of channels */
numChannels = WNI_CFG_VALID_CHANNEL_LIST_LEN;
status = sme_get_cfg_valid_channels(pHddContext->hHal,
&chanList[0], &numChannels);
if (CDF_STATUS_SUCCESS != status) {
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s:failed to get valid channel list",
__func__);
return -ENOENT;
} else {
/* make sure num channels is not more than chan list array */
if (numChannels > OEM_CAP_MAX_NUM_CHANNELS) {
CDF_TRACE(CDF_MODULE_ID_HDD,
CDF_TRACE_LEVEL_ERROR,
"%s:Num of channels(%d) more than length(%d) of chanlist",
__func__, numChannels,
OEM_CAP_MAX_NUM_CHANNELS);
return -ENOMEM;
}
oemDataCap.num_channels = numChannels;
for (i = 0; i < numChannels; i++) {
oemDataCap.channel_list[i] = chanList[i];
}
}
pHddOemDataCap = (t_iw_oem_data_cap *) (extra);
cdf_mem_copy(pHddOemDataCap, &oemDataCap,
sizeof(*pHddOemDataCap));
} while (0);
pHddOemDataCap = (t_iw_oem_data_cap *) (extra);
*pHddOemDataCap = oemDataCap;
EXIT();
return 0;
@ -510,6 +525,132 @@ static int oem_process_channel_info_req_msg(int numOfChannels, char *chanList)
return 0;
}
/**
* oem_process_set_cap_req_msg() - process oem set capability request
* @oem_cap_len: Length of OEM capability
* @oem_cap: Pointer to OEM capability buffer
* @app_pid: process ID, to which rsp message is to be sent
*
* This function sends oem message to SME
*
* Return: error code
*/
static int oem_process_set_cap_req_msg(int oem_cap_len,
char *oem_cap, int32_t app_pid)
{
CDF_STATUS status;
int error_code;
struct sk_buff *skb;
struct nlmsghdr *nlh;
tAniMsgHdr *ani_hdr;
uint8_t *buf;
if (!oem_cap) {
hdd_err("oem_cap is null");
return -EINVAL;
}
status = sme_oem_update_capability(p_hdd_ctx->hHal,
(struct sme_oem_capability *)oem_cap);
if (!CDF_IS_STATUS_SUCCESS(status))
hdd_err("error updating rm capability, status: %d", status);
error_code = cdf_status_to_os_return(status);
skb = alloc_skb(NLMSG_SPACE(WLAN_NL_MAX_PAYLOAD), GFP_KERNEL);
if (skb == NULL) {
hdd_err("alloc_skb failed");
return -ENOMEM;
}
nlh = (struct nlmsghdr *)skb->data;
nlh->nlmsg_pid = 0; /* from kernel */
nlh->nlmsg_flags = 0;
nlh->nlmsg_seq = 0;
nlh->nlmsg_type = WLAN_NL_MSG_OEM;
ani_hdr = NLMSG_DATA(nlh);
ani_hdr->type = ANI_MSG_SET_OEM_CAP_RSP;
/* 64 bit alignment */
ani_hdr->length = sizeof(error_code);
nlh->nlmsg_len = NLMSG_LENGTH(sizeof(tAniMsgHdr) + ani_hdr->length);
/* message body will contain only status code */
buf = (char *)((char *)ani_hdr + sizeof(tAniMsgHdr));
cdf_mem_copy(buf, &error_code, ani_hdr->length);
skb_put(skb, NLMSG_SPACE(sizeof(tAniMsgHdr) + ani_hdr->length));
hdd_info("sending oem response to process pid %d", app_pid);
(void)nl_srv_ucast(skb, app_pid, MSG_DONTWAIT);
return error_code;
}
/**
* oem_process_get_cap_req_msg() - process oem get capability request
*
* This function process the get capability request from OEM and responds
* with the capability.
*
* Return: error code
*/
static int oem_process_get_cap_req_msg(void)
{
int error_code;
struct oem_get_capability_rsp *cap_rsp;
t_iw_oem_data_cap data_cap = { {0} };
struct sme_oem_capability oem_cap;
hdd_adapter_t *adapter;
struct sk_buff *skb;
struct nlmsghdr *nlh;
tAniMsgHdr *ani_hdr;
uint8_t *buf;
/* for now, STA interface only */
adapter = hdd_get_adapter(p_hdd_ctx, WLAN_HDD_INFRA_STATION);
if (!adapter) {
hdd_err("No adapter for STA mode");
return -EINVAL;
}
error_code = populate_oem_data_cap(adapter, &data_cap);
if (0 != error_code)
return error_code;
skb = alloc_skb(NLMSG_SPACE(sizeof(tAniMsgHdr) + sizeof(*cap_rsp)),
GFP_KERNEL);
if (skb == NULL) {
hdd_err("alloc_skb failed");
return -ENOMEM;
}
nlh = (struct nlmsghdr *)skb->data;
nlh->nlmsg_pid = 0; /* from kernel */
nlh->nlmsg_flags = 0;
nlh->nlmsg_seq = 0;
nlh->nlmsg_type = WLAN_NL_MSG_OEM;
ani_hdr = NLMSG_DATA(nlh);
ani_hdr->type = ANI_MSG_GET_OEM_CAP_RSP;
ani_hdr->length = sizeof(*cap_rsp);
nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr) + ani_hdr->length));
buf = (char *)((char *)ani_hdr + sizeof(tAniMsgHdr));
cdf_mem_copy(buf, &data_cap, sizeof(data_cap));
buf = (char *) buf + sizeof(data_cap);
cdf_mem_zero(&oem_cap, sizeof(oem_cap));
sme_oem_get_capability(p_hdd_ctx->hHal, &oem_cap);
cdf_mem_copy(buf, &oem_cap, sizeof(oem_cap));
skb_put(skb, NLMSG_SPACE((sizeof(tAniMsgHdr) + ani_hdr->length)));
hdd_info("send rsp to oem-pid:%d for get_capability",
p_hdd_ctx->oem_pid);
(void)nl_srv_ucast(skb, p_hdd_ctx->oem_pid, MSG_DONTWAIT);
return 0;
}
/**
* hdd_send_peer_status_ind_to_oem_app() -
* Function to send peer status to a registered application
@ -768,6 +909,54 @@ static int oem_msg_callback(struct sk_buff *skb)
sizeof(tAniMsgHdr)));
break;
case ANI_MSG_SET_OEM_CAP_REQ:
hdd_info("Received set oem capability req of length:%d from pid: %d",
msg_hdr->length, nlh->nlmsg_pid);
if ((!p_hdd_ctx->oem_app_registered) ||
(nlh->nlmsg_pid != p_hdd_ctx->oem_pid)) {
/* oem app is not registered yet or pid is different */
hdd_err("set_oem_capability : app not registered(%d) or incorrect pid(%d)",
p_hdd_ctx->oem_app_registered,
nlh->nlmsg_pid);
send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
OEM_ERR_APP_NOT_REGISTERED);
return -EPERM;
}
if ((!msg_hdr->length) ||
(sizeof(struct sme_oem_capability) < msg_hdr->length)) {
hdd_err("Invalid length (%d) in set_oem_capability",
msg_hdr->length);
send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
OEM_ERR_INVALID_MESSAGE_LENGTH);
return -EPERM;
}
oem_process_set_cap_req_msg(msg_hdr->length,
(char *)((char *)msg_hdr +
sizeof(tAniMsgHdr)),
nlh->nlmsg_pid);
break;
case ANI_MSG_GET_OEM_CAP_REQ:
hdd_info("Rcvd get oem capability req of length:%d from pid: %d",
msg_hdr->length, nlh->nlmsg_pid);
if ((!p_hdd_ctx->oem_app_registered) ||
(nlh->nlmsg_pid != p_hdd_ctx->oem_pid)) {
/* oem app is not registered yet or pid is different */
hdd_err("get_oem_capability : app not registered(%d) or incorrect pid(%d)",
p_hdd_ctx->oem_app_registered,
nlh->nlmsg_pid);
send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
OEM_ERR_APP_NOT_REGISTERED);
return -EPERM;
}
oem_process_get_cap_req_msg();
break;
default:
CDF_TRACE(CDF_MODULE_ID_HDD, CDF_TRACE_LEVEL_ERROR,
"%s: Received Invalid message type (%d), length (%d)",

View file

@ -187,6 +187,21 @@ typedef enum {
SME_BI_DIR = 2,
} sme_tspec_dir_type;
/**
* struct sme_oem_capability - OEM capability to be exchanged between host
* and userspace
* @ftm_rr: FTM range report capability bit
* @lci_capability: LCI capability bit
* @reserved1: reserved
* @reserved2: reserved
*/
struct sme_oem_capability {
uint32_t ftm_rr:1;
uint32_t lci_capability:1;
uint32_t reserved1:30;
uint32_t reserved2;
};
/*-------------------------------------------------------------------------
Function declarations and documenation
------------------------------------------------------------------------*/
@ -442,6 +457,10 @@ CDF_STATUS sme_oem_data_req(tHalHandle hHal,
uint8_t sessionId,
tOemDataReqConfig *,
uint32_t *pOemDataReqID);
CDF_STATUS sme_oem_update_capability(tHalHandle hHal,
struct sme_oem_capability *cap);
CDF_STATUS sme_oem_get_capability(tHalHandle hHal,
struct sme_oem_capability *cap);
#endif /*FEATURE_OEM_DATA_SUPPORT */
CDF_STATUS sme_roam_update_apwpsie(tHalHandle, uint8_t sessionId,
tSirAPWPSIEs * pAPWPSIES);

View file

@ -4401,6 +4401,60 @@ CDF_STATUS sme_register_oem_data_rsp_callback(tHalHandle h_hal,
}
#endif
/**
* sme_oem_update_capability() - update UMAC's oem related capability.
* @hal: Handle returned by mac_open
* @oem_cap: pointer to oem_capability
*
* This function updates OEM capability to UMAC. Currently RTT
* related capabilities are updated. More capabilities can be
* added in future.
*
* Return: CDF_STATUS
*/
CDF_STATUS sme_oem_update_capability(tHalHandle hal,
struct sme_oem_capability *cap)
{
CDF_STATUS status = CDF_STATUS_SUCCESS;
tpAniSirGlobal pmac = PMAC_STRUCT(hal);
uint8_t *bytes;
bytes = pmac->rrm.rrmSmeContext.rrmConfig.rm_capability;
if (cap->ftm_rr)
bytes[4] |= RM_CAP_FTM_RANGE_REPORT;
if (cap->lci_capability)
bytes[4] |= RM_CAP_CIVIC_LOC_MEASUREMENT;
return status;
}
/**
* sme_oem_get_capability() - get oem capability
* @hal: Handle returned by mac_open
* @oem_cap: pointer to oem_capability
*
* This function is used to get the OEM capability from UMAC.
* Currently RTT related capabilities are received. More
* capabilities can be added in future.
*
* Return: CDF_STATUS
*/
CDF_STATUS sme_oem_get_capability(tHalHandle hal,
struct sme_oem_capability *cap)
{
CDF_STATUS status = CDF_STATUS_SUCCESS;
tpAniSirGlobal pmac = PMAC_STRUCT(hal);
uint8_t *bytes;
bytes = pmac->rrm.rrmSmeContext.rrmConfig.rm_capability;
cap->ftm_rr = bytes[4] & RM_CAP_FTM_RANGE_REPORT;
cap->lci_capability = bytes[4] & RM_CAP_CIVIC_LOC_MEASUREMENT;
return status;
}
/**
* sme_register_ftm_msg_processor() - registers hdd ftm message processor
* function to MAC/SYS

View file

@ -66,6 +66,10 @@
#define ANI_MSG_CHANNEL_INFO_RSP (ANI_DRIVER_MSG_START + 5)
#define ANI_MSG_OEM_ERROR (ANI_DRIVER_MSG_START + 6)
#define ANI_MSG_PEER_STATUS_IND (ANI_DRIVER_MSG_START + 7)
#define ANI_MSG_SET_OEM_CAP_REQ (ANI_DRIVER_MSG_START + 8)
#define ANI_MSG_SET_OEM_CAP_RSP (ANI_DRIVER_MSG_START + 9)
#define ANI_MSG_GET_OEM_CAP_REQ (ANI_DRIVER_MSG_START + 10)
#define ANI_MSG_GET_OEM_CAP_RSP (ANI_DRIVER_MSG_START + 11)
#define ANI_MAX_RADIOS 3
#define ANI_NL_MSG_OK 0