| /* |
| * 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 <cdp_txrx_ocb.h> |
| #include <cdp_txrx_handle.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_param tconfig = {0}; |
| |
| tconfig.session_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_channel *)config->channels; |
| tconfig.schedule = (struct ocb_config_sched *)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, |
| ch_mhz); |
| 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, |
| request->vdev_id); |
| /* 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 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, |
| clear_stats_param->vdev_id, |
| clear_stats_param->dcc_stats_bitmap); |
| 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 dcc_update_ndl_param *cmd; |
| |
| cmd = (struct 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; |
| } |