Procházet zdrojové kódy

qcacld-3.0: Add and use API to check FW Down indication bit during probe

When commands to Firmware fail during probe due to Firmware being down, in
most cases QDF_BUGs are hit, not allowing re-probing to take place. A new
API is defined to check if the failure is due to Firmware being down or not.
If so, return error gracefully allowing re-probing to take place

Change-Id: Ib2f21a6f1ced1c16e9d2bdbda810b160ff63e2c6
CRs-fixed: 2027444
Nachiket Kukade před 8 roky
rodič
revize
8003d257b9

+ 70 - 0
core/cds/inc/cds_api.h

@@ -75,6 +75,18 @@ enum cds_driver_state {
 
 #define __CDS_IS_DRIVER_STATE(_state, _mask) (((_state) & (_mask)) == (_mask))
 
+/**
+ * enum cds_fw_state - Firmware state
+ * @CDS_FW_STATE_UNINITIALIZED: Firmware is in uninitialized state.
+ * CDS_FW_STATE_DOWN: Firmware is down.
+ */
+enum cds_fw_state {
+	CDS_FW_STATE_UNINITIALIZED = 0,
+	CDS_FW_STATE_DOWN,
+};
+
+#define __CDS_IS_FW_STATE(_state, _mask) (((_state) & (_mask)) == (_mask))
+
 /**
  * struct cds_sme_cbacks - list of sme functions registered with
  * CDS
@@ -91,6 +103,38 @@ void cds_set_driver_state(enum cds_driver_state);
 void cds_clear_driver_state(enum cds_driver_state);
 enum cds_driver_state cds_get_driver_state(void);
 
+/**
+ * cds_set_fw_state() - Set current firmware state
+ * @state:	Firmware state to be set to.
+ *
+ * This API sets firmware state to state. This API only sets the state and
+ * doesn't clear states, please make sure to use cds_clear_firmware_state
+ * to clear any state if required.
+ *
+ * Return: None
+ */
+void cds_set_fw_state(enum cds_fw_state);
+
+/**
+ * cds_clear_fw_state() - Clear current fw state
+ * @state:	Driver state to be cleared.
+ *
+ * This API clears fw state. This API only clears the state, please make
+ * sure to use cds_set_fw_state to set any new states.
+ *
+ * Return: None
+ */
+void cds_clear_fw_state(enum cds_fw_state);
+
+/**
+ * cds_get_fw_state() - Get current firmware state
+ *
+ * This API returns current firmware state stored in global context.
+ *
+ * Return: Firmware state enum
+ */
+enum cds_fw_state cds_get_fw_state(void);
+
 /**
  * cds_is_driver_loading() - Is driver load in progress
  *
@@ -140,6 +184,32 @@ static inline bool cds_is_load_or_unload_in_progress(void)
 		__CDS_IS_DRIVER_STATE(state, CDS_DRIVER_STATE_UNLOADING);
 }
 
+/**
+ * cds_is_fw_down() - Is FW down or not
+ *
+ * Return: true if FW is down and false otherwise.
+ */
+static inline bool cds_is_fw_down(void)
+{
+	enum cds_fw_state state = cds_get_fw_state();
+
+	return __CDS_IS_FW_STATE(state, BIT(CDS_FW_STATE_DOWN));
+}
+
+/**
+ * cds_set_fw_down() - Set or unset FW down bit
+ * @value: value to set
+ *
+ * Return: none
+ */
+static inline void cds_set_fw_down(uint8_t value)
+{
+	if (value)
+		cds_set_fw_state(CDS_FW_STATE_DOWN);
+	else
+		cds_clear_fw_state(CDS_FW_STATE_DOWN);
+}
+
 /**
  * cds_set_recovery_in_progress() - Set recovery in progress
  * @value: value to set

+ 1 - 0
core/cds/inc/cds_sched.h

@@ -187,6 +187,7 @@ typedef struct _cds_context_type {
 	qdf_event_t ProbeEvent;
 
 	uint32_t driver_state;
+	unsigned long fw_state;
 
 	qdf_event_t wmaCompleteEvent;
 

+ 47 - 4
core/cds/src/cds_api.c

@@ -526,6 +526,10 @@ QDF_STATUS cds_open(struct wlan_objmgr_psoc *psoc)
 	if (htc_wait_target(HTCHandle)) {
 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL,
 			  "%s: Failed to complete BMI phase", __func__);
+
+		if (!cds_is_fw_down())
+			QDF_BUG(0);
+
 		goto err_wma_close;
 	}
 	bmi_target_ready(scn, gp_cds_context->cfg_ctx);
@@ -728,12 +732,13 @@ QDF_STATUS cds_pre_enable(v_CONTEXT_t cds_context)
 	if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL,
 			  "Failed to get ready event from target firmware");
+
 		/*
-		 * Panic only if recovery is disabled, else return failure so
-		 * that driver load can fail gracefully. We cannot trigger self
-		 * recovery here because driver is not fully loaded yet.
+		 * Panic when the failure is not because the FW is down,
+		 * fail gracefully if FW is down allowing re-probing from
+		 * from the platform driver
 		 */
-		if (!cds_is_self_recovery_enabled())
+		if (!cds_is_fw_down())
 			QDF_BUG(0);
 
 		htc_stop(gp_cds_context->htc_ctx);
@@ -1301,6 +1306,44 @@ void cds_clear_driver_state(enum cds_driver_state state)
 	gp_cds_context->driver_state &= ~state;
 }
 
+enum cds_fw_state cds_get_fw_state(void)
+{
+	if (gp_cds_context == NULL) {
+		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
+			  "%s: global cds context is NULL", __func__);
+
+		return CDS_FW_STATE_UNINITIALIZED;
+	}
+
+	return gp_cds_context->fw_state;
+}
+
+void cds_set_fw_state(enum cds_fw_state state)
+{
+	if (gp_cds_context == NULL) {
+		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
+			  "%s: global cds context is NULL: %d", __func__,
+			  state);
+
+		return;
+	}
+
+	qdf_atomic_set_bit(state, &gp_cds_context->fw_state);
+}
+
+void cds_clear_fw_state(enum cds_fw_state state)
+{
+	if (gp_cds_context == NULL) {
+		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
+			  "%s: global cds context is NULL: %d", __func__,
+			  state);
+
+		return;
+	}
+
+	qdf_atomic_clear_bit(state, &gp_cds_context->fw_state);
+}
+
 /**
  * cds_alloc_context() - allocate a context within the CDS global Context
  * @p_cds_context: pointer to the global Vos context

+ 5 - 1
core/dp/htt/htt.c

@@ -726,8 +726,12 @@ int htt_htc_attach(struct htt_pdev_t *pdev, uint16_t service_id)
 
 	status = htc_connect_service(pdev->htc_pdev, &connect, &response);
 
-	if (status != A_OK)
+	if (status != A_OK) {
+		if (!cds_is_fw_down())
+			QDF_BUG(0);
+
 		return -EIO;       /* failure */
+	}
 
 	htt_update_endpoint(pdev, service_id, response.Endpoint);
 

+ 10 - 0
core/hdd/src/wlan_hdd_driver_ops.c

@@ -233,6 +233,9 @@ int hdd_hif_open(struct device *dev, void *bdev, const struct hif_bus_id *bid,
 	if (!QDF_IS_STATUS_SUCCESS(status)) {
 		hdd_err("hif_enable failed status: %d, reinit: %d",
 			status, reinit);
+		if (!cds_is_fw_down())
+			QDF_BUG(0);
+
 		ret = qdf_status_to_os_return(status);
 		goto err_hif_close;
 	} else {
@@ -365,6 +368,8 @@ static int wlan_hdd_probe(struct device *dev, void *bdev,
 	hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_INIT);
 	hdd_remove_pm_qos(dev);
 
+	cds_set_fw_down(false);
+
 	return 0;
 
 
@@ -375,6 +380,9 @@ err_hdd_deinit:
 		cds_set_load_in_progress(false);
 	hdd_allow_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_INIT);
 	hdd_remove_pm_qos(dev);
+
+	cds_set_fw_down(false);
+
 	return ret;
 }
 
@@ -1282,6 +1290,8 @@ static void wlan_hdd_pld_uevent(struct device *dev,
 {
 	if (uevent->uevent == PLD_RECOVERY)
 		cds_set_recovery_in_progress(true);
+	else if (uevent->uevent == PLD_FW_DOWN)
+		cds_set_fw_state(CDS_FW_STATE_DOWN);
 }
 
 #ifdef FEATURE_RUNTIME_PM

+ 9 - 4
core/hdd/src/wlan_hdd_main.c

@@ -7539,9 +7539,11 @@ static hdd_context_t *hdd_context_create(struct device *dev)
 		goto err_free_config;
 
 
-	pld_set_fw_log_mode(hdd_ctx->parent_dev,
-			    hdd_ctx->config->enable_fw_log);
+	ret = pld_set_fw_log_mode(hdd_ctx->parent_dev,
+			hdd_ctx->config->enable_fw_log);
 
+	if (ret && cds_is_fw_down())
+		goto err_deinit_hdd_context;
 
 	/* Uses to enabled logging after SSR */
 	hdd_ctx->fw_log_settings.enable = hdd_ctx->config->enable_fw_log;
@@ -9414,11 +9416,14 @@ err_exit_nl_srv:
 
 	cds_deinit_ini_config();
 err_hdd_free_context:
-	hdd_start_complete(ret);
+	if (cds_is_fw_down())
+		hdd_err("Not setting the complete event as fw is down");
+	else
+		hdd_start_complete(ret);
+
 	qdf_mc_timer_destroy(&hdd_ctx->iface_change_timer);
 	mutex_destroy(&hdd_ctx->iface_change_lock);
 	hdd_context_destroy(hdd_ctx);
-	QDF_BUG(1);
 	return -EIO;
 
 success:

+ 11 - 2
core/wma/src/wma_dev_if.c

@@ -2540,6 +2540,9 @@ static inline bool wma_crash_on_fw_timeout(bool crash_enabled)
 	if (cds_is_driver_recovering())
 		return false;
 
+	if (!cds_is_fw_down())
+		return false;
+
 	return crash_enabled;
 }
 
@@ -2794,7 +2797,10 @@ void wma_vdev_resp_timer(void *data)
 		params->status = QDF_STATUS_E_TIMEOUT;
 		WMA_LOGA("%s: WMA_SWITCH_CHANNEL_REQ timedout", __func__);
 
-		/* Trigger host crash if the flag is set */
+		/*
+		 * Trigger host crash if the flag is set or if the timeout
+		 * is not due to fw down
+		 */
 		if (wma_crash_on_fw_timeout(wma->fw_timeout_crash) == true)
 			QDF_BUG(0);
 		else
@@ -2826,7 +2832,10 @@ void wma_vdev_resp_timer(void *data)
 			qdf_mc_timer_stop(&tgt_req->event_timeout);
 			goto free_tgt_req;
 		}
-		/* Trigger host crash when vdev response timesout */
+		/*
+		 * Trigger host crash if the flag is set or if the timeout
+		 * is not due to fw down
+		 */
 		if (wma_crash_on_fw_timeout(wma->fw_timeout_crash) == true) {
 			QDF_BUG(0);
 			return;

+ 4 - 0
core/wma/src/wma_main.c

@@ -2628,6 +2628,10 @@ QDF_STATUS wma_pre_start(void *cds_ctx)
 						 wma_handle->htc_handle);
 	if (A_OK != status) {
 		WMA_LOGE("%s: wmi_unified_connect_htc_service", __func__);
+
+		if (!cds_is_fw_down())
+			QDF_BUG(0);
+
 		qdf_status = QDF_STATUS_E_FAULT;
 		goto end;
 	}