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
diff --git a/core/hdd/src/wlan_hdd_oemdata.c b/core/hdd/src/wlan_hdd_oemdata.c
index e23412a..52c74fe 100644
--- a/core/hdd/src/wlan_hdd_oemdata.c
+++ b/core/hdd/src/wlan_hdd_oemdata.c
@@ -44,10 +44,82 @@
#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
* @info: ioctl request information
@@ -63,15 +135,11 @@
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 @@
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;
@@ -511,6 +526,132 @@
}
/**
+ * 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
* @peerMac: MAC address of peer
@@ -768,6 +909,54 @@
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)",