blob: 304e2829f74db7a0698a9eb2395b040b13358b14 [file] [log] [blame]
/*****************************************************************************
*
* Copyright (c) 2019 Samsung Electronics Co., Ltd. All rights reserved
*
*****************************************************************************/
#include <net/cfg80211.h>
#include <linux/etherdevice.h>
#include "dev.h"
#include "fapi.h"
#include "fw_test.h"
#include "debug.h"
#include "mgt.h"
#include "mlme.h"
#include "netif.h"
#include "ba.h"
static void slsi_fw_test_save_frame(struct slsi_dev *sdev, struct slsi_fw_test *fwtest, struct sk_buff *saved_skbs[CONFIG_SCSC_WLAN_MAX_INTERFACES + 1], struct sk_buff *skb, bool udi_header)
{
u16 vif;
skb = slsi_skb_copy(skb, GFP_KERNEL);
if (udi_header)
skb_pull(skb, sizeof(struct udi_msg_t));
vif = fapi_get_vif(skb);
SLSI_DBG3(sdev, SLSI_FW_TEST, "sig:0x%.4X, vif:%d", fapi_get_sigid(skb), vif);
slsi_debug_frame(sdev, NULL, skb, "SAVE");
slsi_spinlock_lock(&fwtest->fw_test_lock);
slsi_kfree_skb(saved_skbs[vif]);
saved_skbs[vif] = skb;
slsi_spinlock_unlock(&fwtest->fw_test_lock);
}
static void slsi_fw_test_process_frame(struct slsi_dev *sdev, struct slsi_fw_test *fwtest, struct sk_buff *skb, bool udi_header)
{
u16 vif;
skb = slsi_skb_copy(skb, GFP_KERNEL);
if (udi_header)
skb_pull(skb, sizeof(struct udi_msg_t));
vif = fapi_get_vif(skb);
SLSI_DBG3(sdev, SLSI_FW_TEST, "sig:0x%.4X, vif:%d\n", fapi_get_sigid(skb), vif);
slsi_debug_frame(sdev, NULL, skb, "PROCESS");
slsi_skb_work_enqueue(&fwtest->fw_test_work, skb);
}
int slsi_fw_test_signal(struct slsi_dev *sdev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
u16 vif = fapi_get_vif(skb);
/* Atleast one write to via the UDI interface */
fwtest->fw_test_enabled = true;
SLSI_DBG3(sdev, SLSI_FW_TEST, "0x%p: sig:0x%.4X, vif:%d\n", skb, fapi_get_sigid(skb), vif);
if (WARN(vif > CONFIG_SCSC_WLAN_MAX_INTERFACES, "vif(%d) > CONFIG_SCSC_WLAN_MAX_INTERFACES", vif))
return -EINVAL;
switch (fapi_get_sigid(skb)) {
case MLME_ADD_VIF_REQ:
SLSI_DBG2(sdev, SLSI_FW_TEST, "0x%p: Save MLME_ADD_VIF_REQ(0x%.4X, vif:%d)\n", skb, fapi_get_sigid(skb), vif);
slsi_fw_test_save_frame(sdev, fwtest, fwtest->mlme_add_vif_req, skb, false);
slsi_fw_test_process_frame(sdev, fwtest, skb, false);
break;
case MLME_CONNECT_REQ:
SLSI_DBG2(sdev, SLSI_FW_TEST, "0x%p: Save MLME_CONNECT_REQ(0x%.4X, vif:%d)\n", skb, fapi_get_sigid(skb), vif);
slsi_fw_test_save_frame(sdev, fwtest, fwtest->mlme_connect_req, skb, false);
break;
case MLME_DEL_VIF_REQ:
SLSI_DBG2(sdev, SLSI_FW_TEST, "0x%p: Save MLME_DEL_VIF_REQ(0x%.4X, vif:%d)\n", skb, fapi_get_sigid(skb), vif);
slsi_fw_test_process_frame(sdev, fwtest, skb, false);
break;
default:
return 0;
}
return 0;
}
int slsi_fw_test_signal_with_udi_header(struct slsi_dev *sdev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct udi_msg_t *udi_msg = (struct udi_msg_t *)skb->data;
struct fapi_vif_signal_header *fapi_header = (struct fapi_vif_signal_header *)(skb->data + sizeof(struct udi_msg_t));
if (!fwtest->fw_test_enabled)
return 0;
SLSI_DBG3(sdev, SLSI_FW_TEST, "0x%p: sig:0x%.4X, vif:%d\n", skb, le16_to_cpu(fapi_header->id), le16_to_cpu(fapi_header->vif));
if (udi_msg->direction == SLSI_LOG_DIRECTION_TO_HOST) {
switch (le16_to_cpu(fapi_header->id)) {
case MLME_DISCONNECT_IND:
SLSI_DBG2(sdev, SLSI_FW_TEST, "0x%p: Process MLME_DISCONNECT_IND(0x%.4X, vif:%d)\n", skb, le16_to_cpu(fapi_header->id), le16_to_cpu(fapi_header->vif));
slsi_fw_test_process_frame(sdev, fwtest, skb, true);
break;
case MLME_DISCONNECTED_IND:
SLSI_DBG2(sdev, SLSI_FW_TEST, "0x%p: Process MLME_DISCONNECTED_IND(0x%.4X, vif:%d)\n", skb, le16_to_cpu(fapi_header->id), le16_to_cpu(fapi_header->vif));
slsi_fw_test_process_frame(sdev, fwtest, skb, true);
break;
case MLME_CONNECT_IND:
SLSI_DBG2(sdev, SLSI_FW_TEST, "0x%p: Process MLME_CONNECT_IND(0x%.4X, vif:%d)\n", skb, le16_to_cpu(fapi_header->id), le16_to_cpu(fapi_header->vif));
slsi_fw_test_process_frame(sdev, fwtest, skb, true);
break;
case MLME_CONNECTED_IND:
SLSI_DBG2(sdev, SLSI_FW_TEST, "0x%p: Process MLME_CONNECTED_IND(0x%.4X, vif:%d)\n", skb, le16_to_cpu(fapi_header->id), le16_to_cpu(fapi_header->vif));
slsi_fw_test_process_frame(sdev, fwtest, skb, true);
break;
case MLME_ROAMED_IND:
SLSI_DBG2(sdev, SLSI_FW_TEST, "0x%p: Process MLME_ROAMED_IND(0x%.4X, vif:%d)\n", skb, le16_to_cpu(fapi_header->id), le16_to_cpu(fapi_header->vif));
slsi_fw_test_process_frame(sdev, fwtest, skb, true);
break;
case MLME_TDLS_PEER_IND:
SLSI_DBG2(sdev, SLSI_FW_TEST, "0x%p: Process MLME_TDLS_PEER_IND(0x%.4X, vif:%d)\n", skb, le16_to_cpu(fapi_header->id), le16_to_cpu(fapi_header->vif));
slsi_fw_test_process_frame(sdev, fwtest, skb, true);
break;
case MLME_CONNECT_CFM:
SLSI_DBG2(sdev, SLSI_FW_TEST, "0x%p: Save MLME_CONNECT_CFM(0x%.4X, vif:%d)\n", skb, le16_to_cpu(fapi_header->id), le16_to_cpu(fapi_header->vif));
slsi_fw_test_save_frame(sdev, fwtest, fwtest->mlme_connect_cfm, skb, true);
break;
case MLME_PROCEDURE_STARTED_IND:
SLSI_DBG2(sdev, SLSI_FW_TEST, "0x%p: Save MLME_PROCEDURE_STARTED_IND(0x%.4X, vif:%d)\n", skb, le16_to_cpu(fapi_header->id), le16_to_cpu(fapi_header->vif));
slsi_fw_test_save_frame(sdev, fwtest, fwtest->mlme_procedure_started_ind, skb, true);
SLSI_DBG2(sdev, SLSI_FW_TEST, "0x%p: Process MLME_PROCEDURE_STARTED_IND(0x%.4X, vif:%d)\n", skb, le16_to_cpu(fapi_header->id), le16_to_cpu(fapi_header->vif));
slsi_fw_test_process_frame(sdev, fwtest, skb, true);
break;
case MLME_START_CFM:
SLSI_DBG2(sdev, SLSI_FW_TEST, "0x%p: Process MLME_START_CFM(0x%.4X, vif:%d)\n", skb, le16_to_cpu(fapi_header->id), le16_to_cpu(fapi_header->vif));
slsi_fw_test_process_frame(sdev, fwtest, skb, true);
sdev->device_config.ap_disconnect_ind_timeout = SLSI_DEFAULT_AP_DISCONNECT_IND_TIMEOUT;
break;
default:
break;
}
}
return 0;
}
static void slsi_fw_test_connect_station_roam(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct slsi_peer *peer = slsi_get_peer_from_qs(sdev, dev, SLSI_STA_PEER_QUEUESET);
struct ieee80211_mgmt *mgmt = fapi_get_mgmt(skb);
struct sk_buff *mlme_procedure_started_ind;
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "Station Connect(vif:%d) Roam\n", ndev_vif->ifnum);
if (WARN(!ndev_vif->is_fw_test, "!is_fw_test"))
return;
if (WARN(!ndev_vif->activated, "Not Activated"))
return;
if (WARN(ndev_vif->vif_type != FAPI_VIFTYPE_STATION, "Not Station Vif"))
return;
if (WARN(!peer, "peer not found"))
return;
slsi_spinlock_lock(&fwtest->fw_test_lock);
mlme_procedure_started_ind = fwtest->mlme_procedure_started_ind[ndev_vif->ifnum];
fwtest->mlme_procedure_started_ind[ndev_vif->ifnum] = NULL;
slsi_spinlock_unlock(&fwtest->fw_test_lock);
if (WARN(!mlme_procedure_started_ind, "mlme_procedure_started_ind not found"))
return;
slsi_rx_ba_stop_all(dev, peer);
SLSI_ETHER_COPY(peer->address, mgmt->bssid);
slsi_peer_update_assoc_req(sdev, dev, peer, mlme_procedure_started_ind);
slsi_peer_update_assoc_rsp(sdev, dev, peer, slsi_skb_copy(skb, GFP_KERNEL));
}
static void slsi_fw_test_connect_start_station(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
struct sk_buff *ind;
struct slsi_peer *peer;
u8 bssid[ETH_ALEN];
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "Station Connect Start(vif:%d)\n", ndev_vif->ifnum);
if (WARN(!ndev_vif->is_fw_test, "!is_fw_test"))
return;
if (WARN(ndev_vif->activated, "Already Activated"))
return;
slsi_spinlock_lock(&fwtest->fw_test_lock);
req = fwtest->mlme_connect_req[ndev_vif->ifnum];
cfm = fwtest->mlme_connect_cfm[ndev_vif->ifnum];
ind = fwtest->mlme_procedure_started_ind[ndev_vif->ifnum];
if (req)
SLSI_ETHER_COPY(bssid, fapi_get_buff(req, u.mlme_connect_req.bssid));
slsi_spinlock_unlock(&fwtest->fw_test_lock);
if (WARN(!req, "mlme_connect_req Not found"))
return;
if (WARN(!cfm, "mlme_connect_cfm Not found"))
return;
ndev_vif->iftype = NL80211_IFTYPE_STATION;
dev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION;
ndev_vif->vif_type = FAPI_VIFTYPE_STATION;
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "vif:%d slsi_vif_activated\n", ndev_vif->ifnum);
if (WARN(slsi_vif_activated(sdev, dev) != 0, "slsi_vif_activated() Failed"))
return;
peer = slsi_peer_add(sdev, dev, bssid, SLSI_STA_PEER_QUEUESET + 1);
if (WARN(!peer, "slsi_peer_add(%pM) Failed", bssid)) {
slsi_vif_deactivated(sdev, dev);
return;
}
slsi_peer_update_assoc_req(sdev, dev, peer, slsi_skb_copy(skb, GFP_KERNEL));
}
static void slsi_fw_test_connect_station(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *req;
struct sk_buff *cfm;
struct sk_buff *ind;
struct slsi_peer *peer;
u16 result;
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "Station Connect(vif:%d)\n", ndev_vif->ifnum);
if (WARN(!ndev_vif->is_fw_test, "!is_fw_test"))
return;
result = fapi_get_u16(skb, u.mlme_connect_ind.result_code);
slsi_spinlock_lock(&fwtest->fw_test_lock);
req = fwtest->mlme_connect_req[ndev_vif->ifnum];
cfm = fwtest->mlme_connect_cfm[ndev_vif->ifnum];
ind = fwtest->mlme_procedure_started_ind[ndev_vif->ifnum];
fwtest->mlme_connect_req[ndev_vif->ifnum] = NULL;
fwtest->mlme_connect_cfm[ndev_vif->ifnum] = NULL;
fwtest->mlme_procedure_started_ind[ndev_vif->ifnum] = NULL;
slsi_spinlock_unlock(&fwtest->fw_test_lock);
if (WARN(!req, "mlme_connect_req Not found"))
goto exit;
if (WARN(!cfm, "mlme_connect_cfm Not found"))
goto exit;
if (FAPI_RESULTCODE_SUCCESS == result &&
WARN(!ind, "mlme_procedure_started_ind Not found"))
goto exit;
if (FAPI_RESULTCODE_SUCCESS != result)
goto exit;
if (WARN(!ndev_vif->activated, "Not Activated"))
return;
peer = slsi_get_peer_from_mac(sdev, dev, fapi_get_buff(req, u.mlme_connect_req.bssid));
if (WARN(!peer, "slsi_get_peer_from_mac(%pM) Failed", fapi_get_buff(req, u.mlme_connect_req.bssid)))
goto exit;
slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_CONNECTED);
netif_carrier_on(dev);
exit:
slsi_kfree_skb(req);
slsi_kfree_skb(cfm);
slsi_kfree_skb(ind);
}
static void slsi_fw_test_started_network(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
u16 result = fapi_get_u16(skb, u.mlme_start_cfm.result_code);
SLSI_UNUSED_PARAMETER(fwtest);
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "Start Network(vif:%d)\n", ndev_vif->ifnum);
if (WARN(!ndev_vif->is_fw_test, "!is_fw_test"))
return;
if (WARN(ndev_vif->activated, "Already Activated"))
return;
ndev_vif->iftype = NL80211_IFTYPE_AP;
dev->ieee80211_ptr->iftype = NL80211_IFTYPE_AP;
ndev_vif->vif_type = FAPI_VIFTYPE_AP;
if (WARN(slsi_vif_activated(sdev, dev) != 0, "slsi_vif_activated() Failed"))
return;
if (FAPI_RESULTCODE_SUCCESS == result)
netif_carrier_on(dev);
}
static void slsi_fw_test_stop_network(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
SLSI_UNUSED_PARAMETER(fwtest);
SLSI_UNUSED_PARAMETER(skb);
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
if (!ndev_vif->is_fw_test)
return;
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "Stopping Network(vif:%d)\n", ndev_vif->ifnum);
if (WARN(!ndev_vif->activated, "Not Activated"))
return;
netif_carrier_off(dev);
slsi_vif_deactivated(sdev, dev);
}
static void slsi_fw_test_connect_start_ap(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct slsi_peer *peer = NULL;
struct ieee80211_mgmt *mgmt = fapi_get_mgmt(skb);
u16 peer_index;
SLSI_UNUSED_PARAMETER(fwtest);
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "Network Peer Connect Start(vif:%d)\n", ndev_vif->ifnum);
WARN(!ndev_vif->is_fw_test, "!is_fw_test");
if (WARN(!ndev_vif->activated, "Not Activated"))
return;
if (WARN_ON(!ieee80211_is_assoc_req(mgmt->frame_control) &&
!ieee80211_is_reassoc_req(mgmt->frame_control)))
return;
peer_index = fapi_get_u16(skb, u.mlme_procedure_started_ind.peer_index);
peer = slsi_peer_add(sdev, dev, mgmt->sa, peer_index);
if (WARN_ON(!peer))
return;
slsi_peer_update_assoc_req(sdev, dev, peer, slsi_skb_copy(skb, GFP_KERNEL));
peer->connected_state = SLSI_STA_CONN_STATE_CONNECTING;
}
static void slsi_fw_test_connected_network(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct slsi_peer *peer = NULL;
u16 peer_index = fapi_get_u16(skb, u.mlme_connected_ind.peer_index);
SLSI_UNUSED_PARAMETER(fwtest);
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "Network Peer Connect(vif:%d, peer_index:%d)\n", ndev_vif->ifnum, peer_index);
WARN(!ndev_vif->is_fw_test, "!is_fw_test");
if (WARN(!ndev_vif->activated, "Not Activated"))
return;
if (WARN_ON(peer_index > SLSI_PEER_INDEX_MAX))
return;
peer = slsi_get_peer_from_qs(sdev, dev, peer_index - 1);
if (WARN(!peer, "Peer(peer_index:%d) Not Found", peer_index))
return;
slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_CONNECTED);
peer->connected_state = SLSI_STA_CONN_STATE_CONNECTED;
slsi_rx_buffered_frames(sdev, dev, peer);
}
/* Setup the NetDev / Peers based on the saved frames */
static void slsi_fw_test_procedure_started_ind(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *add_vif_req;
u16 viftype = FAPI_VIFTYPE_STATION;
if (!ndev_vif->is_fw_test) {
slsi_kfree_skb(skb);
return;
}
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "ProceedureStarted(vif:%d)\n", ndev_vif->ifnum);
if (fapi_get_u16(skb, u.mlme_procedure_started_ind.procedure_type) != FAPI_PROCEDURETYPE_CONNECTION_STARTED) {
slsi_kfree_skb(skb);
return;
}
/* Set up the VIF and Data plane ready to go BUT do not open the control port */
slsi_spinlock_lock(&fwtest->fw_test_lock);
add_vif_req = fwtest->mlme_add_vif_req[ndev_vif->ifnum];
if (add_vif_req)
viftype = fapi_get_u16(add_vif_req, u.mlme_add_vif_req.virtual_interface_type);
slsi_spinlock_unlock(&fwtest->fw_test_lock);
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "Start UDI test NetDevice(vif:%d)\n", ndev_vif->ifnum);
if (WARN(!add_vif_req, "fwtest->mlme_add_vif_req[ndev_vif->ifnum] == NULL"))
goto out;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
switch (viftype) {
case FAPI_VIFTYPE_STATION:
slsi_fw_test_connect_start_station(sdev, dev, fwtest, skb);
break;
case FAPI_VIFTYPE_AP:
slsi_fw_test_connect_start_ap(sdev, dev, fwtest, skb);
break;
default:
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "vif:%d virtual_interface_type:%d NOT SUPPORTED", ndev_vif->ifnum, viftype);
break;
}
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
out:
slsi_kfree_skb(skb);
}
/* Setup the NetDev / Peers based on the saved frames */
static void slsi_fw_test_connect_ind(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *add_vif_req;
u16 viftype = FAPI_VIFTYPE_STATION;
if (!ndev_vif->is_fw_test) {
slsi_kfree_skb(skb);
return;
}
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "Network Peer Connect(vif:%d)\n", ndev_vif->ifnum);
slsi_spinlock_lock(&fwtest->fw_test_lock);
add_vif_req = fwtest->mlme_add_vif_req[ndev_vif->ifnum];
if (add_vif_req)
viftype = fapi_get_u16(add_vif_req, u.mlme_add_vif_req.virtual_interface_type);
slsi_spinlock_unlock(&fwtest->fw_test_lock);
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "Start UDI test NetDevice(vif:%d)\n", ndev_vif->ifnum);
if (WARN(!add_vif_req, "fwtest->mlme_add_vif_req[ndev_vif->ifnum] == NULL"))
goto out;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
switch (viftype) {
case FAPI_VIFTYPE_STATION:
slsi_fw_test_connect_station(sdev, dev, fwtest, skb);
break;
default:
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "vif:%d virtual_interface_type:%d NOT SUPPORTED", ndev_vif->ifnum, viftype);
break;
}
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
out:
slsi_kfree_skb(skb);
}
static void slsi_fw_test_connected_ind(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *add_vif_req;
u16 viftype = FAPI_VIFTYPE_STATION;
if (!ndev_vif->is_fw_test) {
slsi_kfree_skb(skb);
return;
}
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "Connected(vif:%d)\n", ndev_vif->ifnum);
slsi_spinlock_lock(&fwtest->fw_test_lock);
add_vif_req = fwtest->mlme_add_vif_req[ndev_vif->ifnum];
if (add_vif_req)
viftype = fapi_get_u16(add_vif_req, u.mlme_add_vif_req.virtual_interface_type);
slsi_spinlock_unlock(&fwtest->fw_test_lock);
if (WARN(!add_vif_req, "fwtest->mlme_add_vif_req[ndev_vif->ifnum] == NULL"))
goto out;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
switch (viftype) {
case FAPI_VIFTYPE_AP:
slsi_fw_test_connected_network(sdev, dev, fwtest, skb);
break;
default:
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "vif:%d virtual_interface_type:%d NOT SUPPORTED", ndev_vif->ifnum, viftype);
break;
}
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
out:
slsi_kfree_skb(skb);
}
static void slsi_fw_test_roamed_ind(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *add_vif_req;
u16 viftype = FAPI_VIFTYPE_STATION;
if (!ndev_vif->is_fw_test) {
slsi_kfree_skb(skb);
return;
}
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "Roamed(vif:%d)\n", ndev_vif->ifnum);
slsi_spinlock_lock(&fwtest->fw_test_lock);
add_vif_req = fwtest->mlme_add_vif_req[ndev_vif->ifnum];
if (add_vif_req)
viftype = fapi_get_u16(add_vif_req, u.mlme_add_vif_req.virtual_interface_type);
slsi_spinlock_unlock(&fwtest->fw_test_lock);
if (WARN(!add_vif_req, "fwtest->mlme_add_vif_req[ndev_vif->ifnum] == NULL"))
goto out;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
switch (viftype) {
case FAPI_VIFTYPE_STATION:
slsi_fw_test_connect_station_roam(sdev, dev, fwtest, skb);
break;
default:
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "vif:%d virtual_interface_type:%d NOT SUPPORTED", ndev_vif->ifnum, viftype);
break;
}
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
out:
slsi_kfree_skb(skb);
}
static void slsi_fw_test_disconnect_station(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct slsi_peer *peer = slsi_get_peer_from_qs(sdev, dev, SLSI_STA_PEER_QUEUESET);
SLSI_UNUSED_PARAMETER(fwtest);
SLSI_UNUSED_PARAMETER(skb);
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
if (!ndev_vif->is_fw_test)
return;
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "Station Disconnect(vif:%d)\n", ndev_vif->ifnum);
if (WARN(!ndev_vif->activated, "Not Activated"))
return;
netif_carrier_off(dev);
if (peer) {
slsi_spinlock_lock(&ndev_vif->peer_lock);
slsi_peer_remove(sdev, dev, peer);
slsi_spinlock_unlock(&ndev_vif->peer_lock);
}
slsi_vif_deactivated(sdev, dev);
}
static void slsi_fw_test_disconnect_network(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
/* Find the peer based on MAC address, mlme-disconnect-ind and mlme-disconnected-ind
* both have the MAC address in the same position.
*/
struct slsi_peer *peer = slsi_get_peer_from_mac(sdev, dev, fapi_get_buff(skb, u.mlme_disconnect_ind.peer_sta_address));
SLSI_UNUSED_PARAMETER(fwtest);
WARN_ON(!SLSI_MUTEX_IS_LOCKED(ndev_vif->vif_mutex));
if (!ndev_vif->is_fw_test)
return;
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "Network Peer Disconnect(vif:%d)\n", ndev_vif->ifnum);
if (peer) {
slsi_spinlock_lock(&ndev_vif->peer_lock);
slsi_peer_remove(sdev, dev, peer);
slsi_spinlock_unlock(&ndev_vif->peer_lock);
}
}
static void slsi_fw_test_disconnected_ind(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *add_vif_req;
u16 viftype = FAPI_VIFTYPE_STATION;
if (!ndev_vif->is_fw_test) {
slsi_kfree_skb(skb);
return;
}
slsi_spinlock_lock(&fwtest->fw_test_lock);
add_vif_req = fwtest->mlme_add_vif_req[ndev_vif->ifnum];
if (add_vif_req)
viftype = fapi_get_u16(add_vif_req, u.mlme_add_vif_req.virtual_interface_type);
slsi_spinlock_unlock(&fwtest->fw_test_lock);
if (WARN(!add_vif_req, "fwtest->mlme_add_vif_req[ndev_vif->ifnum] == NULL"))
goto out;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
switch (viftype) {
case FAPI_VIFTYPE_STATION:
slsi_fw_test_disconnect_station(sdev, dev, fwtest, skb);
break;
case FAPI_VIFTYPE_AP:
slsi_fw_test_disconnect_network(sdev, dev, fwtest, skb);
break;
default:
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "vif:%d virtual_interface_type:%d NOT SUPPORTED", ndev_vif->ifnum, viftype);
break;
}
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
out:
slsi_kfree_skb(skb);
}
static void slsi_fw_test_tdls_event_connected(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb)
{
struct slsi_peer *peer = NULL;
struct netdev_vif *ndev_vif = netdev_priv(dev);
u16 peer_index = fapi_get_u16(skb, u.mlme_tdls_peer_ind.peer_index);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
ndev_vif->sta.tdls_enabled = true;
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "TDLS connect (vif:%d, peer_index:%d, mac:%pM)\n", fapi_get_vif(skb), peer_index, fapi_get_buff(skb, u.mlme_tdls_peer_ind.peer_sta_address));
if ((ndev_vif->sta.tdls_peer_sta_records) + 1 > SLSI_TDLS_PEER_CONNECTIONS_MAX) {
SLSI_NET_ERR(dev, "max TDLS limit reached (peer_index:%d)\n", peer_index);
goto out;
}
if (peer_index < SLSI_TDLS_PEER_INDEX_MIN || peer_index > SLSI_TDLS_PEER_INDEX_MAX) {
SLSI_NET_ERR(dev, "incorrect index (peer_index:%d)\n", peer_index);
goto out;
}
peer = slsi_peer_add(sdev, dev, fapi_get_buff(skb, u.mlme_tdls_peer_ind.peer_sta_address), peer_index);
if (!peer) {
SLSI_NET_ERR(dev, "peer add failed\n");
goto out;
}
/* QoS is mandatory for TDLS - enable QoS for TDLS peer by default */
peer->qos_enabled = true;
slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_CONNECTED);
/* move TDLS packets from STA Q to TDLS Q */
slsi_tdls_move_packets(sdev, dev, ndev_vif->peer_sta_record[SLSI_STA_PEER_QUEUESET], peer, true);
out:
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
}
static void slsi_fw_test_tdls_event_disconnected(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb)
{
struct slsi_peer *peer = NULL;
struct netdev_vif *ndev_vif = netdev_priv(dev);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
SLSI_NET_DBG1(dev, SLSI_MLME, "TDLS dis-connect (vif:%d, mac:%pM)\n", ndev_vif->ifnum, fapi_get_buff(skb, u.mlme_tdls_peer_ind.peer_sta_address));
slsi_spinlock_lock(&ndev_vif->tcp_ack_lock);
slsi_spinlock_lock(&ndev_vif->peer_lock);
peer = slsi_get_peer_from_mac(sdev, dev, fapi_get_buff(skb, u.mlme_tdls_peer_ind.peer_sta_address));
if (!peer || (peer->aid == 0)) {
WARN_ON(!peer || (peer->aid == 0));
SLSI_NET_DBG1(dev, SLSI_MLME, "can't find peer by MAC address\n");
goto out;
}
slsi_ps_port_control(sdev, dev, peer, SLSI_STA_CONN_STATE_DISCONNECTED);
/* move TDLS packets from TDLS Q to STA Q */
slsi_tdls_move_packets(sdev, dev, ndev_vif->peer_sta_record[SLSI_STA_PEER_QUEUESET], peer, false);
slsi_peer_remove(sdev, dev, peer);
out:
slsi_spinlock_unlock(&ndev_vif->peer_lock);
slsi_spinlock_unlock(&ndev_vif->tcp_ack_lock);
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
}
static void slsi_fw_test_tdls_peer_ind(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *add_vif_req;
u16 vif_type = 0;
u16 tdls_event;
if (!ndev_vif->is_fw_test) {
slsi_kfree_skb(skb);
return;
}
if (WARN(!ndev_vif->activated, "Not Activated")) {
slsi_kfree_skb(skb);
return;
}
slsi_spinlock_lock(&fwtest->fw_test_lock);
add_vif_req = fwtest->mlme_add_vif_req[ndev_vif->ifnum];
if (add_vif_req)
vif_type = fapi_get_u16(add_vif_req, u.mlme_add_vif_req.virtual_interface_type);
slsi_spinlock_unlock(&fwtest->fw_test_lock);
if (WARN(vif_type != FAPI_VIFTYPE_STATION, "Not STA VIF")) {
slsi_kfree_skb(skb);
return;
}
tdls_event = fapi_get_u16(skb, u.mlme_tdls_peer_ind.tdls_event);
SLSI_NET_DBG1(dev, SLSI_MLME, "TDLS peer(vif:%d tdls_event:%d)\n", ndev_vif->ifnum, tdls_event);
switch (tdls_event) {
case FAPI_TDLSEVENT_CONNECTED:
slsi_fw_test_tdls_event_connected(sdev, dev, skb);
break;
case FAPI_TDLSEVENT_DISCONNECTED:
slsi_fw_test_tdls_event_disconnected(sdev, dev, skb);
break;
case FAPI_TDLSEVENT_DISCOVERED:
/* nothing to do */
break;
default:
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "vif:%d tdls_event:%d not supported\n", ndev_vif->ifnum, tdls_event);
break;
}
slsi_kfree_skb(skb);
}
/* Setup the NetDev */
static void slsi_fw_test_start_cfm(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *add_vif_req;
u16 viftype = FAPI_VIFTYPE_UNSYNCHRONISED;
if (!ndev_vif->is_fw_test) {
slsi_kfree_skb(skb);
return;
}
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "Network Start(vif:%d)\n", ndev_vif->ifnum);
slsi_spinlock_lock(&fwtest->fw_test_lock);
add_vif_req = fwtest->mlme_add_vif_req[ndev_vif->ifnum];
if (add_vif_req)
viftype = fapi_get_u16(add_vif_req, u.mlme_add_vif_req.virtual_interface_type);
slsi_spinlock_unlock(&fwtest->fw_test_lock);
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "Start UDI test NetDevice(vif:%d)\n", ndev_vif->ifnum);
if (WARN(!add_vif_req, "fwtest->mlme_add_vif_req[ndev_vif->ifnum] == NULL"))
goto out;
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
switch (viftype) {
case FAPI_VIFTYPE_AP:
slsi_fw_test_started_network(sdev, dev, fwtest, skb);
break;
default:
SLSI_NET_DBG1(dev, SLSI_FW_TEST, "vif:%d virtual_interface_type:%d NOT SUPPORTED", ndev_vif->ifnum, viftype);
break;
}
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
out:
slsi_kfree_skb(skb);
}
static void slsi_fw_test_add_vif_req(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
SLSI_UNUSED_PARAMETER(sdev);
SLSI_UNUSED_PARAMETER(fwtest);
SLSI_DBG1(sdev, SLSI_FW_TEST, "Mark UDI test NetDevice(vif:%d)\n", fapi_get_vif(skb));
ndev_vif->is_fw_test = true;
slsi_kfree_skb(skb);
}
static void slsi_fw_test_del_vif_req(struct slsi_dev *sdev, struct net_device *dev, struct slsi_fw_test *fwtest, struct sk_buff *skb)
{
struct netdev_vif *ndev_vif = netdev_priv(dev);
struct sk_buff *add_vif_req;
u16 viftype = FAPI_VIFTYPE_UNSYNCHRONISED;
SLSI_DBG1(sdev, SLSI_FW_TEST, "Unmark UDI test NetDevice(vif:%d)\n", fapi_get_vif(skb));
slsi_spinlock_lock(&fwtest->fw_test_lock);
add_vif_req = fwtest->mlme_add_vif_req[ndev_vif->ifnum];
if (add_vif_req)
viftype = fapi_get_u16(add_vif_req, u.mlme_add_vif_req.virtual_interface_type);
slsi_kfree_skb(fwtest->mlme_add_vif_req[ndev_vif->ifnum]);
slsi_kfree_skb(fwtest->mlme_connect_req[ndev_vif->ifnum]);
slsi_kfree_skb(fwtest->mlme_connect_cfm[ndev_vif->ifnum]);
slsi_kfree_skb(fwtest->mlme_procedure_started_ind[ndev_vif->ifnum]);
fwtest->mlme_add_vif_req[ndev_vif->ifnum] = NULL;
fwtest->mlme_connect_req[ndev_vif->ifnum] = NULL;
fwtest->mlme_connect_cfm[ndev_vif->ifnum] = NULL;
fwtest->mlme_procedure_started_ind[ndev_vif->ifnum] = NULL;
slsi_spinlock_unlock(&fwtest->fw_test_lock);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
switch (viftype) {
/* As there is no specific MLME primitive for shutting down the network
* perform an actions on the MLME-DEL-VIF.
*/
case FAPI_VIFTYPE_AP:
slsi_fw_test_stop_network(sdev, dev, fwtest, skb);
break;
default:
if (ndev_vif->is_fw_test && ndev_vif->activated) {
netif_carrier_off(dev);
slsi_vif_deactivated(sdev, dev);
}
break;
}
ndev_vif->is_fw_test = false;
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
slsi_kfree_skb(skb);
}
void slsi_fw_test_work(struct work_struct *work)
{
struct slsi_fw_test *fw_test = container_of(work, struct slsi_fw_test, fw_test_work.work);
struct slsi_dev *sdev = fw_test->sdev;
struct sk_buff *skb = slsi_skb_work_dequeue(&fw_test->fw_test_work);
struct net_device *dev;
while (skb) {
u16 vif = fapi_get_vif(skb);
SLSI_DBG3(sdev, SLSI_FW_TEST, "0x%p: Signal:0x%.4X, vif:%d\n", skb, fapi_get_sigid(skb), vif);
if (WARN(!vif, "!vif")) {
slsi_kfree_skb(skb);
skb = slsi_skb_work_dequeue(&fw_test->fw_test_work);
continue;
}
SLSI_MUTEX_LOCK(sdev->netdev_add_remove_mutex);
dev = slsi_get_netdev_locked(sdev, vif);
if (!dev) {
/* Just ignore the signal. This is valid in some error testing scenarios*/
SLSI_MUTEX_UNLOCK(sdev->netdev_add_remove_mutex);
slsi_kfree_skb(skb);
skb = slsi_skb_work_dequeue(&fw_test->fw_test_work);
continue;
}
switch (fapi_get_sigid(skb)) {
case MLME_PROCEDURE_STARTED_IND:
slsi_fw_test_procedure_started_ind(sdev, dev, fw_test, skb);
break;
case MLME_CONNECT_IND:
slsi_fw_test_connect_ind(sdev, dev, fw_test, skb);
break;
case MLME_ROAMED_IND:
slsi_fw_test_roamed_ind(sdev, dev, fw_test, skb);
break;
case MLME_CONNECTED_IND:
slsi_fw_test_connected_ind(sdev, dev, fw_test, skb);
break;
case MLME_DISCONNECT_IND:
case MLME_DISCONNECTED_IND:
slsi_fw_test_disconnected_ind(sdev, dev, fw_test, skb);
break;
case MLME_TDLS_PEER_IND:
slsi_fw_test_tdls_peer_ind(sdev, dev, fw_test, skb);
break;
case MLME_START_CFM:
slsi_fw_test_start_cfm(sdev, dev, fw_test, skb);
break;
case MLME_ADD_VIF_REQ:
slsi_fw_test_add_vif_req(sdev, dev, fw_test, skb);
break;
case MLME_DEL_VIF_REQ:
slsi_fw_test_del_vif_req(sdev, dev, fw_test, skb);
break;
default:
WARN(1, "Unhandled Signal");
slsi_kfree_skb(skb);
break;
}
SLSI_MUTEX_UNLOCK(sdev->netdev_add_remove_mutex);
skb = slsi_skb_work_dequeue(&fw_test->fw_test_work);
}
}
void slsi_fw_test_init(struct slsi_dev *sdev, struct slsi_fw_test *fwtest)
{
SLSI_DBG1(sdev, SLSI_FW_TEST, "\n");
memset(fwtest, 0x00, sizeof(struct slsi_fw_test));
fwtest->sdev = sdev;
slsi_spinlock_create(&fwtest->fw_test_lock);
slsi_skb_work_init(sdev, NULL, &fwtest->fw_test_work, "slsi_wlan_fw_test", slsi_fw_test_work);
}
void slsi_fw_test_deinit(struct slsi_dev *sdev, struct slsi_fw_test *fwtest)
{
int i;
SLSI_UNUSED_PARAMETER(sdev);
SLSI_DBG1(sdev, SLSI_FW_TEST, "\n");
fwtest->fw_test_enabled = false;
slsi_skb_work_deinit(&fwtest->fw_test_work);
slsi_spinlock_lock(&fwtest->fw_test_lock);
for (i = 1; i <= CONFIG_SCSC_WLAN_MAX_INTERFACES; i++) {
slsi_kfree_skb(fwtest->mlme_add_vif_req[i]);
slsi_kfree_skb(fwtest->mlme_connect_req[i]);
slsi_kfree_skb(fwtest->mlme_connect_cfm[i]);
slsi_kfree_skb(fwtest->mlme_procedure_started_ind[i]);
fwtest->mlme_add_vif_req[i] = NULL;
fwtest->mlme_connect_req[i] = NULL;
fwtest->mlme_connect_cfm[i] = NULL;
fwtest->mlme_procedure_started_ind[i] = NULL;
}
slsi_spinlock_unlock(&fwtest->fw_test_lock);
memset(fwtest, 0x00, sizeof(struct slsi_fw_test));
}