blob: 0d2ee68fdf2bce46a7ed64b14a53342574f2fcb3 [file] [log] [blame]
/****************************************************************************
*
* Copyright (c) 2012 - 2017 Samsung Electronics Co., Ltd. All rights reserved
*
****************************************************************************/
#include <scsc/scsc_logring.h>
#include "hip.h"
#include "debug.h"
#include "procfs.h"
#include "sap.h"
/* SAP implementations container. Local and static to hip */
static struct hip_sap {
struct sap_api *sap[SAP_TOTAL];
} hip_sap_cont;
/* Register SAP with HIP layer */
int slsi_hip_sap_register(struct sap_api *sap_api)
{
u8 class = sap_api->sap_class;
if (class >= SAP_TOTAL)
return -ENODEV;
hip_sap_cont.sap[class] = sap_api;
return 0;
}
/* UNregister SAP with HIP layer */
int slsi_hip_sap_unregister(struct sap_api *sap_api)
{
u8 class = sap_api->sap_class;
if (class >= SAP_TOTAL)
return -ENODEV;
hip_sap_cont.sap[class] = NULL;
return 0;
}
int slsi_hip_sap_setup(struct slsi_dev *sdev)
{
/* Execute callbacks to intorm Supported version */
u16 version = 0;
u32 conf_hip4_ver = 0;
conf_hip4_ver = scsc_wifi_get_hip_config_version(&sdev->hip4_inst.hip_control->init);
/* We enforce that all the SAPs are registered at this point */
if ((!hip_sap_cont.sap[SAP_MLME]) || (!hip_sap_cont.sap[SAP_MA]) ||
(!hip_sap_cont.sap[SAP_DBG]) || (!hip_sap_cont.sap[SAP_TST]))
return -ENODEV;
if (hip_sap_cont.sap[SAP_MLME]->sap_version_supported) {
if (conf_hip4_ver == 4)
version = scsc_wifi_get_hip_config_version_4_u16(&sdev->hip4_inst.hip_control->config_v4, sap_mlme_ver);
if (conf_hip4_ver == 3)
version = scsc_wifi_get_hip_config_version_3_u16(&sdev->hip4_inst.hip_control->config_v3, sap_mlme_ver);
if (hip_sap_cont.sap[SAP_MLME]->sap_version_supported(version))
return -ENODEV;
} else {
return -ENODEV;
}
if (hip_sap_cont.sap[SAP_MA]->sap_version_supported) {
if (conf_hip4_ver == 4)
version = scsc_wifi_get_hip_config_version_4_u16(&sdev->hip4_inst.hip_control->config_v4, sap_ma_ver);
if (conf_hip4_ver == 3)
version = scsc_wifi_get_hip_config_version_3_u16(&sdev->hip4_inst.hip_control->config_v3, sap_ma_ver);
if (hip_sap_cont.sap[SAP_MA]->sap_version_supported(version))
return -ENODEV;
} else {
return -ENODEV;
}
if (hip_sap_cont.sap[SAP_DBG]->sap_version_supported) {
if (conf_hip4_ver == 4)
version = scsc_wifi_get_hip_config_version_4_u16(&sdev->hip4_inst.hip_control->config_v4, sap_debug_ver);
if (conf_hip4_ver == 3)
version = scsc_wifi_get_hip_config_version_3_u16(&sdev->hip4_inst.hip_control->config_v3, sap_debug_ver);
if (hip_sap_cont.sap[SAP_DBG]->sap_version_supported(version))
return -ENODEV;
} else {
return -ENODEV;
}
if (hip_sap_cont.sap[SAP_TST]->sap_version_supported) {
if (conf_hip4_ver == 4)
version = scsc_wifi_get_hip_config_version_4_u16(&sdev->hip4_inst.hip_control->config_v4, sap_test_ver);
if (conf_hip4_ver == 3)
version = scsc_wifi_get_hip_config_version_3_u16(&sdev->hip4_inst.hip_control->config_v3, sap_test_ver);
if (hip_sap_cont.sap[SAP_TST]->sap_version_supported(version))
return -ENODEV;
} else {
return -ENODEV;
}
/* Success */
return 0;
}
static int slsi_hip_service_notifier(struct notifier_block *nb, unsigned long event, void *data)
{
struct slsi_dev *sdev = (struct slsi_dev *)data;
int i;
if (!sdev)
return NOTIFY_BAD;
/* We enforce that all the SAPs are registered at this point */
if ((!hip_sap_cont.sap[SAP_MLME]) || (!hip_sap_cont.sap[SAP_MA]) ||
(!hip_sap_cont.sap[SAP_DBG]) || (!hip_sap_cont.sap[SAP_TST]))
return NOTIFY_BAD;
/* Check whether any sap is interested in the notifications */
for (i = 0; i < SAP_TOTAL; i++)
if (hip_sap_cont.sap[i]->sap_notifier) {
if (hip_sap_cont.sap[i]->sap_notifier(sdev, event))
return NOTIFY_BAD;
}
switch (event) {
case SCSC_WIFI_STOP:
SLSI_INFO(sdev, "Freeze HIP4\n");
mutex_lock(&sdev->hip.hip_mutex);
hip4_freeze(&sdev->hip4_inst);
mutex_unlock(&sdev->hip.hip_mutex);
break;
case SCSC_WIFI_FAILURE_RESET:
SLSI_INFO(sdev, "Set HIP4 up again\n");
mutex_lock(&sdev->hip.hip_mutex);
hip4_setup(&sdev->hip4_inst);
mutex_unlock(&sdev->hip.hip_mutex);
break;
case SCSC_WIFI_SUSPEND:
SLSI_INFO(sdev, "Suspend HIP4\n");
mutex_lock(&sdev->hip.hip_mutex);
hip4_suspend(&sdev->hip4_inst);
mutex_unlock(&sdev->hip.hip_mutex);
break;
case SCSC_WIFI_RESUME:
SLSI_INFO(sdev, "Resume HIP4\n");
mutex_lock(&sdev->hip.hip_mutex);
hip4_resume(&sdev->hip4_inst);
mutex_unlock(&sdev->hip.hip_mutex);
break;
default:
SLSI_INFO(sdev, "Unknown event code %lu\n", event);
break;
}
return NOTIFY_OK;
}
static struct notifier_block cm_nb = {
.notifier_call = slsi_hip_service_notifier,
};
int slsi_hip_init(struct slsi_dev *sdev, struct device *dev)
{
SLSI_UNUSED_PARAMETER(dev);
memset(&sdev->hip4_inst, 0, sizeof(sdev->hip4_inst));
sdev->hip.sdev = sdev;
atomic_set(&sdev->hip.hip_state, SLSI_HIP_STATE_STOPPED);
mutex_init(&sdev->hip.hip_mutex);
/* Register with the service notifier to receiver
* asynchronus messages such as SCSC_WIFI_STOP(Freeze), SCSC_WIFI_FAILURE_RESET i
*/
slsi_wlan_service_notifier_register(&cm_nb);
return 0;
}
void slsi_hip_deinit(struct slsi_dev *sdev)
{
slsi_wlan_service_notifier_unregister(&cm_nb);
mutex_destroy(&sdev->hip.hip_mutex);
}
int slsi_hip_start(struct slsi_dev *sdev)
{
if (!sdev->maxwell_core) {
SLSI_ERR(sdev, "Maxwell core does not exist\n");
return -EINVAL;
}
SLSI_DBG4(sdev, SLSI_HIP_INIT_DEINIT, "[1/3]. Update HIP state (SLSI_HIP_STATE_STARTING)\n");
atomic_set(&sdev->hip.hip_state, SLSI_HIP_STATE_STARTING);
SLSI_DBG4(sdev, SLSI_HIP_INIT_DEINIT, "[2/3]. Initialise HIP\n");
if (hip4_init(&sdev->hip4_inst)) {
atomic_set(&sdev->hip.hip_state, SLSI_HIP_STATE_STOPPED);
SLSI_ERR(sdev, "hip4_init failed\n");
return -EINVAL;
}
SLSI_DBG4(sdev, SLSI_HIP_INIT_DEINIT, "[3/3]. Update HIP state (SLSI_HIP_STATE_STARTED)\n");
atomic_set(&sdev->hip.hip_state, SLSI_HIP_STATE_STARTED);
return 0;
}
/* SAP rx proxy */
int slsi_hip_rx(struct slsi_dev *sdev, struct sk_buff *skb)
{
u16 pid;
/* We enforce that all the SAPs are registered at this point */
if ((!hip_sap_cont.sap[SAP_MLME]) || (!hip_sap_cont.sap[SAP_MA]) ||
(!hip_sap_cont.sap[SAP_DBG]) || (!hip_sap_cont.sap[SAP_TST]))
return -ENODEV;
/* Here we push a copy of the bare RECEIVED skb data also to the
* logring as a binary record.
* Note that bypassing UDI subsystem as a whole means we are losing:
* UDI filtering / UDI Header INFO / UDI QueuesFrames Throttling /
* UDI Skb Asynchronous processing
* We keep split DATA/CTRL path.
*/
if (fapi_is_ma(skb))
SCSC_BIN_TAG_DEBUG(BIN_WIFI_DATA_RX, skb->data, skb->len);
else
SCSC_BIN_TAG_DEBUG(BIN_WIFI_CTRL_RX, skb->data, skb->len);
/* Udi test : If pid in UDI range then pass to UDI and ignore */
slsi_log_clients_log_signal_fast(sdev, &sdev->log_clients, skb, SLSI_LOG_DIRECTION_TO_HOST);
pid = fapi_get_u16(skb, receiver_pid);
if (pid >= SLSI_TX_PROCESS_ID_UDI_MIN && pid <= SLSI_TX_PROCESS_ID_UDI_MAX) {
slsi_kfree_skb(skb);
return 0;
}
if (fapi_is_ma(skb))
return hip_sap_cont.sap[SAP_MA]->sap_handler(sdev, skb);
if (fapi_is_mlme(skb))
return hip_sap_cont.sap[SAP_MLME]->sap_handler(sdev, skb);
if (fapi_is_debug(skb))
return hip_sap_cont.sap[SAP_DBG]->sap_handler(sdev, skb);
if (fapi_is_test(skb))
return hip_sap_cont.sap[SAP_TST]->sap_handler(sdev, skb);
return -EIO;
}
/* Only DATA plane will look at the returning FB to account BoT */
int slsi_hip_tx_done(struct slsi_dev *sdev, u16 colour)
{
return hip_sap_cont.sap[SAP_MA]->sap_txdone(sdev, colour);
}
int slsi_hip_setup(struct slsi_dev *sdev)
{
/* Setup hip4 after initialization */
return hip4_setup(&sdev->hip4_inst);
}
int slsi_hip_stop(struct slsi_dev *sdev)
{
mutex_lock(&sdev->hip.hip_mutex);
SLSI_DBG4(sdev, SLSI_HIP_INIT_DEINIT, "Update HIP state (SLSI_HIP_STATE_STOPPING)\n");
atomic_set(&sdev->hip.hip_state, SLSI_HIP_STATE_STOPPING);
hip4_deinit(&sdev->hip4_inst);
SLSI_DBG4(sdev, SLSI_HIP_INIT_DEINIT, "Update HIP state (SLSI_HIP_STATE_STOPPED)\n");
atomic_set(&sdev->hip.hip_state, SLSI_HIP_STATE_STOPPED);
mutex_unlock(&sdev->hip.hip_mutex);
return 0;
}