1103 lines
37 KiB
C
Executable file
1103 lines
37 KiB
C
Executable file
/****************************************************************************
|
|
*
|
|
* Copyright (c) 2012 - 2022 Samsung Electronics Co., Ltd. All rights reserved
|
|
*
|
|
* Chip Manager interface
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include "mgt.h"
|
|
#include "dev.h"
|
|
#include "debug.h"
|
|
#include "scsc_wifi_cm_if.h"
|
|
#include "utils.h"
|
|
#ifdef CONFIG_SCSC_WLAN_HIP4_PROFILING
|
|
#include "hip4_sampler.h"
|
|
#endif
|
|
#include <linux/fs.h>
|
|
|
|
#include <scsc/scsc_mx.h>
|
|
#if IS_ENABLED(CONFIG_SCSC_LOG_COLLECTION)
|
|
#include <scsc/scsc_log_collector.h>
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
|
|
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
|
|
#endif
|
|
|
|
static bool EnableTestMode;
|
|
module_param(EnableTestMode, bool, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(EnableTestMode, "Enable WlanLite test mode driver.");
|
|
|
|
static BLOCKING_NOTIFIER_HEAD(slsi_wlan_notifier);
|
|
|
|
static struct mutex slsi_start_mutex;
|
|
static int recovery_in_progress;
|
|
static u16 latest_scsc_panic_code;
|
|
|
|
#define SLSI_RETRY_STOP_COUNT_ERROR 1
|
|
|
|
/* TODO: Would be good to get this removed - use module_client? */
|
|
struct slsi_cm_ctx {
|
|
struct slsi_dev *sdev;
|
|
};
|
|
|
|
/* Only one wlan service instance is assumed for now. */
|
|
static struct slsi_cm_ctx cm_ctx;
|
|
|
|
static void slsi_hip_block_bh(struct slsi_dev *sdev);
|
|
|
|
int slsi_wlan_service_notifier_register(struct notifier_block *nb)
|
|
{
|
|
return blocking_notifier_chain_register(&slsi_wlan_notifier, nb);
|
|
}
|
|
|
|
int slsi_wlan_service_notifier_unregister(struct notifier_block *nb)
|
|
{
|
|
return blocking_notifier_chain_unregister(&slsi_wlan_notifier, nb);
|
|
}
|
|
|
|
static int wlan_suspend(struct scsc_service_client *client)
|
|
{
|
|
struct slsi_dev *sdev = container_of(client, struct slsi_dev, mx_wlan_client);
|
|
|
|
SLSI_INFO_NODEV("Nofity registered functions\n");
|
|
blocking_notifier_call_chain(&slsi_wlan_notifier, SCSC_WIFI_SUSPEND, sdev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int wlan_resume(struct scsc_service_client *client)
|
|
{
|
|
struct slsi_dev *sdev = container_of(client, struct slsi_dev, mx_wlan_client);
|
|
|
|
SLSI_INFO_NODEV("Nofity registered functions\n");
|
|
blocking_notifier_call_chain(&slsi_wlan_notifier, SCSC_WIFI_RESUME, sdev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void slsi_append_log_to_system_buffer(struct slsi_dev *sdev)
|
|
{
|
|
int pos = 0;
|
|
int buf_size = 128;
|
|
int i = 0;
|
|
char *log_to_sys_error_buffer = NULL;
|
|
struct netdev_vif *ndev_vif;
|
|
|
|
log_to_sys_error_buffer = kzalloc(128, GFP_KERNEL);
|
|
if (!log_to_sys_error_buffer) {
|
|
SLSI_ERR_NODEV("Failed to allocate memory\n");
|
|
return;
|
|
}
|
|
|
|
scnprintf(log_to_sys_error_buffer + pos, buf_size - pos, "netdev_up_count=%d ", sdev->netdev_up_count);
|
|
|
|
for (i = 1; i <= CONFIG_SCSC_WLAN_MAX_INTERFACES; i++) {
|
|
if (sdev->netdev[i]) {
|
|
ndev_vif = netdev_priv(sdev->netdev[i]);
|
|
if (ndev_vif->is_available)
|
|
pos += scnprintf(log_to_sys_error_buffer + pos, buf_size - pos, "[%d: vif_type = %d] ", i, ndev_vif->vif_type);
|
|
}
|
|
}
|
|
pos += scnprintf(log_to_sys_error_buffer + pos, buf_size - pos, "\n");
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
}
|
|
|
|
static u8 wlan_failure_notification(struct scsc_service_client *client, struct mx_syserr_decode *err)
|
|
{
|
|
struct slsi_dev *sdev = container_of(client, struct slsi_dev, mx_wlan_client);
|
|
char log_to_sys_error_buffer[128] = { 0 };
|
|
|
|
atomic_set(&sdev->cm_if.reset_level, err->level);
|
|
SLSI_INFO_NODEV("SubSystem:%d,Level:%d,Type:%d,Sub_code:%d\n", err->subsys, err->level, err->type, err->subcode);
|
|
|
|
sprintf(log_to_sys_error_buffer, "%s: SubSystem:%d,Level:%d,Type:%d,Sub_code:%d\n",
|
|
__func__, err->subsys, err->level, err->type, err->subcode);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
|
|
return err->level;
|
|
}
|
|
|
|
static void wlan_failure_reset_v2(struct scsc_service_client *client, u8 level, u16 scsc_syserr_code)
|
|
{
|
|
struct slsi_dev *sdev = container_of(client, struct slsi_dev, mx_wlan_client);
|
|
char log_to_sys_error_buffer[128] = { 0 };
|
|
|
|
SLSI_INFO_NODEV("Level:%d scsc_syserr_code:%d\n", level, scsc_syserr_code);
|
|
|
|
sprintf(log_to_sys_error_buffer, "%s: Level:%d scsc_syserr_code:%d\n", __func__, level, scsc_syserr_code);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
|
|
mutex_lock(&slsi_start_mutex);
|
|
atomic_set(&sdev->cm_if.reset_level, level);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
if (level < 5) {
|
|
SLSI_WARN_NODEV("Low level error level:%d\n", level);
|
|
} else if (level == 5 || level == 6) {
|
|
blocking_notifier_call_chain(&slsi_wlan_notifier, SCSC_WIFI_SUBSYSTEM_RESET, sdev);
|
|
} else if (level == SLSI_WIFI_CM_IF_SYSTEM_ERROR_PANIC) {
|
|
#ifndef SCSC_SEP_VERSION
|
|
if (sdev->forced_se_7) {
|
|
mutex_lock(&slsi_start_mutex);
|
|
atomic_set(&sdev->cm_if.reset_level, 7);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
}
|
|
sdev->forced_se_7 = false;
|
|
#endif
|
|
latest_scsc_panic_code = scsc_syserr_code;
|
|
}
|
|
}
|
|
|
|
static bool wlan_stop_on_failure_v2(struct scsc_service_client *client, struct mx_syserr_decode *err)
|
|
{
|
|
int state;
|
|
u8 system_error_level;
|
|
struct slsi_dev *sdev = container_of(client, struct slsi_dev, mx_wlan_client);
|
|
#ifndef SCSC_SEP_VERSION
|
|
struct netdev_vif *wlan_dev_vif;
|
|
#endif
|
|
char *error = "Subsystem Restart";
|
|
#ifdef CONFIG_SCSC_WLAN_AP_AUTO_RECOVERY
|
|
struct netdev_vif *ndev_vif;
|
|
int i;
|
|
#endif
|
|
|
|
SLSI_INFO_NODEV("state:%d, err_level:%d\n", sdev->cm_if.cm_if_state, err->level);
|
|
mutex_lock(&slsi_start_mutex);
|
|
recovery_in_progress = 1;
|
|
sdev->recovery_status = 1;
|
|
#ifndef SCSC_SEP_VERSION
|
|
sdev->forced_se_7 = false;
|
|
#endif
|
|
|
|
slsi_append_log_to_system_buffer(sdev);
|
|
|
|
reinit_completion(&sdev->recovery_completed);
|
|
|
|
state = atomic_read(&sdev->cm_if.cm_if_state);
|
|
atomic_set(&sdev->cm_if.reset_level, err->level);
|
|
/* system error level is set in failure_notification. if this is not yet set, consider
|
|
* a full panic. set it to SLSI_WIFI_CM_IF_SYSTEM_ERROR_PANIC
|
|
*/
|
|
system_error_level = atomic_read(&sdev->cm_if.reset_level);
|
|
if (!system_error_level || system_error_level == SLSI_WIFI_CM_IF_SYSTEM_ERROR_PANIC) {
|
|
atomic_set(&sdev->cm_if.reset_level, SLSI_WIFI_CM_IF_SYSTEM_ERROR_PANIC);
|
|
#ifndef SCSC_SEP_VERSION
|
|
/* When wifi and softap is off and still wlan0 is up, recovery is not
|
|
* handled in upper layers.
|
|
* So force it to level7 to recover internally.
|
|
*/
|
|
if (sdev->netdev_up_count == 1 && sdev->netdev[SLSI_NET_INDEX_WLAN]) {
|
|
wlan_dev_vif = netdev_priv(sdev->netdev[SLSI_NET_INDEX_WLAN]);
|
|
if (wlan_dev_vif->vif_type != FAPI_VIFTYPE_AP && wlan_dev_vif->is_available) {
|
|
atomic_set(&sdev->cm_if.reset_level, 7);
|
|
sdev->forced_se_7 = true;
|
|
SLSI_INFO_NODEV("Reducing reset level: L8->L7\n");
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
if (state != SCSC_WIFI_CM_IF_STATE_STOPPED) {
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_BLOCKED);
|
|
sdev->fail_reported = true;
|
|
|
|
/* If next state is stopped, then don't signal recovery since
|
|
* the Android framework won't/shouldn't restart (supplicant
|
|
* stop and start).
|
|
*/
|
|
if (sdev->recovery_next_state != SCSC_WIFI_CM_IF_STATE_STOPPING) {
|
|
slsi_hip_block_bh(sdev);
|
|
|
|
/* Stop wlan operations. Send event to registered parties */
|
|
mutex_unlock(&slsi_start_mutex);
|
|
SLSI_INFO_NODEV("Nofity registered functions\n");
|
|
blocking_notifier_call_chain(&slsi_wlan_notifier, SCSC_WIFI_STOP, sdev);
|
|
mutex_lock(&slsi_start_mutex);
|
|
#ifdef CONFIG_SCSC_WLAN_AP_AUTO_RECOVERY
|
|
system_error_level = atomic_read(&sdev->cm_if.reset_level);
|
|
if (system_error_level == SLSI_WIFI_CM_IF_SYSTEM_ERROR_PANIC)
|
|
for (i = 1; i <= CONFIG_SCSC_WLAN_MAX_INTERFACES; i++)
|
|
if (sdev->netdev[i]) {
|
|
ndev_vif = netdev_priv(sdev->netdev[i]);
|
|
if (ndev_vif->iftype == NL80211_IFTYPE_AP) {
|
|
if (slsi_send_hanged_vendor_event(sdev,
|
|
latest_scsc_panic_code) < 0)
|
|
SLSI_ERR(sdev, "Failed to send hang event\n");
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
} else {
|
|
SLSI_INFO_NODEV("Wi-Fi service driver not started\n");
|
|
}
|
|
|
|
SLSI_INFO_NODEV("Sending SLSI_NL80211_SUBSYSTEM_RESTART_EVENT\n");
|
|
slsi_vendor_event(sdev, SLSI_NL80211_SUBSYSTEM_RESTART_EVENT, error, strlen(error));
|
|
mutex_unlock(&slsi_start_mutex);
|
|
SLSI_INFO_NODEV("Done!\n");
|
|
return true;
|
|
}
|
|
|
|
/* WLAN service driver registration
|
|
* ================================
|
|
*/
|
|
void slsi_wlan_service_probe(struct scsc_mx_module_client *module_client, struct scsc_mx *mx, enum scsc_module_client_reason reason)
|
|
{
|
|
struct slsi_dev *sdev;
|
|
struct device *dev;
|
|
struct scsc_service_client mx_wlan_client;
|
|
#if IS_ENABLED(CONFIG_SCSC_LOG_COLLECTION)
|
|
char buf[SCSC_LOG_FAPI_VERSION_SIZE];
|
|
#endif
|
|
char log_to_sys_error_buffer[128] = { 0 };
|
|
|
|
SLSI_UNUSED_PARAMETER(module_client);
|
|
|
|
SLSI_INFO_NODEV("WLAN service probe\n");
|
|
|
|
memset((void *)&mx_wlan_client, 0, (size_t)sizeof(mx_wlan_client));
|
|
mutex_lock(&slsi_start_mutex);
|
|
|
|
if (reason == SCSC_MODULE_CLIENT_REASON_RECOVERY && !recovery_in_progress)
|
|
goto done;
|
|
|
|
if (reason == SCSC_MODULE_CLIENT_REASON_RECOVERY) {
|
|
SLSI_INFO_NODEV("Probe recovery\n");
|
|
sdev = cm_ctx.sdev;
|
|
recovery_in_progress = 0;
|
|
sdev->fail_reported = false;
|
|
sdev->recovery_status = 0;
|
|
mutex_unlock(&slsi_start_mutex);
|
|
blocking_notifier_call_chain(&slsi_wlan_notifier, SCSC_WIFI_CHIP_READY, sdev);
|
|
mutex_lock(&slsi_start_mutex);
|
|
complete_all(&sdev->recovery_completed);
|
|
sprintf(log_to_sys_error_buffer, "%s: recovery_completed\n", __func__);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
} else {
|
|
/* Register callbacks */
|
|
mx_wlan_client.failure_notification = wlan_failure_notification;
|
|
mx_wlan_client.stop_on_failure_v2 = wlan_stop_on_failure_v2;
|
|
mx_wlan_client.failure_reset_v2 = wlan_failure_reset_v2;
|
|
mx_wlan_client.suspend = wlan_suspend;
|
|
mx_wlan_client.resume = wlan_resume;
|
|
|
|
dev = scsc_service_get_device_by_mx(mx);
|
|
|
|
/* The mutex must be released at this point since the attach
|
|
* process may call various functions including
|
|
* slsi_sm_wlan_service_start and slsi_sm_wlan_service_open, which will
|
|
* claim the same mutex.
|
|
*/
|
|
mutex_unlock(&slsi_start_mutex);
|
|
sdev = slsi_dev_attach(dev, mx, &mx_wlan_client);
|
|
mutex_lock(&slsi_start_mutex);
|
|
if (!sdev) {
|
|
SLSI_ERR_NODEV("WLAN attach failed - slsi_dev_attach\n");
|
|
goto done;
|
|
}
|
|
|
|
cm_ctx.sdev = sdev; /* TODO: For now. */
|
|
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_PROBING);
|
|
get_device(dev);
|
|
|
|
#ifdef CONFIG_SCSC_WLAN_HIP4_PROFILING
|
|
hip4_sampler_create(sdev, mx);
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_SCSC_LOG_COLLECTION)
|
|
memset(buf, 0, SCSC_LOG_FAPI_VERSION_SIZE);
|
|
/* Write FAPI VERSION to collector header */
|
|
/* IMPORTANT - Do not change the formatting as User space tooling is parsing the string
|
|
* to read SAP fapi versions.
|
|
*/
|
|
snprintf(buf, SCSC_LOG_FAPI_VERSION_SIZE, "ma:%u.%u, mlme:%u.%u, debug:%u.%u, test:%u.%u",
|
|
FAPI_MAJOR_VERSION(FAPI_DATA_SAP_VERSION), FAPI_MINOR_VERSION(FAPI_DATA_SAP_VERSION),
|
|
FAPI_MAJOR_VERSION(FAPI_CONTROL_SAP_VERSION), FAPI_MINOR_VERSION(FAPI_CONTROL_SAP_VERSION),
|
|
FAPI_MAJOR_VERSION(FAPI_DEBUG_SAP_VERSION), FAPI_MINOR_VERSION(FAPI_DEBUG_SAP_VERSION),
|
|
FAPI_MAJOR_VERSION(FAPI_TEST_SAP_VERSION), FAPI_MINOR_VERSION(FAPI_TEST_SAP_VERSION));
|
|
|
|
scsc_log_collector_write_fapi(buf, SCSC_LOG_FAPI_VERSION_SIZE);
|
|
#endif
|
|
}
|
|
|
|
if (reason != SCSC_MODULE_CLIENT_REASON_RECOVERY)
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_PROBED);
|
|
atomic_set(&sdev->cm_if.reset_level, 0);
|
|
done:
|
|
mutex_unlock(&slsi_start_mutex);
|
|
}
|
|
|
|
/* service_clean_up_locked expects the slsi_start_mutex mutex to be claimed when
|
|
* service_clean_up_locked is called.
|
|
*/
|
|
static void service_clean_up_locked(struct slsi_dev *sdev)
|
|
{
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_REMOVING);
|
|
put_device(sdev->dev);
|
|
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_REMOVED);
|
|
|
|
sdev->maxwell_core = NULL;
|
|
|
|
/* The mutex must be released at this point since the tear down
|
|
* process will call various functions including
|
|
* slsi_sm_wlan_service_stop and slsi_sm_wlan_service_close, which will
|
|
* claim the same mutex.
|
|
*/
|
|
mutex_unlock(&slsi_start_mutex);
|
|
slsi_dev_detach(sdev);
|
|
mutex_lock(&slsi_start_mutex);
|
|
}
|
|
|
|
static void slsi_wlan_service_remove(struct scsc_mx_module_client *module_client, struct scsc_mx *mx, enum scsc_module_client_reason reason)
|
|
{
|
|
struct slsi_dev *sdev;
|
|
int state;
|
|
int level = 0;
|
|
char log_to_sys_error_buffer[128] = { 0 };
|
|
|
|
SLSI_UNUSED_PARAMETER(mx);
|
|
SLSI_UNUSED_PARAMETER(module_client);
|
|
|
|
sdev = cm_ctx.sdev;
|
|
if (!sdev) {
|
|
SLSI_INFO_NODEV("no sdev\n");
|
|
return;
|
|
}
|
|
|
|
if (reason == SCSC_MODULE_CLIENT_REASON_RECOVERY && !recovery_in_progress) {
|
|
SLSI_INFO_NODEV("WLAN service remove - recovery. Service not active.\n");
|
|
} else if (reason == SCSC_MODULE_CLIENT_REASON_RECOVERY && recovery_in_progress) {
|
|
int r;
|
|
|
|
level = atomic_read(&sdev->cm_if.reset_level);
|
|
|
|
SLSI_INFO_NODEV("WLAN service remove - recovery, Error Level:%d\n", level);
|
|
|
|
sprintf(log_to_sys_error_buffer, "%s: recovery, Error Level:%d\n", __func__, level);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
|
|
/* Only indicate if the next state is not stopping. The recovery
|
|
* handling won't have any affect if the framework is closing
|
|
* anyway.
|
|
*/
|
|
if (sdev->recovery_next_state != SCSC_WIFI_CM_IF_STATE_STOPPING) {
|
|
SLSI_INFO_NODEV("Nofity registered functions\n");
|
|
blocking_notifier_call_chain(&slsi_wlan_notifier, SCSC_WIFI_FAILURE_RESET, sdev);
|
|
}
|
|
if (level == SLSI_WIFI_CM_IF_SYSTEM_ERROR_PANIC) {
|
|
mutex_lock(&slsi_start_mutex);
|
|
/**
|
|
* If there was a request to stop during the recovery, then do
|
|
* not sent a hang - just stop here. The Wi-Fi service driver is
|
|
* ready to be turned on again. Let the service_stop complete.
|
|
*/
|
|
complete_all(&sdev->recovery_remove_completion);
|
|
if (sdev->recovery_next_state == SCSC_WIFI_CM_IF_STATE_STOPPING) {
|
|
SLSI_INFO_NODEV("Recovery - next state stopping\n");
|
|
} else {
|
|
SLSI_INFO_NODEV("Calling slsi_send_hanged_vendor_event with latest_scsc_panic_code=0x%x\n",
|
|
latest_scsc_panic_code);
|
|
sprintf(log_to_sys_error_buffer, "Calling slsi_send_hanged_vendor_event, panic_code=0x%x\n",
|
|
latest_scsc_panic_code);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
|
|
if (slsi_send_hanged_vendor_event(sdev, latest_scsc_panic_code) < 0) {
|
|
SLSI_ERR(sdev, "Failed to send hang event\n");
|
|
sprintf(log_to_sys_error_buffer, "Failed to send vendor hang event\n");
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
}
|
|
|
|
/* Complete any pending ctrl signals, which will prevent
|
|
* the hang event from being processed.
|
|
*/
|
|
complete_all(&sdev->sig_wait.completion);
|
|
}
|
|
|
|
mutex_unlock(&slsi_start_mutex);
|
|
|
|
r = wait_for_completion_timeout(&sdev->recovery_stop_completion,
|
|
msecs_to_jiffies(sdev->recovery_timeout));
|
|
if (r == 0) {
|
|
SLSI_INFO(sdev, "recovery_stop_completion timedout, netdev_up_count = %d\n", sdev->netdev_up_count);
|
|
sprintf(log_to_sys_error_buffer, "recovery_stop_completion timedout[%d]\n",
|
|
sdev->recovery_timeout);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
if (sdev->netdev_up_count) {
|
|
sdev->recovery_fail_safe = true;
|
|
SLSI_MUTEX_LOCK(sdev->start_stop_mutex);
|
|
slsi_stop_chip(sdev);
|
|
SLSI_MUTEX_UNLOCK(sdev->start_stop_mutex);
|
|
queue_work(sdev->device_wq, &sdev->system_error_user_fail_work);
|
|
}
|
|
}
|
|
}
|
|
mutex_lock(&slsi_start_mutex);
|
|
reinit_completion(&sdev->recovery_stop_completion);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
|
|
} else {
|
|
SLSI_INFO_NODEV("WLAN service remove\n");
|
|
|
|
#ifdef CONFIG_SCSC_WLAN_HIP4_PROFILING
|
|
hip4_sampler_destroy(sdev, mx);
|
|
#endif
|
|
|
|
mutex_lock(&slsi_start_mutex);
|
|
state = atomic_read(&sdev->cm_if.cm_if_state);
|
|
if (state != SCSC_WIFI_CM_IF_STATE_STARTED &&
|
|
state != SCSC_WIFI_CM_IF_STATE_PROBED &&
|
|
state != SCSC_WIFI_CM_IF_STATE_STOPPED &&
|
|
state != SCSC_WIFI_CM_IF_STATE_BLOCKED) {
|
|
mutex_unlock(&slsi_start_mutex);
|
|
SLSI_INFO_NODEV("state-event error %d\n", state);
|
|
return;
|
|
}
|
|
|
|
service_clean_up_locked(sdev);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
}
|
|
SLSI_INFO_NODEV("Done!\n");
|
|
}
|
|
|
|
/* Block future HIP runs through the hip_switch */
|
|
static void slsi_hip_block_bh(struct slsi_dev *sdev)
|
|
{
|
|
SLSI_WARN(sdev, "HIP state set to #SLSI_HIP_STATE_BLOCKED#\n");
|
|
atomic_set(&sdev->hip.hip_state, SLSI_HIP_STATE_BLOCKED);
|
|
}
|
|
|
|
static struct scsc_mx_module_client wlan_driver = {
|
|
.name = "WLAN driver",
|
|
.probe = slsi_wlan_service_probe,
|
|
.remove = slsi_wlan_service_remove,
|
|
};
|
|
|
|
int slsi_sm_service_driver_register(void)
|
|
{
|
|
struct slsi_cm_ctx *ctx = &cm_ctx;
|
|
|
|
memset(ctx, 0, sizeof(*ctx));
|
|
mutex_init(&slsi_start_mutex);
|
|
scsc_mx_module_register_client_module(&wlan_driver);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void slsi_sm_service_driver_unregister(void)
|
|
{
|
|
scsc_mx_module_unregister_client_module(&wlan_driver);
|
|
}
|
|
|
|
/* start/stop wlan service
|
|
* =======================
|
|
*/
|
|
void slsi_sm_service_failed(struct slsi_dev *sdev, const char *reason, bool is_work)
|
|
{
|
|
int state;
|
|
|
|
mutex_lock(&slsi_start_mutex);
|
|
|
|
if (is_work)
|
|
complete_all(&sdev->service_fail_started_indication);
|
|
|
|
state = atomic_read(&sdev->cm_if.cm_if_state);
|
|
if (state != SCSC_WIFI_CM_IF_STATE_STARTED &&
|
|
state != SCSC_WIFI_CM_IF_STATE_STOPPING) {
|
|
mutex_unlock(&slsi_start_mutex);
|
|
SLSI_INFO(sdev, "State %d - ignoring event\n", state);
|
|
return;
|
|
}
|
|
|
|
/* Limit the volume of error reports to the core */
|
|
if (!sdev->fail_reported) {
|
|
/* This log may be scraped by test systems */
|
|
SLSI_ERR(sdev, "scsc_wifibt: FATAL ERROR: %s\n", reason);
|
|
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_BLOCKED);
|
|
slsi_hip_block_bh(sdev);
|
|
scsc_mx_service_service_failed(sdev->service, reason);
|
|
scsc_mx_service_mif_dump_registers(sdev->service);
|
|
sdev->fail_reported = true;
|
|
}
|
|
|
|
if (is_work)
|
|
reinit_completion(&sdev->service_fail_started_indication);
|
|
|
|
mutex_unlock(&slsi_start_mutex);
|
|
}
|
|
|
|
/* Is production test mode enabled? */
|
|
bool slsi_is_test_mode_enabled(void)
|
|
{
|
|
return EnableTestMode;
|
|
}
|
|
|
|
#define SLSI_SM_WLAN_SERVICE_RECOVERY_COMPLETED_TIMEOUT 20000
|
|
#define SLSI_SM_WLAN_SERVICE_RECOVERY_DISABLED_TIMEOUT 2000
|
|
|
|
int slsi_sm_recovery_service_stop(struct slsi_dev *sdev)
|
|
{
|
|
int err = 0;
|
|
char log_to_sys_error_buffer[128] = { 0 };
|
|
|
|
mutex_lock(&slsi_start_mutex);
|
|
SLSI_INFO_NODEV("Stopping WLAN service\n");
|
|
sprintf(log_to_sys_error_buffer, "Stopping WLAN service\n");
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
|
|
sdev->wlan_service_on = 0;
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPING);
|
|
|
|
if (!sdev->service) {
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
SLSI_WARN(sdev, "Service is NULL\n");
|
|
sprintf(log_to_sys_error_buffer, "Service is NULL err=%d\n", err);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
err = -EINVAL;
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
|
|
err = scsc_mx_service_stop(sdev->service);
|
|
if (err == -EILSEQ || err == -EIO)
|
|
SLSI_INFO(sdev, "scsc_mx_service_stop failed err: %d\n", err);
|
|
else
|
|
err = 0;
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
|
|
int slsi_sm_recovery_service_close(struct slsi_dev *sdev)
|
|
{
|
|
int err = 0;
|
|
char log_to_sys_error_buffer[128] = { 0 };
|
|
|
|
mutex_lock(&slsi_start_mutex);
|
|
if (!sdev->service) {
|
|
SLSI_WARN(sdev, "sdev->service is NULL\n");
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return -EINVAL;
|
|
}
|
|
|
|
SLSI_INFO_NODEV("Closing WLAN service\n");
|
|
sprintf(log_to_sys_error_buffer, "Closing WLAN service\n");
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
|
|
err = scsc_mx_service_close(sdev->service);
|
|
if (err == -EILSEQ || err == -EIO)
|
|
SLSI_INFO(sdev, "scsc_mx_service_close failed err: %d\n", err);
|
|
else
|
|
err = 0;
|
|
if (!err)
|
|
sdev->service = NULL;
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
|
|
int slsi_sm_recovery_service_open(struct slsi_dev *sdev)
|
|
{
|
|
int err = 0;
|
|
char log_to_sys_error_buffer[128] = { 0 };
|
|
|
|
mutex_lock(&slsi_start_mutex);
|
|
SLSI_INFO_NODEV("Open WLAN service\n");
|
|
sprintf(log_to_sys_error_buffer, "Open WLAN service\n");
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
|
|
sdev->service = scsc_mx_service_open(sdev->maxwell_core, SCSC_SERVICE_ID_WLAN, &sdev->mx_wlan_client, &err);
|
|
if (err == -EILSEQ || err == -EIO)
|
|
SLSI_INFO(sdev, "scsc_mx_service_open failed err: %d\n", err);
|
|
else
|
|
err = 0;
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
|
|
int slsi_sm_recovery_service_start(struct slsi_dev *sdev)
|
|
{
|
|
int err = 0, err2 = 0;
|
|
struct slsi_hip4 *hip = &sdev->hip4_inst;
|
|
scsc_mifram_ref ref;
|
|
int state;
|
|
char log_to_sys_error_buffer[128] = { 0 };
|
|
|
|
mutex_lock(&slsi_start_mutex);
|
|
state = atomic_read(&sdev->cm_if.cm_if_state);
|
|
SLSI_INFO(sdev,
|
|
"Recovery -- Status:%d In_Progress:%d -- cm_if_state:%d\n",
|
|
sdev->recovery_status, recovery_in_progress, state);
|
|
if (state != SCSC_WIFI_CM_IF_STATE_PROBED &&
|
|
state != SCSC_WIFI_CM_IF_STATE_STOPPED) {
|
|
SLSI_INFO(sdev, "State-event error %d\n", state);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!sdev->service) {
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
SLSI_WARN(sdev, "Service is NULL\n");
|
|
sprintf(log_to_sys_error_buffer, "Service is NULL err=%d\n", err);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
err = -EINVAL;
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STARTING);
|
|
#ifdef CONFIG_SCSC_PCIE
|
|
err = scsc_mx_service_mifram_alloc(sdev->service, 1.5 * 1024 * 1024, &sdev->hip4_inst.hip_ref, 4096);
|
|
#else
|
|
err = scsc_mx_service_mifram_alloc(sdev->service, 2.5 * 1024 * 1024, &sdev->hip4_inst.hip_ref, 4096);
|
|
#endif
|
|
if (err) {
|
|
SLSI_WARN(sdev, "scsc_mx_service_mifram_alloc failed err: %d\n", err);
|
|
sprintf(log_to_sys_error_buffer, "scsc_mx_service_mifram_alloc failed err: %d\n", err);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
|
|
err = slsi_hip_start(sdev);
|
|
if (err) {
|
|
SLSI_WARN(sdev, "slsi_hip_start failed err: %d\n", err);
|
|
sprintf(log_to_sys_error_buffer, "slsi_hip_start failed err: %d\n", err);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
slsi_hip_stop(sdev);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
|
|
err = scsc_mx_service_mif_ptr_to_addr(sdev->service, hip->hip_control, &ref);
|
|
if (err) {
|
|
SLSI_WARN(sdev, "scsc_mx_service_mif_ptr_to_addr failed err: %d\n", err);
|
|
sprintf(log_to_sys_error_buffer, "scsc_mx_service_mif_ptr_to_addr failed err: %d\n", err);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
slsi_hip_stop(sdev);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
|
|
err = scsc_mx_service_start(sdev->service, ref);
|
|
if (err) {
|
|
SLSI_WARN(sdev, "scsc_mx_service_start failed err: %d\n", err);
|
|
sprintf(log_to_sys_error_buffer, "scsc_mx_service_start failed err: %d\n", err);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
slsi_hip_stop(sdev);
|
|
if (err == -EILSEQ) {
|
|
sdev->cm_if.recovery_state = SLSI_RECOVERY_SERVICE_STOPPED;
|
|
sdev->require_service_close = true;
|
|
}
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
err = slsi_hip_setup(sdev);
|
|
if (err) {
|
|
SLSI_WARN(sdev, "slsi_hip_setup failed err: %d\n", err);
|
|
sprintf(log_to_sys_error_buffer, "slsi_hip_setup failed err: %d\n", err);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
err2 = scsc_mx_service_stop(sdev->service);
|
|
if (err2)
|
|
SLSI_INFO(sdev, "scsc_mx_service_stop failed err2: %d\n", err2);
|
|
if (err2 == -EILSEQ) {
|
|
sdev->cm_if.recovery_state = SLSI_RECOVERY_SERVICE_STOPPED;
|
|
sdev->require_service_close = true;
|
|
}
|
|
slsi_hip_stop(sdev);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
/* Service has started, inform SAP versions to the registered SAPs */
|
|
err = slsi_hip_sap_setup(sdev);
|
|
if (err) {
|
|
SLSI_WARN(sdev, "slsi_hip_sap_setup failed err: %d\n", err);
|
|
sprintf(log_to_sys_error_buffer, "slsi_hip_sap_setup failed err: %d\n", err);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
err2 = scsc_mx_service_stop(sdev->service);
|
|
if (err2)
|
|
SLSI_INFO(sdev, "scsc_mx_service_stop failed err2: %d\n", err2);
|
|
if (err2 == -EILSEQ) {
|
|
sdev->cm_if.recovery_state = SLSI_RECOVERY_SERVICE_STOPPED;
|
|
sdev->require_service_close = true;
|
|
}
|
|
slsi_hip_stop(sdev);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STARTED);
|
|
sdev->wlan_service_on = 1;
|
|
sprintf(log_to_sys_error_buffer, "%s: wlan_service_on[1]\n", __func__);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
sdev->require_service_close = false;
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
|
|
int slsi_sm_wlan_service_open(struct slsi_dev *sdev)
|
|
{
|
|
int err = 0;
|
|
int state;
|
|
char log_to_sys_error_buffer[128] = { 0 };
|
|
|
|
mutex_lock(&slsi_start_mutex);
|
|
state = atomic_read(&sdev->cm_if.cm_if_state);
|
|
if (state != SCSC_WIFI_CM_IF_STATE_PROBED &&
|
|
state != SCSC_WIFI_CM_IF_STATE_STOPPED) {
|
|
SLSI_INFO(sdev, "State-event error %d\n", state);
|
|
err = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
sdev->recovery_timeout = mxman_recovery_disabled() ? SLSI_SM_WLAN_SERVICE_RECOVERY_DISABLED_TIMEOUT : SLSI_SM_WLAN_SERVICE_RECOVERY_COMPLETED_TIMEOUT;
|
|
|
|
/* Open service - will download FW - will set MBOX0 with Starting address */
|
|
SLSI_INFO(sdev, "Open WLAN service\n");
|
|
sprintf(log_to_sys_error_buffer, "Open WLAN service, recovery timeout = %d\n", sdev->recovery_timeout);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
|
|
sdev->service = scsc_mx_service_open(sdev->maxwell_core, SCSC_SERVICE_ID_WLAN, &sdev->mx_wlan_client, &err);
|
|
if (!sdev->service) {
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_PROBED);
|
|
SLSI_WARN(sdev, "Service open failed\n");
|
|
sprintf(log_to_sys_error_buffer, "Service open failed, err=%d\n", err);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
err = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
|
|
#if defined(CONFIG_SLUB_DEBUG_ON) || defined(CONFIG_DEBUG_PREEMPT) || defined(CONFIG_DEBUG_RT_MUTEXES) || \
|
|
defined(CONFIG_DEBUG_SPINLOCK) && defined(CONFIG_DEBUG_MUTEXES) && defined(CONFIG_DEBUG_LOCK_ALLOC) || \
|
|
defined(CONFIG_DEBUG_LOCK_ALLOC) && defined(CONFIG_DEBUG_ATOMIC_SLEEP) && defined(CONFIG_DEBUG_LIST)
|
|
#define KERNEL_DEBUG_OPTIONS_ENABLED
|
|
#endif
|
|
|
|
int slsi_sm_wlan_service_start(struct slsi_dev *sdev)
|
|
{
|
|
struct slsi_hip4 *hip = &sdev->hip4_inst;
|
|
scsc_mifram_ref ref;
|
|
int err = 0;
|
|
int err2 = 0;
|
|
int state;
|
|
char log_to_sys_error_buffer[128] = { 0 };
|
|
|
|
mutex_lock(&slsi_start_mutex);
|
|
state = atomic_read(&sdev->cm_if.cm_if_state);
|
|
SLSI_INFO(sdev,
|
|
"Recovery -- Status:%d In_Progress:%d -- cm_if_state:%d\n",
|
|
sdev->recovery_status, recovery_in_progress, state);
|
|
|
|
sprintf(log_to_sys_error_buffer, "%s: recovery_status = %d, recovery_in_progress = %d\n",
|
|
__func__, sdev->recovery_status, recovery_in_progress);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
|
|
if (state != SCSC_WIFI_CM_IF_STATE_PROBED &&
|
|
state != SCSC_WIFI_CM_IF_STATE_STOPPED) {
|
|
SLSI_INFO(sdev, "State-event error %d\n", state);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!sdev->service) {
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
SLSI_WARN(sdev, "Service is NULL\n");
|
|
sprintf(log_to_sys_error_buffer, "Service is NULL err=%d\n", err);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
err = -EINVAL;
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STARTING);
|
|
|
|
#ifdef KERNEL_DEBUG_OPTIONS_ENABLED
|
|
SLSI_WARN(sdev, "Kernel config debug options are enabled. This might impact the throughput performance.\n");
|
|
#endif
|
|
|
|
/* Get RAM from the MIF */
|
|
SLSI_INFO(sdev, "Allocate mifram\n");
|
|
|
|
#ifdef CONFIG_SCSC_PCIE
|
|
err = scsc_mx_service_mifram_alloc(sdev->service, 1.5 * 1024 * 1024, &sdev->hip4_inst.hip_ref, 4096);
|
|
#else
|
|
err = scsc_mx_service_mifram_alloc(sdev->service, 2.5 * 1024 * 1024, &sdev->hip4_inst.hip_ref, 4096);
|
|
#endif
|
|
if (err) {
|
|
SLSI_WARN(sdev, "scsc_mx_service_mifram_alloc failed err: %d\n", err);
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
|
|
SLSI_INFO(sdev, "Start HIP\n");
|
|
err = slsi_hip_start(sdev);
|
|
if (err) {
|
|
SLSI_WARN(sdev, "slsi_hip_start failed err: %d\n", err);
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
slsi_hip_stop(sdev);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
|
|
err = scsc_mx_service_mif_ptr_to_addr(sdev->service, hip->hip_control, &ref);
|
|
if (err) {
|
|
SLSI_WARN(sdev, "scsc_mx_service_mif_ptr_to_addr failed err: %d\n", err);
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
slsi_hip_stop(sdev);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
|
|
SLSI_INFO(sdev, "Starting WLAN service\n");
|
|
err = scsc_mx_service_start(sdev->service, ref);
|
|
if (err) {
|
|
SLSI_WARN(sdev, "scsc_mx_service_start failed err: %d\n", err);
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
if (err == -EILSEQ) {
|
|
sdev->cm_if.recovery_state = SLSI_RECOVERY_SERVICE_STOPPED;
|
|
sdev->require_service_close = true;
|
|
}
|
|
slsi_hip_stop(sdev);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
err = slsi_hip_setup(sdev);
|
|
if (err) {
|
|
SLSI_WARN(sdev, "slsi_hip_setup failed err: %d\n", err);
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
SLSI_INFO_NODEV("Stopping WLAN service\n");
|
|
err2 = scsc_mx_service_stop(sdev->service);
|
|
if (err2)
|
|
SLSI_INFO(sdev, "scsc_mx_service_stop failed err2: %d\n", err2);
|
|
err2 = scsc_mx_service_stop(sdev->service);
|
|
if (err2)
|
|
SLSI_INFO(sdev, "scsc_mx_service_stop failed err2: %d\n", err2);
|
|
if (err2 == -EILSEQ) {
|
|
sdev->cm_if.recovery_state = SLSI_RECOVERY_SERVICE_STOPPED;
|
|
sdev->require_service_close = true;
|
|
}
|
|
slsi_hip_stop(sdev);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err2;
|
|
}
|
|
/* Service has started, inform SAP versions to the registered SAPs */
|
|
err = slsi_hip_sap_setup(sdev);
|
|
if (err) {
|
|
SLSI_WARN(sdev, "slsi_hip_sap_setup failed err: %d\n", err);
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
SLSI_INFO_NODEV("Stopping WLAN service\n");
|
|
err2 = scsc_mx_service_stop(sdev->service);
|
|
if (err2)
|
|
SLSI_INFO(sdev, "scsc_mx_service_stop failed err2: %d\n", err2);
|
|
if (err2 == -EILSEQ) {
|
|
sdev->cm_if.recovery_state = SLSI_RECOVERY_SERVICE_STOPPED;
|
|
sdev->require_service_close = true;
|
|
}
|
|
slsi_hip_stop(sdev);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err2;
|
|
}
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STARTED);
|
|
sdev->wlan_service_on = 1;
|
|
sdev->require_service_close = false;
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return 0;
|
|
}
|
|
|
|
static void __slsi_sm_wlan_service_stop_wait_locked(struct slsi_dev *sdev)
|
|
{
|
|
int r;
|
|
char log_to_sys_error_buffer[128] = { 0 };
|
|
|
|
mutex_unlock(&slsi_start_mutex);
|
|
r = wait_for_completion_timeout(&sdev->recovery_remove_completion,
|
|
msecs_to_jiffies(sdev->recovery_timeout));
|
|
if (r == 0) {
|
|
SLSI_INFO(sdev, "recovery_remove_completion timeout\n");
|
|
|
|
sprintf(log_to_sys_error_buffer, "%s: recovery_remove_completion timeout\n", __func__);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
}
|
|
|
|
mutex_lock(&slsi_start_mutex);
|
|
sdev->recovery_next_state = SCSC_WIFI_CM_IF_STATE_STOPPED;
|
|
|
|
reinit_completion(&sdev->recovery_remove_completion);
|
|
}
|
|
|
|
int slsi_sm_wlan_service_stop(struct slsi_dev *sdev)
|
|
{
|
|
int cm_if_state;
|
|
int err = 0;
|
|
/* Variable to track number of stop request made to maxwell manager */
|
|
u8 retry_count = SLSI_RETRY_STOP_COUNT_ERROR;
|
|
char log_to_sys_error_buffer[128] = { 0 };
|
|
|
|
mutex_lock(&slsi_start_mutex);
|
|
cm_if_state = atomic_read(&sdev->cm_if.cm_if_state);
|
|
SLSI_INFO(sdev,
|
|
"Recovery -- Status:%d In_Progress:%d -- cm_if_state:%d\n",
|
|
sdev->recovery_status, recovery_in_progress, cm_if_state);
|
|
|
|
if (!sdev->service) {
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
SLSI_WARN(sdev, "Service is NULL\n");
|
|
sprintf(log_to_sys_error_buffer, "Service is NULL err=%d\n", err);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
err = -EINVAL;
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return err;
|
|
}
|
|
|
|
if (cm_if_state == SCSC_WIFI_CM_IF_STATE_BLOCKED) {
|
|
__slsi_sm_wlan_service_stop_wait_locked(sdev);
|
|
/* service stop is called from another context*/
|
|
if (atomic_read(&sdev->cm_if.cm_if_state) == SCSC_WIFI_CM_IF_STATE_STOPPED) {
|
|
SLSI_INFO(sdev, "skip mx_service_close cm_if_state=%d\n", SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return -EUSERS;
|
|
}
|
|
|
|
/* If the wait hasn't timed out, the recovery remove completion
|
|
* will have completed properly and the cm_if_state will be
|
|
* set to stopped here. If the probe hasn't fired for some reason
|
|
* try and do a service_stop regardless, since that's all we can
|
|
* do in this situation; hence skip the state check.
|
|
*/
|
|
goto skip_state_check;
|
|
}
|
|
|
|
if (cm_if_state != SCSC_WIFI_CM_IF_STATE_STARTED &&
|
|
cm_if_state != SCSC_WIFI_CM_IF_STATE_REMOVED &&
|
|
cm_if_state != SCSC_WIFI_CM_IF_STATE_PROBED) {
|
|
SLSI_INFO(sdev, "Service not started or incorrect state %d\n",
|
|
cm_if_state);
|
|
goto exit;
|
|
}
|
|
|
|
/**
|
|
* Note that the SCSC_WIFI_CM_IF_STATE_STOPPING state will inhibit
|
|
* auto-recovery mechanism, so be careful not to abuse it: as an
|
|
* example if panic happens on start or stop we don't want to
|
|
* un-necessarily pass by STOPPING in order to have a successful
|
|
* recovery in such a situation.
|
|
*/
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPING);
|
|
skip_state_check:
|
|
SLSI_INFO_NODEV("Stopping WLAN service\n");
|
|
sprintf(log_to_sys_error_buffer, "%s: Stopping WLAN service\n", __func__);
|
|
|
|
err = scsc_mx_service_stop(sdev->service);
|
|
if (err == -EILSEQ) {
|
|
/* scsc_mx_service_stop failed since there's a recovery in
|
|
* progress, so just wait for it to complete.
|
|
*/
|
|
SLSI_INFO(sdev, "scsc_mx_service_stop failed err: %d attempts left: %d\n", err, retry_count);
|
|
__slsi_sm_wlan_service_stop_wait_locked(sdev);
|
|
/* Wait for 20 second should be enough for recovery procedure to complete */
|
|
/* Need to close the service manually when receive remove callback*/
|
|
if (!retry_count)
|
|
sdev->require_service_close = true;
|
|
if (retry_count--)
|
|
goto skip_state_check;
|
|
} else if (err == -EIO) {
|
|
char reason[80];
|
|
|
|
SLSI_INFO(sdev, "scsc_mx_service_stop failed err: %d\n", err);
|
|
|
|
/* scsc_mx_service_stop since there was no respons from firmware
|
|
* to the stop request. Generate a host initiated panic to reset
|
|
* the chip and wait for it to complete.
|
|
*/
|
|
sdev->recovery_next_state = SCSC_WIFI_CM_IF_STATE_STOPPING;
|
|
snprintf(reason, sizeof(reason), "WLAN scsc_mx_service_stop failed");
|
|
|
|
mutex_unlock(&slsi_start_mutex);
|
|
slsi_sm_service_failed(sdev, reason, false);
|
|
mutex_lock(&slsi_start_mutex);
|
|
|
|
__slsi_sm_wlan_service_stop_wait_locked(sdev);
|
|
} else if (err == -EPERM) {
|
|
/* Special case when recovery is disabled, otherwise the driver
|
|
* will wait forever for recovery that never comes
|
|
*/
|
|
SLSI_INFO(sdev, "refused due to previous failure, recovery is disabled: %d\n", err);
|
|
} else if (err != 0) {
|
|
SLSI_INFO(sdev, "scsc_mx_service_stop failed, unknown err: %d\n", err);
|
|
}
|
|
|
|
atomic_set(&sdev->cm_if.cm_if_state, SCSC_WIFI_CM_IF_STATE_STOPPED);
|
|
exit:
|
|
mutex_unlock(&slsi_start_mutex);
|
|
return 0;
|
|
}
|
|
|
|
#define SLSI_SM_WLAN_SERVICE_CLOSE_RETRY 60
|
|
void slsi_sm_wlan_service_close(struct slsi_dev *sdev)
|
|
{
|
|
int cm_if_state, r = 0;
|
|
char log_to_sys_error_buffer[128] = { 0 };
|
|
|
|
mutex_lock(&slsi_start_mutex);
|
|
sdev->wlan_service_on = 0;
|
|
cm_if_state = atomic_read(&sdev->cm_if.cm_if_state);
|
|
if (cm_if_state != SCSC_WIFI_CM_IF_STATE_STOPPED) {
|
|
SLSI_INFO(sdev, "Service not stopped. cm_if_state = %d service is = %d\n", cm_if_state, sdev->service);
|
|
|
|
/**
|
|
* Close the service if failure has occurred after service has successfully opened
|
|
* but before service has attempted to start
|
|
*/
|
|
if (cm_if_state == SCSC_WIFI_CM_IF_STATE_PROBED) {
|
|
SLSI_INFO_NODEV("Closing WLAN service on error\n");
|
|
if (sdev->service)
|
|
r = scsc_mx_service_close(sdev->service);
|
|
}
|
|
goto exit;
|
|
}
|
|
|
|
SLSI_INFO_NODEV("Closing WLAN service. Service is %d\n", sdev->service);
|
|
|
|
sprintf(log_to_sys_error_buffer, "%s: Closing WLAN service. Service is %d\n", __func__, sdev->service);
|
|
slsi_add_log_to_system_error_buffer(sdev, log_to_sys_error_buffer);
|
|
|
|
if (sdev->service) {
|
|
scsc_mx_service_mifram_free(sdev->service, sdev->hip4_inst.hip_ref);
|
|
r = scsc_mx_service_close(sdev->service);
|
|
if (r == -EIO) {
|
|
/**
|
|
* Error handling in progress
|
|
*/
|
|
sdev->require_service_close = true;
|
|
sdev->cm_if.recovery_state = SLSI_RECOVERY_SERVICE_STOPPED;
|
|
SLSI_INFO(sdev, "scsc_mx_service_close failed with error:%d\n", r);
|
|
} else if (r == -EPERM) {
|
|
SLSI_ERR(sdev, "scsc_mx_service_close - recovery is disabled (%d)\n", r);
|
|
}
|
|
}
|
|
|
|
if (recovery_in_progress)
|
|
complete_all(&sdev->recovery_stop_completion);
|
|
exit:
|
|
if (!r)
|
|
sdev->service = NULL;
|
|
mutex_unlock(&slsi_start_mutex);
|
|
}
|
|
|
|
struct slsi_dev *slsi_get_sdev(void)
|
|
{
|
|
return cm_ctx.sdev;
|
|
}
|