blob: e279116ed0a0aea9495efeae9febe9388e7a2b24 [file] [log] [blame]
/*
* Copyright (c) 2014-2019 The Linux Foundation. All rights reserved.
*
*
* 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.
*/
/**
* DOC: reg_lte.c
* This file contains the LTE feature APIs.
*/
#include <qdf_status.h>
#include <qdf_types.h>
#include <wlan_cmn.h>
#include "reg_services_public_struct.h"
#include <wlan_objmgr_psoc_obj.h>
#include <wlan_objmgr_pdev_obj.h>
#include "reg_services_common.h"
#include "reg_priv_objs.h"
#include "reg_build_chan_list.h"
#include "reg_callbacks.h"
#include "reg_lte.h"
#ifdef LTE_COEX
/**
* reg_process_ch_avoid_freq() - Update unsafe frequencies in psoc_priv_obj
* @psoc: pointer to psoc object
* @pdev: pointer to pdev object
*
* Return: QDF_STATUS
*/
static QDF_STATUS reg_process_ch_avoid_freq(struct wlan_objmgr_psoc *psoc,
struct wlan_objmgr_pdev *pdev)
{
enum channel_enum ch_loop;
enum channel_enum start_ch_idx;
enum channel_enum end_ch_idx;
uint16_t start_channel;
uint16_t end_channel;
uint32_t i;
struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
struct ch_avoid_freq_type *range;
psoc_priv_obj = reg_get_psoc_obj(psoc);
if (!psoc_priv_obj) {
reg_err("reg psoc private obj is NULL");
return QDF_STATUS_E_FAILURE;
}
for (i = 0; i < psoc_priv_obj->avoid_freq_list.ch_avoid_range_cnt;
i++) {
if (psoc_priv_obj->unsafe_chan_list.chan_cnt >= NUM_CHANNELS) {
reg_warn("LTE Coex unsafe channel list full");
break;
}
start_ch_idx = INVALID_CHANNEL;
end_ch_idx = INVALID_CHANNEL;
range = &psoc_priv_obj->avoid_freq_list.avoid_freq_range[i];
start_channel = reg_freq_to_chan(pdev, range->start_freq);
end_channel = reg_freq_to_chan(pdev, range->end_freq);
reg_debug("start: freq %d, ch %d, end: freq %d, ch %d",
range->start_freq, start_channel, range->end_freq,
end_channel);
/* do not process frequency bands that are not mapped to
* predefined channels
*/
if (start_channel == 0 || end_channel == 0)
continue;
for (ch_loop = 0; ch_loop < NUM_CHANNELS;
ch_loop++) {
if (REG_CH_TO_FREQ(ch_loop) >= range->start_freq) {
start_ch_idx = ch_loop;
break;
}
}
for (ch_loop = 0; ch_loop < NUM_CHANNELS;
ch_loop++) {
if (REG_CH_TO_FREQ(ch_loop) >= range->end_freq) {
end_ch_idx = ch_loop;
if (REG_CH_TO_FREQ(ch_loop) > range->end_freq)
end_ch_idx--;
break;
}
}
if (start_ch_idx == INVALID_CHANNEL ||
end_ch_idx == INVALID_CHANNEL)
continue;
for (ch_loop = start_ch_idx; ch_loop <= end_ch_idx;
ch_loop++) {
psoc_priv_obj->unsafe_chan_list.chan_freq_list[
psoc_priv_obj->unsafe_chan_list.chan_cnt++] =
REG_CH_TO_FREQ(ch_loop);
if (psoc_priv_obj->unsafe_chan_list.chan_cnt >=
NUM_CHANNELS) {
reg_warn("LTECoex unsafe ch list full");
break;
}
}
}
reg_debug("number of unsafe channels is %d ",
psoc_priv_obj->unsafe_chan_list.chan_cnt);
if (!psoc_priv_obj->unsafe_chan_list.chan_cnt) {
reg_debug("No valid ch are present in avoid freq event");
return QDF_STATUS_SUCCESS;
}
for (ch_loop = 0; ch_loop < psoc_priv_obj->unsafe_chan_list.chan_cnt;
ch_loop++) {
if (ch_loop >= NUM_CHANNELS)
break;
reg_debug("channel freq %d is not safe",
psoc_priv_obj->unsafe_chan_list.chan_freq_list[ch_loop]);
}
return QDF_STATUS_SUCCESS;
}
/**
* reg_update_unsafe_ch() - Updates unsafe channels in current channel list
* @psoc: Pointer to psoc structure
* @object: Pointer to pdev structure
* @arg: List of arguments
*
* Return: None
*/
static void reg_update_unsafe_ch(struct wlan_objmgr_psoc *psoc,
void *object, void *arg)
{
struct wlan_objmgr_pdev *pdev = (struct wlan_objmgr_pdev *)object;
struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
QDF_STATUS status;
psoc_priv_obj = reg_get_psoc_obj(psoc);
if (!psoc_priv_obj) {
reg_err("reg psoc private obj is NULL");
return;
}
pdev_priv_obj = reg_get_pdev_obj(pdev);
if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) {
reg_err("reg pdev priv obj is NULL");
return;
}
if (psoc_priv_obj->ch_avoid_ind) {
status = reg_process_ch_avoid_freq(psoc, pdev);
if (QDF_IS_STATUS_ERROR(status))
psoc_priv_obj->ch_avoid_ind = false;
}
reg_compute_pdev_current_chan_list(pdev_priv_obj);
status = reg_send_scheduler_msg_nb(psoc, pdev);
if (QDF_IS_STATUS_ERROR(status))
reg_err("channel change msg schedule failed");
}
QDF_STATUS reg_process_ch_avoid_event(struct wlan_objmgr_psoc *psoc,
struct ch_avoid_ind_type *ch_avoid_event)
{
uint32_t i;
struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
QDF_STATUS status;
psoc_priv_obj = reg_get_psoc_obj(psoc);
if (!psoc_priv_obj) {
reg_err("reg psoc private obj is NULL");
return QDF_STATUS_E_FAILURE;
}
if (CH_AVOID_RULE_DO_NOT_RESTART ==
psoc_priv_obj->restart_beaconing) {
reg_debug("skipping all LTE Coex unsafe channel range");
return QDF_STATUS_SUCCESS;
}
/* Make unsafe channel list */
reg_debug("band count %d", ch_avoid_event->ch_avoid_range_cnt);
/* generate vendor specific event */
qdf_mem_zero(&psoc_priv_obj->avoid_freq_list,
sizeof(struct ch_avoid_ind_type));
qdf_mem_zero(&psoc_priv_obj->unsafe_chan_list,
sizeof(struct unsafe_ch_list));
for (i = 0; i < ch_avoid_event->ch_avoid_range_cnt; i++) {
if ((CH_AVOID_RULE_RESTART_24G_ONLY ==
psoc_priv_obj->restart_beaconing) &&
REG_IS_5GHZ_FREQ(ch_avoid_event->
avoid_freq_range[i].start_freq)) {
reg_debug(
"skipping 5Ghz LTE Coex unsafe channel range");
continue;
}
psoc_priv_obj->avoid_freq_list.avoid_freq_range[i].start_freq =
ch_avoid_event->avoid_freq_range[i].start_freq;
psoc_priv_obj->avoid_freq_list.avoid_freq_range[i].end_freq =
ch_avoid_event->avoid_freq_range[i].end_freq;
}
psoc_priv_obj->avoid_freq_list.ch_avoid_range_cnt =
ch_avoid_event->ch_avoid_range_cnt;
psoc_priv_obj->ch_avoid_ind = true;
status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_NB_ID);
if (QDF_IS_STATUS_ERROR(status)) {
reg_err("error taking psoc ref cnt");
return status;
}
status = wlan_objmgr_iterate_obj_list(
psoc, WLAN_PDEV_OP, reg_update_unsafe_ch, NULL, 1,
WLAN_REGULATORY_NB_ID);
wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
return status;
}
#endif