blob: 16351c11d51c3cbb350b1c5ecb6e24e65b28df41 [file] [log] [blame]
/*
* Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/**
* DOC: wma_ocb.c
*
* WLAN Host Device Driver 802.11p OCB implementation
*/
#include "wma_ocb.h"
#include "wmi_unified_api.h"
#include "cds_utils.h"
#include "cds_api.h"
#include <cdp_txrx_ocb.h>
#include <cdp_txrx_handle.h>
#include "wlan_ocb_ucfg_api.h"
/**
* wma_ocb_resp() - send the OCB set config response via callback
* @wma_handle: pointer to the WMA handle
* @status: status of the set config command
*/
int wma_ocb_set_config_resp(tp_wma_handle wma_handle, uint8_t status)
{
QDF_STATUS qdf_status;
struct sir_ocb_set_config_response *resp;
struct scheduler_msg msg = {0};
struct sir_ocb_config *req = wma_handle->ocb_config_req;
void *vdev = (req ?
wma_handle->interfaces[req->session_id].handle : NULL);
struct ol_txrx_ocb_set_chan ocb_set_chan;
void *soc = cds_get_context(QDF_MODULE_ID_SOC);
/*
* If the command was successful, save the channel information in the
* vdev.
*/
if (status == QDF_STATUS_SUCCESS && vdev && req) {
ocb_set_chan.ocb_channel_info = cdp_get_ocb_chan_info(soc,
(struct cdp_vdev *)vdev);
if (ocb_set_chan.ocb_channel_info)
qdf_mem_free(ocb_set_chan.ocb_channel_info);
ocb_set_chan.ocb_channel_count =
req->channel_count;
if (req->channel_count) {
int i;
int buf_size = sizeof(*ocb_set_chan.ocb_channel_info) *
req->channel_count;
ocb_set_chan.ocb_channel_info =
qdf_mem_malloc(buf_size);
if (!ocb_set_chan.ocb_channel_info)
return -ENOMEM;
qdf_mem_zero(ocb_set_chan.ocb_channel_info, buf_size);
for (i = 0; i < req->channel_count; i++) {
ocb_set_chan.ocb_channel_info[i].chan_freq =
req->channels[i].chan_freq;
if (req->channels[i].flags &
OCB_CHANNEL_FLAG_DISABLE_RX_STATS_HDR)
ocb_set_chan.ocb_channel_info[i].
disable_rx_stats_hdr = 1;
}
} else {
ocb_set_chan.ocb_channel_info = 0;
ocb_set_chan.ocb_channel_count = 0;
}
cdp_set_ocb_chan_info(soc,
(struct cdp_vdev *)vdev,
ocb_set_chan);
}
/* Free the configuration that was saved in wma_ocb_set_config. */
qdf_mem_free(wma_handle->ocb_config_req);
wma_handle->ocb_config_req = NULL;
resp = qdf_mem_malloc(sizeof(*resp));
if (!resp)
return -ENOMEM;
resp->status = status;
msg.type = eWNI_SME_OCB_SET_CONFIG_RSP;
msg.bodyptr = resp;
qdf_status = scheduler_post_msg(QDF_MODULE_ID_SME, &msg);
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
WMA_LOGE(FL("Fail to post msg to SME"));
qdf_mem_free(resp);
return -EINVAL;
}
return 0;
}
/**
* copy_sir_ocb_config() - deep copy of an OCB config struct
* @src: pointer to the source struct
*
* Return: pointer to the copied struct
*/
static struct sir_ocb_config *copy_sir_ocb_config(struct sir_ocb_config *src)
{
struct sir_ocb_config *dst;
uint32_t length;
void *cursor;
length = sizeof(*src) +
src->channel_count * sizeof(*src->channels) +
src->schedule_size * sizeof(*src->schedule) +
src->dcc_ndl_chan_list_len +
src->dcc_ndl_active_state_list_len;
dst = qdf_mem_malloc(length);
if (!dst)
return NULL;
*dst = *src;
cursor = dst;
cursor += sizeof(*dst);
dst->channels = cursor;
cursor += src->channel_count * sizeof(*dst->channels);
qdf_mem_copy(dst->channels, src->channels,
src->channel_count * sizeof(*dst->channels));
dst->schedule = cursor;
cursor += src->schedule_size * sizeof(*dst->schedule);
qdf_mem_copy(dst->schedule, src->schedule,
src->schedule_size * sizeof(*dst->schedule));
dst->dcc_ndl_chan_list = cursor;
cursor += src->dcc_ndl_chan_list_len;
qdf_mem_copy(dst->dcc_ndl_chan_list, src->dcc_ndl_chan_list,
src->dcc_ndl_chan_list_len);
dst->dcc_ndl_active_state_list = cursor;
cursor += src->dcc_ndl_active_state_list_len;
qdf_mem_copy(dst->dcc_ndl_active_state_list,
src->dcc_ndl_active_state_list,
src->dcc_ndl_active_state_list_len);
return dst;
}
/**
* wma_ocb_set_config_req() - send the OCB config request
* @wma_handle: pointer to the WMA handle
* @config_req: the configuration to be set.
*/
int wma_ocb_set_config_req(tp_wma_handle wma_handle,
struct sir_ocb_config *config_req)
{
struct wma_target_req *msg;
struct wma_vdev_start_req req;
QDF_STATUS status = QDF_STATUS_SUCCESS;
/* if vdev is not yet up, send vdev start request and wait for response.
* OCB set_config request should be sent on receiving
* vdev start response message
*/
if (!wma_is_vdev_up(config_req->session_id)) {
qdf_mem_zero(&req, sizeof(req));
/* Enqueue OCB Set Schedule request message */
msg = wma_fill_vdev_req(wma_handle, config_req->session_id,
WMA_OCB_SET_CONFIG_CMD,
WMA_TARGET_REQ_TYPE_VDEV_START,
(void *)config_req, 1000);
if (!msg) {
WMA_LOGE(FL("Failed to fill vdev req %d"), req.vdev_id);
status = QDF_STATUS_E_NOMEM;
return status;
}
req.chan = cds_freq_to_chan(config_req->channels[0].chan_freq);
req.vdev_id = msg->vdev_id;
if (cds_chan_to_band(req.chan) == CDS_BAND_2GHZ)
req.dot11_mode = WNI_CFG_DOT11_MODE_11G;
else
req.dot11_mode = WNI_CFG_DOT11_MODE_11A;
if (wma_handle->ocb_config_req)
qdf_mem_free(wma_handle->ocb_config_req);
wma_handle->ocb_config_req = copy_sir_ocb_config(config_req);
req.preferred_rx_streams = 2;
req.preferred_tx_streams = 2;
status = wma_vdev_start(wma_handle, &req, false);
if (status != QDF_STATUS_SUCCESS) {
wma_remove_vdev_req(wma_handle, req.vdev_id,
WMA_TARGET_REQ_TYPE_VDEV_START);
WMA_LOGE(FL("vdev_start failed, status = %d"), status);
}
return 0;
} else {
return wma_ocb_set_config(wma_handle, config_req);
}
}
int wma_ocb_start_resp_ind_cont(tp_wma_handle wma_handle)
{
QDF_STATUS qdf_status = 0;
if (!wma_handle->ocb_config_req) {
WMA_LOGE(FL("The request could not be found"));
return QDF_STATUS_E_EMPTY;
}
qdf_status = wma_ocb_set_config(wma_handle, wma_handle->ocb_config_req);
return qdf_status;
}
static WLAN_PHY_MODE wma_ocb_freq_to_mode(uint32_t freq)
{
if (cds_chan_to_band(cds_freq_to_chan(freq)) == CDS_BAND_2GHZ)
return MODE_11G;
else
return MODE_11A;
}
/**
* wma_send_ocb_set_config() - send the OCB config to the FW
* @wma_handle: pointer to the WMA handle
* @config: the OCB configuration
*
* Return: 0 on success
*/
int wma_ocb_set_config(tp_wma_handle wma_handle, struct sir_ocb_config *config)
{
int32_t ret, i;
uint32_t *ch_mhz;
struct ocb_config tconfig = {0};
tconfig.vdev_id = config->session_id;
tconfig.channel_count = config->channel_count;
tconfig.schedule_size = config->schedule_size;
tconfig.flags = config->flags;
tconfig.channels = (struct ocb_config_chan *)config->channels;
tconfig.schedule = (struct ocb_config_schdl *)config->schedule;
tconfig.dcc_ndl_chan_list_len = config->dcc_ndl_chan_list_len;
tconfig.dcc_ndl_chan_list = config->dcc_ndl_chan_list;
tconfig.dcc_ndl_active_state_list_len =
config->dcc_ndl_active_state_list_len;
tconfig.dcc_ndl_active_state_list = config->dcc_ndl_active_state_list;
ch_mhz = qdf_mem_malloc(sizeof(uint32_t)*config->channel_count);
if (ch_mhz == NULL) {
WMA_LOGE(FL("Memory allocation failed"));
return -ENOMEM;
}
for (i = 0; i < config->channel_count; i++)
ch_mhz[i] = wma_ocb_freq_to_mode(config->channels[i].chan_freq);
/*
* Save the configuration so that it can be used in
* wma_ocb_set_config_event_handler.
*/
if (wma_handle->ocb_config_req != config) {
if (wma_handle->ocb_config_req)
qdf_mem_free(wma_handle->ocb_config_req);
wma_handle->ocb_config_req = copy_sir_ocb_config(config);
}
ret = wmi_unified_ocb_set_config(wma_handle->wmi_handle, &tconfig);
if (ret != EOK) {
if (wma_handle->ocb_config_req) {
qdf_mem_free(wma_handle->ocb_config_req);
wma_handle->ocb_config_req = NULL;
}
qdf_mem_free(ch_mhz);
WMA_LOGE("Failed to set OCB config");
return -EIO;
}
qdf_mem_free(ch_mhz);
return 0;
}
/**
* wma_ocb_set_config_event_handler() - Response event for the set config cmd
* @handle: the WMA handle
* @event_buf: buffer with the event parameters
* @len: length of the buffer
*
* Return: 0 on success
*/
int wma_ocb_set_config_event_handler(void *handle, uint8_t *event_buf,
uint32_t len)
{
WMI_OCB_SET_CONFIG_RESP_EVENTID_param_tlvs *param_tlvs;
wmi_ocb_set_config_resp_event_fixed_param *fix_param;
param_tlvs = (WMI_OCB_SET_CONFIG_RESP_EVENTID_param_tlvs *)event_buf;
fix_param = param_tlvs->fixed_param;
return wma_ocb_set_config_resp(handle, fix_param->status);
};
/**
* wma_ocb_set_utc_time() - send the UTC time to the firmware
* @wma_handle: pointer to the WMA handle
* @utc: pointer to the UTC time struct
*
* Return: 0 on succes
*/
int wma_ocb_set_utc_time(tp_wma_handle wma_handle, struct sir_ocb_utc *utc)
{
int32_t ret;
struct ocb_utc_param cmd = {0};
cmd.vdev_id = utc->vdev_id;
qdf_mem_copy(&cmd.utc_time, &utc->utc_time, WMI_SIZE_UTC_TIME);
qdf_mem_copy(&cmd.time_error, &utc->time_error,
WMI_SIZE_UTC_TIME_ERROR);
ret = wmi_unified_ocb_set_utc_time_cmd(wma_handle->wmi_handle, &cmd);
if (ret != EOK) {
WMA_LOGE(FL("Failed to set OCB UTC time"));
return -EIO;
}
return 0;
}
/**
* wma_ocb_start_timing_advert() - start sending the timing advertisement
* frames on a channel
* @wma_handle: pointer to the WMA handle
* @timing_advert: pointer to the timing advertisement struct
*
* Return: 0 on succes
*/
int wma_ocb_start_timing_advert(tp_wma_handle wma_handle,
struct sir_ocb_timing_advert *timing_advert)
{
int32_t ret;
struct ocb_timing_advert_param cmd = {0};
cmd.vdev_id = timing_advert->vdev_id;
cmd.repeat_rate = timing_advert->repeat_rate;
cmd.chan_freq = timing_advert->chan_freq;
cmd.timestamp_offset = timing_advert->timestamp_offset;
cmd.time_value_offset = timing_advert->time_value_offset;
cmd.template_length = timing_advert->template_length;
cmd.template_value = (uint8_t *)timing_advert->template_value;
ret = wmi_unified_ocb_start_timing_advert(wma_handle->wmi_handle,
&cmd);
if (ret != EOK) {
WMA_LOGE(FL("Failed to start OCB timing advert"));
return -EIO;
}
return 0;
}
/**
* wma_ocb_stop_timing_advert() - stop sending the timing advertisement frames
* on a channel
* @wma_handle: pointer to the WMA handle
* @timing_advert: pointer to the timing advertisement struct
*
* Return: 0 on succes
*/
int wma_ocb_stop_timing_advert(tp_wma_handle wma_handle,
struct sir_ocb_timing_advert *timing_advert)
{
int32_t ret;
struct ocb_timing_advert_param cmd = {0};
cmd.vdev_id = timing_advert->vdev_id;
cmd.chan_freq = timing_advert->chan_freq;
ret = wmi_unified_ocb_stop_timing_advert(wma_handle->wmi_handle,
&cmd);
if (ret != EOK) {
WMA_LOGE(FL("Failed to stop OCB timing advert"));
return -EIO;
}
return 0;
}
/**
* wma_ocb_get_tsf_timer() - stop sending the timing advertisement frames on a
* channel
* @wma_handle: pointer to the WMA handle
* @request: pointer to the request
*
* Return: 0 on succes
*/
int wma_ocb_get_tsf_timer(tp_wma_handle wma_handle,
struct sir_ocb_get_tsf_timer *request)
{
QDF_STATUS ret;
/* Send the WMI command */
ret = wmi_unified_ocb_get_tsf_timer(wma_handle->wmi_handle,
(struct ocb_get_tsf_timer_param *)request);
/* If there is an error, set the completion event */
if (ret != EOK) {
WMA_LOGE(FL("Failed to send WMI message: %d"), ret);
return -EIO;
}
return 0;
}
/**
* wma_ocb_get_tsf_timer_resp_event_handler() - Event for the get TSF timer cmd
* @handle: the WMA handle
* @event_buf: buffer with the event parameters
* @len: length of the buffer
*
* Return: 0 on success
*/
static int wma_ocb_get_tsf_timer_resp_event_handler(void *handle,
uint8_t *event_buf,
uint32_t len)
{
QDF_STATUS qdf_status;
struct sir_ocb_get_tsf_timer_response *response;
WMI_OCB_GET_TSF_TIMER_RESP_EVENTID_param_tlvs *param_tlvs;
wmi_ocb_get_tsf_timer_resp_event_fixed_param *fix_param;
struct scheduler_msg msg = {0};
param_tlvs = (WMI_OCB_GET_TSF_TIMER_RESP_EVENTID_param_tlvs *)event_buf;
fix_param = param_tlvs->fixed_param;
/* Allocate and populate the response */
response = qdf_mem_malloc(sizeof(*response));
if (response == NULL)
return -ENOMEM;
response->vdev_id = fix_param->vdev_id;
response->timer_high = fix_param->tsf_timer_high;
response->timer_low = fix_param->tsf_timer_low;
msg.type = eWNI_SME_OCB_GET_TSF_TIMER_RSP;
msg.bodyptr = response;
qdf_status = scheduler_post_msg(QDF_MODULE_ID_SME, &msg);
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
WMA_LOGE(FL("Failed to post msg to SME"));
qdf_mem_free(response);
return -EINVAL;
}
return 0;
}
/**
* wma_dcc_get_stats() - get the DCC channel stats
* @wma_handle: pointer to the WMA handle
* @get_stats_param: pointer to the dcc stats
*
* Return: 0 on succes
*/
int wma_dcc_get_stats(tp_wma_handle wma_handle,
struct sir_dcc_get_stats *get_stats_param)
{
int32_t ret;
struct ocb_dcc_get_stats_param cmd = {0};
cmd.vdev_id = get_stats_param->vdev_id;
cmd.channel_count = get_stats_param->channel_count;
cmd.request_array_len = get_stats_param->request_array_len;
cmd.request_array = get_stats_param->request_array;
/* Send the WMI command */
ret = wmi_unified_dcc_get_stats_cmd(wma_handle->wmi_handle, &cmd);
if (ret != EOK) {
WMA_LOGE(FL("Failed to send WMI message: %d"), ret);
return -EIO;
}
return 0;
}
/**
* wma_dcc_get_stats_resp_event_handler() - Response event for the get stats cmd
* @handle: the WMA handle
* @event_buf: buffer with the event parameters
* @len: length of the buffer
*
* Return: 0 on success
*/
static int wma_dcc_get_stats_resp_event_handler(void *handle,
uint8_t *event_buf,
uint32_t len)
{
QDF_STATUS qdf_status;
struct sir_dcc_get_stats_response *response;
WMI_DCC_GET_STATS_RESP_EVENTID_param_tlvs *param_tlvs;
wmi_dcc_get_stats_resp_event_fixed_param *fix_param;
struct scheduler_msg msg = {0};
param_tlvs = (WMI_DCC_GET_STATS_RESP_EVENTID_param_tlvs *)event_buf;
fix_param = param_tlvs->fixed_param;
/* Allocate and populate the response */
if (fix_param->num_channels > ((WMI_SVC_MSG_MAX_SIZE -
sizeof(*fix_param)) / sizeof(wmi_dcc_ndl_stats_per_channel)) ||
fix_param->num_channels > param_tlvs->num_stats_per_channel_list) {
WMA_LOGE("%s: too many channels:%d, param_tlvs->num_stats_per_channel_list:%d",
__func__, fix_param->num_channels,
param_tlvs->num_stats_per_channel_list);
return -EINVAL;
}
response = qdf_mem_malloc(sizeof(*response) + fix_param->num_channels *
sizeof(wmi_dcc_ndl_stats_per_channel));
if (response == NULL)
return -ENOMEM;
response->vdev_id = fix_param->vdev_id;
response->num_channels = fix_param->num_channels;
response->channel_stats_array_len =
fix_param->num_channels * sizeof(wmi_dcc_ndl_stats_per_channel);
response->channel_stats_array = ((void *)response) + sizeof(*response);
qdf_mem_copy(response->channel_stats_array,
param_tlvs->stats_per_channel_list,
response->channel_stats_array_len);
msg.type = eWNI_SME_DCC_GET_STATS_RSP;
msg.bodyptr = response;
qdf_status = scheduler_post_msg(QDF_MODULE_ID_SME, &msg);
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
WMA_LOGE(FL("Failed to post msg to SME"));
qdf_mem_free(response);
return -EINVAL;
}
return 0;
}
/**
* wma_dcc_clear_stats() - command to clear the DCC stats
* @wma_handle: pointer to the WMA handle
* @clear_stats_param: parameters to the command
*
* Return: 0 on succes
*/
int wma_dcc_clear_stats(tp_wma_handle wma_handle,
struct sir_dcc_clear_stats *clear_stats_param)
{
int32_t ret;
/* Send the WMI command */
ret = wmi_unified_dcc_clear_stats(wma_handle->wmi_handle,
(struct ocb_dcc_clear_stats_param *)clear_stats_param);
if (ret != EOK) {
WMA_LOGE(FL("Failed to send the WMI command"));
return -EIO;
}
return 0;
}
/**
* wma_dcc_update_ndl() - command to update the NDL data
* @wma_handle: pointer to the WMA handle
* @update_ndl_param: pointer to the request parameters
*
* Return: 0 on success
*/
int wma_dcc_update_ndl(tp_wma_handle wma_handle,
struct sir_dcc_update_ndl *update_ndl_param)
{
QDF_STATUS qdf_status;
struct ocb_dcc_update_ndl_param *cmd;
cmd = (struct ocb_dcc_update_ndl_param *) update_ndl_param;
/* Send the WMI command */
qdf_status = wmi_unified_dcc_update_ndl(wma_handle->wmi_handle,
cmd);
/* If there is an error, set the completion event */
if (qdf_status) {
WMA_LOGE(FL("Failed to send WMI message: %d"), qdf_status);
return -EIO;
}
return 0;
}
/**
* wma_dcc_update_ndl_resp_event_handler() - Response event for the update NDL
* command
* @handle: the WMA handle
* @event_buf: buffer with the event parameters
* @len: length of the buffer
*
* Return: 0 on success
*/
static int wma_dcc_update_ndl_resp_event_handler(void *handle,
uint8_t *event_buf,
uint32_t len)
{
QDF_STATUS qdf_status;
struct sir_dcc_update_ndl_response *resp;
WMI_DCC_UPDATE_NDL_RESP_EVENTID_param_tlvs *param_tlvs;
wmi_dcc_update_ndl_resp_event_fixed_param *fix_param;
struct scheduler_msg msg = {0};
param_tlvs = (WMI_DCC_UPDATE_NDL_RESP_EVENTID_param_tlvs *)event_buf;
fix_param = param_tlvs->fixed_param;
/* Allocate and populate the response */
resp = qdf_mem_malloc(sizeof(*resp));
if (!resp) {
WMA_LOGE(FL("Error allocating memory for the response."));
return -ENOMEM;
}
resp->vdev_id = fix_param->vdev_id;
resp->status = fix_param->status;
msg.type = eWNI_SME_DCC_UPDATE_NDL_RSP;
msg.bodyptr = resp;
qdf_status = scheduler_post_msg(QDF_MODULE_ID_SME, &msg);
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
WMA_LOGE(FL("Failed to post msg to SME"));
qdf_mem_free(resp);
return -EINVAL;
}
return 0;
}
/**
* wma_dcc_stats_event_handler() - Response event for the get stats cmd
* @handle: the WMA handle
* @event_buf: buffer with the event parameters
* @len: length of the buffer
*
* Return: 0 on success
*/
static int wma_dcc_stats_event_handler(void *handle, uint8_t *event_buf,
uint32_t len)
{
QDF_STATUS qdf_status;
struct sir_dcc_get_stats_response *response;
WMI_DCC_STATS_EVENTID_param_tlvs *param_tlvs;
wmi_dcc_stats_event_fixed_param *fix_param;
struct scheduler_msg msg = {0};
param_tlvs = (WMI_DCC_STATS_EVENTID_param_tlvs *)event_buf;
fix_param = param_tlvs->fixed_param;
/* Allocate and populate the response */
if (fix_param->num_channels > ((WMI_SVC_MSG_MAX_SIZE -
sizeof(*fix_param)) / sizeof(wmi_dcc_ndl_stats_per_channel))) {
WMA_LOGE("%s: too many channels:%d", __func__,
fix_param->num_channels);
QDF_ASSERT(0);
return -EINVAL;
}
response = qdf_mem_malloc(sizeof(*response) +
fix_param->num_channels * sizeof(wmi_dcc_ndl_stats_per_channel));
if (response == NULL)
return -ENOMEM;
response->vdev_id = fix_param->vdev_id;
response->num_channels = fix_param->num_channels;
response->channel_stats_array_len =
fix_param->num_channels * sizeof(wmi_dcc_ndl_stats_per_channel);
if (fix_param->num_channels > param_tlvs->num_stats_per_channel_list) {
WMA_LOGE("FW message num_chan %d more than TLV hdr %d",
fix_param->num_channels,
param_tlvs->num_stats_per_channel_list);
return -EINVAL;
}
response->channel_stats_array = ((void *)response) + sizeof(*response);
qdf_mem_copy(response->channel_stats_array,
param_tlvs->stats_per_channel_list,
response->channel_stats_array_len);
msg.type = eWNI_SME_DCC_STATS_EVENT;
msg.bodyptr = response;
qdf_status = scheduler_post_msg(QDF_MODULE_ID_SME, &msg);
if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
WMA_LOGE(FL("Failed to post msg to SME"));
qdf_mem_free(response);
return -EINVAL;
}
return 0;
}
/**
* wma_ocb_register_event_handlers() - register handlers for the OCB WMI
* events
* @wma_handle: pointer to the WMA handle
*
* Return: 0 on success, non-zero on failure
*/
int wma_ocb_register_event_handlers(tp_wma_handle wma_handle)
{
int status;
if (!wma_handle) {
WMA_LOGE(FL("wma_handle is NULL"));
return -EINVAL;
}
/* Initialize the members in WMA used by wma_ocb */
status = wmi_unified_register_event_handler(wma_handle->wmi_handle,
wmi_ocb_set_config_resp_event_id,
wma_ocb_set_config_event_handler,
WMA_RX_SERIALIZER_CTX);
if (status)
return status;
status = wmi_unified_register_event_handler(
wma_handle->wmi_handle,
wmi_ocb_get_tsf_timer_resp_event_id,
wma_ocb_get_tsf_timer_resp_event_handler,
WMA_RX_SERIALIZER_CTX);
if (status)
return status;
status = wmi_unified_register_event_handler(
wma_handle->wmi_handle,
wmi_dcc_get_stats_resp_event_id,
wma_dcc_get_stats_resp_event_handler,
WMA_RX_SERIALIZER_CTX);
if (status)
return status;
status = wmi_unified_register_event_handler(
wma_handle->wmi_handle,
wmi_dcc_update_ndl_resp_event_id,
wma_dcc_update_ndl_resp_event_handler,
WMA_RX_SERIALIZER_CTX);
if (status)
return status;
status = wmi_unified_register_event_handler(wma_handle->wmi_handle,
wmi_dcc_stats_event_id,
wma_dcc_stats_event_handler,
WMA_RX_SERIALIZER_CTX);
if (status)
return status;
return QDF_STATUS_SUCCESS;
}
/**
* wma_start_ocb_vdev() - start OCB vdev
* @config: ocb channel config
*
* Return: QDF_STATUS_SUCCESS on success
*/
static QDF_STATUS wma_start_ocb_vdev(struct ocb_config *config)
{
struct wma_target_req *msg;
struct wma_vdev_start_req req;
QDF_STATUS status;
tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA);
qdf_mem_zero(&req, sizeof(req));
msg = wma_fill_vdev_req(wma, config->vdev_id,
WMA_OCB_SET_CONFIG_CMD,
WMA_TARGET_REQ_TYPE_VDEV_START,
(void *)config, 1000);
if (!msg) {
WMA_LOGE(FL("Failed to fill vdev req %d"), config->vdev_id);
return QDF_STATUS_E_NOMEM;
}
req.chan = cds_freq_to_chan(config->channels[0].chan_freq);
req.vdev_id = msg->vdev_id;
if (cds_chan_to_band(req.chan) == CDS_BAND_2GHZ)
req.dot11_mode = WNI_CFG_DOT11_MODE_11G;
else
req.dot11_mode = WNI_CFG_DOT11_MODE_11A;
req.preferred_rx_streams = 2;
req.preferred_tx_streams = 2;
status = wma_vdev_start(wma, &req, false);
if (status != QDF_STATUS_SUCCESS) {
wma_remove_vdev_req(wma, req.vdev_id,
WMA_TARGET_REQ_TYPE_VDEV_START);
WMA_LOGE(FL("vdev_start failed, status = %d"), status);
}
return status;
}
QDF_STATUS wma_ocb_register_callbacks(tp_wma_handle wma_handle)
{
ucfg_ocb_register_vdev_start(wma_handle->pdev, wma_start_ocb_vdev);
return QDF_STATUS_SUCCESS;
}