blob: 5d2ef5751d5adc6d4fc10db127d2864b73e2ba6c [file] [log] [blame]
/****************************************************************************
*
* Copyright (c) 2014 - 2019 Samsung Electronics Co., Ltd. All rights reserved
*
****************************************************************************/
#include <linux/types.h>
#include "debug.h"
#include "dev.h"
#include "sap.h"
#include "sap_mlme.h"
#include "hip.h"
#include "mgt.h"
#include "scsc_wifilogger_rings.h"
#include "nl80211_vendor.h"
#include "mlme.h"
#define SUPPORTED_OLD_VERSION 0
static int sap_mlme_version_supported(u16 version);
static int sap_mlme_rx_handler(struct slsi_dev *sdev, struct sk_buff *skb);
static int sap_mlme_notifier(struct slsi_dev *sdev, unsigned long event);
static struct sap_api sap_mlme = {
.sap_class = SAP_MLME,
.sap_version_supported = sap_mlme_version_supported,
.sap_handler = sap_mlme_rx_handler,
.sap_versions = { FAPI_CONTROL_SAP_VERSION, SUPPORTED_OLD_VERSION },
.sap_notifier = sap_mlme_notifier,
};
static int sap_mlme_notifier(struct slsi_dev *sdev, unsigned long event)
{
int i;
#ifdef CONFIG_SLSI_WLAN_STA_FWD_BEACON
struct net_device *dev;
#endif
struct netdev_vif *ndev_vif;
SLSI_INFO_NODEV("Notifier event received: %lu\n", event);
if (event >= SCSC_MAX_NOTIFIER)
return -EIO;
switch (event) {
case SCSC_WIFI_STOP:
/* Stop sending signals down*/
sdev->mlme_blocked = true;
SLSI_INFO_NODEV("MLME BLOCKED\n");
/* cleanup all the VIFs and scan data */
SLSI_MUTEX_LOCK(sdev->netdev_add_remove_mutex);
complete_all(&sdev->sig_wait.completion);
for (i = 1; i <= CONFIG_SCSC_WLAN_MAX_INTERFACES; i++)
if (sdev->netdev[i]) {
ndev_vif = netdev_priv(sdev->netdev[i]);
slsi_scan_cleanup(sdev, sdev->netdev[i]);
if (cancel_work_sync(&ndev_vif->set_multicast_filter_work))
slsi_wakeunlock(&sdev->wlan_wl);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
slsi_vif_cleanup(sdev, sdev->netdev[i], 0);
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
}
SLSI_MUTEX_UNLOCK(sdev->netdev_add_remove_mutex);
SLSI_INFO_NODEV("Force cleaned all VIFs\n");
break;
case SCSC_WIFI_FAILURE_RESET:
break;
case SCSC_WIFI_SUSPEND:
break;
case SCSC_WIFI_RESUME:
#ifdef CONFIG_SLSI_WLAN_STA_FWD_BEACON
dev = slsi_get_netdev(sdev, SLSI_NET_INDEX_WLAN);
ndev_vif = netdev_priv(dev);
SLSI_MUTEX_LOCK(ndev_vif->vif_mutex);
if ((ndev_vif->is_wips_running) && (ndev_vif->activated) &&
(ndev_vif->vif_type == FAPI_VIFTYPE_STATION) &&
(ndev_vif->sta.vif_status == SLSI_VIF_STATUS_CONNECTED)) {
ndev_vif->is_wips_running = false;
slsi_send_forward_beacon_abort_vendor_event(sdev, SLSI_FORWARD_BEACON_ABORT_REASON_SUSPENDED);
SLSI_INFO_NODEV("FORWARD_BEACON: SUSPEND_RESUMED!! send abort event\n");
}
SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex);
#endif
break;
default:
SLSI_INFO_NODEV("Unknown event code %lu\n", event);
break;
}
return 0;
}
static int sap_mlme_version_supported(u16 version)
{
unsigned int major = SAP_MAJOR(version);
unsigned int minor = SAP_MINOR(version);
u8 i = 0;
SLSI_INFO_NODEV("Reported version: %d.%d\n", major, minor);
for (i = 0; i < SAP_MAX_VER; i++)
if (SAP_MAJOR(sap_mlme.sap_versions[i]) == major)
return 0;
SLSI_ERR_NODEV("Version %d.%d Not supported\n", major, minor);
return -EINVAL;
}
static int slsi_rx_netdev_mlme(struct slsi_dev *sdev, struct net_device *dev, struct sk_buff *skb)
{
u16 id = fapi_get_u16(skb, id);
/* The skb is consumed by the functions called.
*/
switch (id) {
case MLME_SCAN_IND:
slsi_rx_scan_ind(sdev, dev, skb);
break;
case MLME_SCAN_DONE_IND:
slsi_rx_scan_done_ind(sdev, dev, skb);
break;
case MLME_CONNECT_IND:
slsi_rx_connect_ind(sdev, dev, skb);
break;
case MLME_CONNECTED_IND:
slsi_rx_connected_ind(sdev, dev, skb);
break;
case MLME_RECEIVED_FRAME_IND:
slsi_rx_received_frame_ind(sdev, dev, skb);
break;
case MLME_DISCONNECT_IND:
slsi_rx_disconnect_ind(sdev, dev, skb);
break;
case MLME_DISCONNECTED_IND:
slsi_rx_disconnected_ind(sdev, dev, skb);
break;
case MLME_PROCEDURE_STARTED_IND:
slsi_rx_procedure_started_ind(sdev, dev, skb);
break;
case MLME_FRAME_TRANSMISSION_IND:
slsi_rx_frame_transmission_ind(sdev, dev, skb);
break;
case MA_BLOCKACK_IND:
slsi_rx_blockack_ind(sdev, dev, skb);
break;
case MLME_ROAMED_IND:
slsi_rx_roamed_ind(sdev, dev, skb);
break;
case MLME_ROAM_IND:
slsi_rx_roam_ind(sdev, dev, skb);
break;
case MLME_MIC_FAILURE_IND:
slsi_rx_mic_failure_ind(sdev, dev, skb);
break;
case MLME_REASSOCIATE_IND:
slsi_rx_reassoc_ind(sdev, dev, skb);
break;
case MLME_TDLS_PEER_IND:
slsi_tdls_peer_ind(sdev, dev, skb);
break;
case MLME_LISTEN_END_IND:
slsi_rx_listen_end_ind(dev, skb);
break;
case MLME_CHANNEL_SWITCHED_IND:
slsi_rx_channel_switched_ind(sdev, dev, skb);
break;
case MLME_AC_PRIORITY_UPDATE_IND:
SLSI_DBG1(sdev, SLSI_MLME, "Unexpected MLME_AC_PRIORITY_UPDATE_IND\n");
slsi_kfree_skb(skb);
break;
#ifdef CONFIG_SCSC_WLAN_GSCAN_ENABLE
case MLME_RSSI_REPORT_IND:
slsi_rx_rssi_report_ind(sdev, dev, skb);
break;
case MLME_RANGE_IND:
slsi_rx_range_ind(sdev, dev, skb);
break;
case MLME_RANGE_DONE_IND:
slsi_rx_range_done_ind(sdev, dev, skb);
break;
case MLME_EVENT_LOG_IND:
slsi_rx_event_log_indication(sdev, dev, skb);
break;
#endif
#ifdef CONFIG_SCSC_WIFI_NAN_ENABLE
case MLME_NAN_EVENT_IND:
slsi_nan_event(sdev, dev, skb);
break;
case MLME_NAN_FOLLOWUP_IND:
slsi_nan_followup_ind(sdev, dev, skb);
break;
case MLME_NAN_SERVICE_IND:
slsi_nan_service_ind(sdev, dev, skb);
break;
case MLME_NDP_REQUEST_IND:
slsi_nan_ndp_setup_ind(sdev, dev, skb, true);
break;
case MLME_NDP_REQUESTED_IND:
slsi_nan_ndp_requested_ind(sdev, dev, skb);
break;
case MLME_NDP_RESPONSE_IND:
slsi_nan_ndp_setup_ind(sdev, dev, skb, false);
break;
case MLME_NDP_TERMINATE_IND:
slsi_nan_ndp_termination_ind(sdev, dev, skb, false);
break;
case MLME_NDP_TERMINATED_IND:
slsi_nan_ndp_termination_ind(sdev, dev, skb, true);
break;
#endif
#ifdef CONFIG_SCSC_WLAN_SAE_CONFIG
case MLME_SYNCHRONISED_IND:
slsi_rx_synchronised_ind(sdev, dev, skb);
break;
#endif
#ifdef CONFIG_SLSI_WLAN_STA_FWD_BEACON
case MLME_BEACON_REPORTING_EVENT_IND:
slsi_rx_beacon_reporting_event_ind(sdev, dev, skb);
break;
#endif
default:
slsi_kfree_skb(skb);
SLSI_NET_ERR(dev, "Unhandled Ind: 0x%.4x\n", id);
break;
}
return 0;
}
void slsi_rx_netdev_mlme_work(struct work_struct *work)
{
struct slsi_skb_work *w = container_of(work, struct slsi_skb_work, work);
struct slsi_dev *sdev = w->sdev;
struct net_device *dev = w->dev;
struct sk_buff *skb = slsi_skb_work_dequeue(w);
if (WARN_ON(!dev))
return;
slsi_wakelock(&sdev->wlan_wl);
while (skb) {
slsi_debug_frame(sdev, dev, skb, "RX");
slsi_rx_netdev_mlme(sdev, dev, skb);
skb = slsi_skb_work_dequeue(w);
}
slsi_wakeunlock(&sdev->wlan_wl);
}
int slsi_rx_enqueue_netdev_mlme(struct slsi_dev *sdev, struct sk_buff *skb, u16 vif)
{
struct net_device *dev;
struct netdev_vif *ndev_vif;
rcu_read_lock();
dev = slsi_get_netdev_rcu(sdev, vif);
/* in case of del_vif failure, we may get mlme signals for a netdev which is already removed */
if (!dev) {
SLSI_WARN(sdev, "dev is NULL");
kfree_skb(skb);
rcu_read_unlock();
return 0;
}
ndev_vif = netdev_priv(dev);
if (unlikely(ndev_vif->is_fw_test) &&
likely(fapi_get_sigid(skb) != MA_BLOCKACK_IND)) {
slsi_kfree_skb(skb);
rcu_read_unlock();
return 0;
}
slsi_skb_work_enqueue(&ndev_vif->rx_mlme, skb);
rcu_read_unlock();
return 0;
}
static int slsi_rx_action_enqueue_netdev_mlme(struct slsi_dev *sdev, struct sk_buff *skb, u16 vif)
{
struct net_device *dev;
struct netdev_vif *ndev_vif;
rcu_read_lock();
dev = slsi_get_netdev_rcu(sdev, vif);
if (WARN_ON(!dev)) {
rcu_read_unlock();
return -ENODEV;
}
ndev_vif = netdev_priv(dev);
if (unlikely(ndev_vif->is_fw_test)) {
rcu_read_unlock();
return 0;
}
if (ndev_vif->iftype == NL80211_IFTYPE_P2P_GO || ndev_vif->iftype == NL80211_IFTYPE_P2P_CLIENT) {
struct ieee80211_mgmt *mgmt = fapi_get_mgmt(skb);
/* Check the DA of received action frame with the GO interface address */
if (memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) != 0) {
/* If not equal, compare DA of received action frame with the P2P DEV address*/
struct net_device *p2pdev = slsi_get_netdev_rcu(sdev, SLSI_NET_INDEX_P2P);
if (WARN_ON(!p2pdev)) {
rcu_read_unlock();
return -ENODEV;
}
if (memcmp(mgmt->da, p2pdev->dev_addr, ETH_ALEN) == 0) {
/* If destination address is equal to P2P DEV ADDR, then action frame is received on
* GO interface. Hence indicate action frames on P2P DEV
*/
ndev_vif = netdev_priv(p2pdev);
if (unlikely(ndev_vif->is_fw_test)) {
slsi_kfree_skb(skb);
rcu_read_unlock();
return 0;
}
}
}
}
slsi_skb_work_enqueue(&ndev_vif->rx_mlme, skb);
rcu_read_unlock();
return 0;
}
static int sap_mlme_rx_handler(struct slsi_dev *sdev, struct sk_buff *skb)
{
u16 scan_id;
u16 vif = fapi_get_vif(skb);
if (slsi_rx_blocking_signals(sdev, skb) == 0)
return 0;
if (fapi_is_ind(skb)) {
SCSC_WLOG_PKTFATE_LOG_RX_CTRL_FRAME(fapi_get_data(skb), fapi_get_datalen(skb));
switch (fapi_get_sigid(skb)) {
case MLME_SCAN_DONE_IND:
scan_id = fapi_get_u16(skb, u.mlme_scan_done_ind.scan_id);
#ifdef CONFIG_SCSC_WLAN_GSCAN_ENABLE
if (slsi_is_gscan_id(scan_id))
return slsi_rx_enqueue_netdev_mlme(sdev, skb, SLSI_NET_INDEX_WLAN);
#endif
return slsi_rx_enqueue_netdev_mlme(sdev, skb, (scan_id >> 8));
case MLME_SCAN_IND:
if (vif)
return slsi_rx_enqueue_netdev_mlme(sdev, skb, vif);
scan_id = fapi_get_u16(skb, u.mlme_scan_ind.scan_id);
#ifdef CONFIG_SCSC_WLAN_GSCAN_ENABLE
if (slsi_is_gscan_id(scan_id))
return slsi_rx_enqueue_netdev_mlme(sdev, skb, SLSI_NET_INDEX_WLAN);
#endif
return slsi_rx_enqueue_netdev_mlme(sdev, skb, (scan_id >> 8));
case MLME_RECEIVED_FRAME_IND:
if (vif == 0) {
SLSI_WARN(sdev, "Received MLME_RECEIVED_FRAME_IND on VIF 0\n");
goto err;
}
return slsi_rx_action_enqueue_netdev_mlme(sdev, skb, vif);
#ifdef CONFIG_SCSC_WLAN_GSCAN_ENABLE
#ifdef CONFIG_SCSC_WIFI_NAN_ENABLE
case MLME_NAN_EVENT_IND:
case MLME_NAN_FOLLOWUP_IND:
case MLME_NAN_SERVICE_IND:
case MLME_NDP_REQUEST_IND:
case MLME_NDP_REQUESTED_IND:
case MLME_NDP_RESPONSE_IND:
case MLME_NDP_TERMINATE_IND:
case MLME_NDP_TERMINATED_IND:
return slsi_rx_enqueue_netdev_mlme(sdev, skb, vif);
#endif
case MLME_RANGE_IND:
case MLME_RANGE_DONE_IND:
if (vif == 0)
return slsi_rx_enqueue_netdev_mlme(sdev, skb, SLSI_NET_INDEX_WLAN);
else
return slsi_rx_enqueue_netdev_mlme(sdev, skb, vif);
#endif
#ifdef CONFIG_SCSC_WLAN_ENHANCED_LOGGING
case MLME_EVENT_LOG_IND:
return slsi_rx_enqueue_netdev_mlme(sdev, skb, SLSI_NET_INDEX_WLAN);
#endif
case MLME_ROAMED_IND:
if (vif == 0) {
SLSI_WARN(sdev, "Received MLME_ROAMED_IND on VIF 0, return error\n");
goto err;
} else {
struct net_device *dev;
struct netdev_vif *ndev_vif;
rcu_read_lock();
dev = slsi_get_netdev_rcu(sdev, vif);
if (WARN_ON(!dev)) {
rcu_read_unlock();
return -ENODEV;
}
ndev_vif = netdev_priv(dev);
if (atomic_read(&ndev_vif->sta.drop_roamed_ind)) {
/* If roam cfm is not received for the req, ignore this roamed indication. */
slsi_kfree_skb(skb);
rcu_read_unlock();
return 0;
}
rcu_read_unlock();
return slsi_rx_enqueue_netdev_mlme(sdev, skb, vif);
}
default:
if (vif == 0) {
SLSI_WARN(sdev, "Received signal 0x%04x on VIF 0, return error\n", fapi_get_sigid(skb));
goto err;
} else {
return slsi_rx_enqueue_netdev_mlme(sdev, skb, vif);
}
}
}
if (WARN_ON(fapi_is_req(skb)))
goto err;
if (slsi_is_test_mode_enabled()) {
slsi_kfree_skb(skb);
return 0;
}
WARN_ON(1);
err:
return -EINVAL;
}
int sap_mlme_init(void)
{
SLSI_INFO_NODEV("Registering SAP\n");
slsi_hip_sap_register(&sap_mlme);
return 0;
}
int sap_mlme_deinit(void)
{
SLSI_INFO_NODEV("Unregistering SAP\n");
slsi_hip_sap_unregister(&sap_mlme);
return 0;
}