|
@@ -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;
|
|
|
- }
|
|
|
-
|
|
|
- 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];
|
|
|
- }
|
|
|
- }
|
|
|
+ status = populate_oem_data_cap(pAdapter, &oemDataCap);
|
|
|
+ if (!status)
|
|
|
+ return status;
|
|
|
|
|
|
- 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)",
|