/***************************************************************************** * * Copyright (c) 2019 Samsung Electronics Co., Ltd. All rights reserved * *****************************************************************************/ #include #include #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" #include "sap_mlme.h" #ifdef CONFIG_SCSC_WLAN_TX_API #include "tx_api.h" #endif 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 = 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, "SAVE"); slsi_spinlock_lock(&fwtest->fw_test_lock); 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 = 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_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; case MA_BLOCKACKREQ_IND: SLSI_DBG2(sdev, SLSI_FW_TEST, "0x%p: Process MA_BLOCKACKREQ_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; 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, 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, 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: kfree_skb(req); kfree_skb(cfm); 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; u16 flow_id; 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; flow_id = fapi_get_u16(skb, u.mlme_procedure_started_ind.flow_id); peer_index = (flow_id >> 8); peer = slsi_peer_add(sdev, dev, mgmt->sa, peer_index); if (WARN_ON(!peer)) return; slsi_peer_update_assoc_req(sdev, dev, peer, 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 flow_id = fapi_get_u16(skb, u.mlme_connected_ind.flow_id); u16 aid = (flow_id >> 8); 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, aid:%d)\n", ndev_vif->ifnum, aid); WARN(!ndev_vif->is_fw_test, "!is_fw_test"); if (WARN(!ndev_vif->activated, "Not Activated")) return; if (WARN_ON(aid > SLSI_PEER_INDEX_MAX)) return; peer = slsi_get_peer_from_qs(sdev, dev, aid - 1); if (WARN(!peer, "Peer(aid:%d) Not Found", aid)) 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) { kfree_skb(skb); return; } SLSI_NET_DBG1(dev, SLSI_FW_TEST, "ProcedureStarted(vif:%d)\n", ndev_vif->ifnum); if (fapi_get_u16(skb, u.mlme_procedure_started_ind.procedure_type) != FAPI_PROCEDURETYPE_CONNECTION_STARTED) { 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\n", ndev_vif->ifnum, viftype); break; } SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); out: 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) { 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\n", ndev_vif->ifnum, viftype); break; } SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); out: 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) { 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\n", ndev_vif->ifnum, viftype); break; } SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); out: 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) { 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\n", ndev_vif->ifnum, viftype); break; } SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); out: 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_disconnected_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) { 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\n", ndev_vif->ifnum, viftype); break; } SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); out: 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 flow_id = fapi_get_u16(skb, u.mlme_tdls_peer_ind.flow_id); u16 peer_index = (flow_id >> 8); 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); #ifdef CONFIG_SCSC_WLAN_TX_API slsi_tx_tdls_update(sdev, dev, ndev_vif->peer_sta_record[SLSI_STA_PEER_QUEUESET], peer, true); #else /* 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); #endif 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); #ifdef CONFIG_SCSC_WLAN_TX_API slsi_tx_tdls_update(sdev, dev, ndev_vif->peer_sta_record[SLSI_STA_PEER_QUEUESET], peer, false); #else /* 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); #endif 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) { kfree_skb(skb); return; } if (WARN(!ndev_vif->activated, "Not Activated")) { 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")) { 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; } 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_DISCOVERY; if (!ndev_vif->is_fw_test) { 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\n", ndev_vif->ifnum, viftype); break; } SLSI_MUTEX_UNLOCK(ndev_vif->vif_mutex); out: 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; 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_DISCOVERY; 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); kfree_skb(fwtest->mlme_add_vif_req[ndev_vif->ifnum]); kfree_skb(fwtest->mlme_connect_req[ndev_vif->ifnum]); kfree_skb(fwtest->mlme_connect_cfm[ndev_vif->ifnum]); 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); kfree_skb(skb); } static void slsi_fw_test_ma_blockackreq_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); if (!ndev_vif->is_fw_test) { kfree_skb(skb); return; } SLSI_NET_DBG1(dev, SLSI_FW_TEST, "MA Block Ack Req Indication(vif:%d)\n", ndev_vif->ifnum); slsi_rx_blockack_ind(sdev, dev, 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")) { 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); 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_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; case MA_BLOCKACKREQ_IND: slsi_fw_test_ma_blockackreq_ind(sdev, dev, fw_test, skb); break; default: WARN(1, "Unhandled Signal"); 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++) { kfree_skb(fwtest->mlme_add_vif_req[i]); kfree_skb(fwtest->mlme_connect_req[i]); kfree_skb(fwtest->mlme_connect_cfm[i]); 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)); }