blob: 8b35247d8b94e63076f1a320792f5a772f3e17f3 [file] [log] [blame]
/*
* Copyright (c) 2017 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: wlan_tdls_tgt_api.c
*
* TDLS south bound interface definitions
*/
#include "qdf_status.h"
#include <wlan_tdls_tgt_api.h>
#include "../../core/src/wlan_tdls_main.h"
#include "../../core/src/wlan_tdls_cmds_process.h"
#include "../../core/src/wlan_tdls_mgmt.h"
static inline struct wlan_lmac_if_tdls_tx_ops *
wlan_psoc_get_tdls_txops(struct wlan_objmgr_psoc *psoc)
{
return &psoc->soc_cb.tx_ops.tdls_tx_ops;
}
static inline struct wlan_lmac_if_tdls_rx_ops *
wlan_psoc_get_tdls_rxops(struct wlan_objmgr_psoc *psoc)
{
return &psoc->soc_cb.rx_ops.tdls_rx_ops;
}
QDF_STATUS tgt_tdls_set_fw_state(struct wlan_objmgr_psoc *psoc,
struct tdls_info *tdls_param)
{
struct wlan_lmac_if_tdls_tx_ops *tdls_ops = NULL;
tdls_ops = wlan_psoc_get_tdls_txops(psoc);
if (tdls_ops && tdls_ops->update_fw_state)
return tdls_ops->update_fw_state(psoc, tdls_param);
else
return QDF_STATUS_SUCCESS;
}
QDF_STATUS tgt_tdls_set_peer_state(struct wlan_objmgr_psoc *psoc,
struct tdls_peer_update_state *peer_param)
{
struct wlan_lmac_if_tdls_tx_ops *tdls_ops = NULL;
tdls_ops = wlan_psoc_get_tdls_txops(psoc);
if (tdls_ops && tdls_ops->update_peer_state)
return tdls_ops->update_peer_state(psoc, peer_param);
else
return QDF_STATUS_SUCCESS;
}
QDF_STATUS tgt_tdls_set_offchan_mode(struct wlan_objmgr_psoc *psoc,
struct tdls_channel_switch_params *param)
{
struct wlan_lmac_if_tdls_tx_ops *tdls_ops = NULL;
tdls_ops = wlan_psoc_get_tdls_txops(psoc);
if (tdls_ops && tdls_ops->set_offchan_mode)
return tdls_ops->set_offchan_mode(psoc, param);
else
return QDF_STATUS_SUCCESS;
}
QDF_STATUS tgt_tdls_set_uapsd(struct wlan_objmgr_psoc *psoc,
struct sta_uapsd_trig_params *params)
{
struct wlan_lmac_if_tdls_tx_ops *tdls_ops = NULL;
tdls_ops = wlan_psoc_get_tdls_txops(psoc);
if (tdls_ops && tdls_ops->tdls_set_uapsd)
return tdls_ops->tdls_set_uapsd(psoc, params);
else
return QDF_STATUS_SUCCESS;
}
QDF_STATUS tgt_tdls_send_mgmt_tx_completion(struct scheduler_msg *pmsg)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
if (!pmsg || !pmsg->bodyptr) {
tdls_err("msg: 0x%pK", pmsg);
QDF_ASSERT(0);
return QDF_STATUS_E_NULL_VALUE;
}
status = tdls_send_mgmt_tx_completion(pmsg->bodyptr);
return status;
}
QDF_STATUS tgt_tdls_send_mgmt_rsp(struct scheduler_msg *pmsg)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
if (!pmsg || !pmsg->bodyptr) {
tdls_err("msg: 0x%pK", pmsg);
QDF_ASSERT(0);
return QDF_STATUS_E_NULL_VALUE;
}
status = tdls_process_send_mgmt_rsp(pmsg->bodyptr);
return status;
}
QDF_STATUS tgt_tdls_add_peer_rsp(struct scheduler_msg *pmsg)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
if (!pmsg || !pmsg->bodyptr) {
tdls_err("msg: 0x%pK", pmsg);
QDF_ASSERT(0);
return QDF_STATUS_E_NULL_VALUE;
}
status = tdls_process_add_peer_rsp(pmsg->bodyptr);
return status;
}
QDF_STATUS tgt_tdls_del_peer_rsp(struct scheduler_msg *pmsg)
{
QDF_STATUS status = QDF_STATUS_SUCCESS;
if (!pmsg || !pmsg->bodyptr) {
tdls_err("msg: 0x%pK", pmsg);
QDF_ASSERT(0);
return QDF_STATUS_E_NULL_VALUE;
}
status = tdls_process_del_peer_rsp(pmsg->bodyptr);
return status;
}
QDF_STATUS tgt_tdls_register_ev_handler(struct wlan_objmgr_psoc *psoc)
{
struct wlan_lmac_if_tdls_tx_ops *tdls_ops = NULL;
tdls_ops = wlan_psoc_get_tdls_txops(psoc);
if (tdls_ops && tdls_ops->tdls_reg_ev_handler)
return tdls_ops->tdls_reg_ev_handler(psoc, NULL);
else
return QDF_STATUS_SUCCESS;
}
QDF_STATUS tgt_tdls_unregister_ev_handler(struct wlan_objmgr_psoc *psoc)
{
struct wlan_lmac_if_tdls_tx_ops *tdls_ops = NULL;
tdls_ops = wlan_psoc_get_tdls_txops(psoc);
if (tdls_ops->tdls_unreg_ev_handler)
return tdls_ops->tdls_unreg_ev_handler(psoc, NULL);
else
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
tgt_tdls_event_handler(struct wlan_objmgr_psoc *psoc,
struct tdls_event_info *info)
{
struct scheduler_msg msg = {0,};
struct tdls_event_notify *notify;
uint8_t vdev_id;
QDF_STATUS status;
if (!psoc || !info) {
tdls_err("psoc: 0x%pK, info: 0x%pK", psoc, info);
return QDF_STATUS_E_NULL_VALUE;
}
tdls_debug("vdev: %d, type: %d, reason: %d" QDF_MAC_ADDRESS_STR,
info->vdev_id, info->message_type, info->peer_reason,
QDF_MAC_ADDR_ARRAY(info->peermac.bytes));
notify = qdf_mem_malloc(sizeof(*notify));
if (!notify) {
tdls_err("mem allocate fail");
return QDF_STATUS_E_NOMEM;
}
vdev_id = info->vdev_id;
notify->vdev =
wlan_objmgr_get_vdev_by_id_from_psoc(psoc,
vdev_id, WLAN_TDLS_SB_ID);
if (!notify->vdev) {
tdls_err("null vdev, vdev_id: %d, psoc: 0x%pK", vdev_id, psoc);
return QDF_STATUS_E_INVAL;
}
qdf_mem_copy(&notify->event, info, sizeof(*info));
msg.bodyptr = notify;
msg.callback = tdls_process_evt;
status = scheduler_post_msg(QDF_MODULE_ID_TARGET_IF, &msg);
if (QDF_IS_STATUS_ERROR(status)) {
tdls_err("can't post msg to handle tdls event");
wlan_objmgr_vdev_release_ref(notify->vdev, WLAN_TDLS_SB_ID);
qdf_mem_free(notify);
}
return status;
}
static
QDF_STATUS tgt_tdls_mgmt_frame_process_rx_cb(
struct wlan_objmgr_psoc *psoc,
struct wlan_objmgr_peer *peer,
qdf_nbuf_t buf,
struct mgmt_rx_event_params *mgmt_rx_params,
enum mgmt_frame_type frm_type)
{
struct tdls_rx_mgmt_frame *rx_mgmt;
struct tdls_rx_mgmt_event *rx_mgmt_event;
struct tdls_soc_priv_obj *tdls_soc_obj;
struct scheduler_msg msg = {0};
struct wlan_objmgr_vdev *vdev;
uint32_t vdev_id;
uint8_t *pdata;
QDF_STATUS status;
tdls_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc,
WLAN_UMAC_COMP_TDLS);
if (!tdls_soc_obj) {
tdls_err("tdls ctx is NULL, drop this frame");
return QDF_STATUS_E_FAILURE;
}
if (!peer) {
vdev = tdls_get_vdev(psoc, WLAN_TDLS_SB_ID);
if (!vdev) {
tdls_err("current tdls vdev is null, can't get vdev id");
return QDF_STATUS_E_FAILURE;
}
vdev_id = wlan_vdev_get_id(vdev);
wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID);
} else {
vdev = wlan_peer_get_vdev(peer);
if (!vdev) {
tdls_err("vdev is NULL in peer, drop this frame");
return QDF_STATUS_E_FAILURE;
}
vdev_id = wlan_vdev_get_id(vdev);
}
rx_mgmt_event = qdf_mem_malloc(sizeof(*rx_mgmt_event));
if (!rx_mgmt_event) {
tdls_err("Failed to allocate rx mgmt event");
return QDF_STATUS_E_NOMEM;
}
rx_mgmt = qdf_mem_malloc(sizeof(*rx_mgmt) +
mgmt_rx_params->buf_len);
if (!rx_mgmt) {
tdls_err("Failed to allocate rx mgmt frame");
qdf_mem_free(rx_mgmt_event);
return QDF_STATUS_E_NOMEM;
}
pdata = (uint8_t *)qdf_nbuf_data(buf);
rx_mgmt->frame_len = mgmt_rx_params->buf_len;
rx_mgmt->rx_chan = mgmt_rx_params->channel;
rx_mgmt->vdev_id = vdev_id;
rx_mgmt->frm_type = frm_type;
rx_mgmt->rx_rssi = mgmt_rx_params->rssi;
rx_mgmt_event->rx_mgmt = rx_mgmt;
rx_mgmt_event->tdls_soc_obj = tdls_soc_obj;
qdf_mem_copy(rx_mgmt->buf, pdata, mgmt_rx_params->buf_len);
msg.type = TDLS_EVENT_RX_MGMT;
msg.bodyptr = rx_mgmt_event;
msg.callback = tdls_process_rx_frame;
status = scheduler_post_msg(QDF_MODULE_ID_TARGET_IF, &msg);
qdf_nbuf_free(buf);
return status;
}
QDF_STATUS tgt_tdls_mgmt_frame_rx_cb(
struct wlan_objmgr_psoc *psoc,
struct wlan_objmgr_peer *peer,
qdf_nbuf_t buf,
struct mgmt_rx_event_params *mgmt_rx_params,
enum mgmt_frame_type frm_type)
{
QDF_STATUS status;
tdls_debug("psoc:%pK, peer:%pK, type:%d", psoc, peer, frm_type);
if (!buf) {
tdls_err("rx frame buff is null buf:%pK", buf);
return QDF_STATUS_E_INVAL;
}
if (!mgmt_rx_params || !psoc) {
tdls_err("input is NULL mgmt_rx_params:%pK psoc:%pK, peer:%pK",
mgmt_rx_params, psoc, peer);
status = QDF_STATUS_E_INVAL;
goto release_nbuf;
}
status = wlan_objmgr_peer_try_get_ref(peer, WLAN_TDLS_SB_ID);
if (QDF_STATUS_SUCCESS != status)
goto release_nbuf;
status = tgt_tdls_mgmt_frame_process_rx_cb(psoc, peer, buf,
mgmt_rx_params, frm_type);
wlan_objmgr_peer_release_ref(peer, WLAN_TDLS_SB_ID);
if (QDF_STATUS_SUCCESS != status)
release_nbuf:
qdf_nbuf_free(buf);
return status;
}
void tgt_tdls_peers_deleted_notification(struct wlan_objmgr_psoc *psoc,
uint32_t session_id)
{
struct wlan_objmgr_vdev *vdev;
vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc,
session_id,
WLAN_TDLS_SB_ID);
if (!vdev) {
tdls_err("vdev not exist for the session id %d",
session_id);
return;
}
tdls_peers_deleted_notification(vdev, session_id);
wlan_objmgr_vdev_release_ref(vdev,
WLAN_TDLS_SB_ID);
}