Browse Source

qcacld-3.0: report RSSI at disconnection time in STA mode

qcacld-2.0 to qcacld-3.0 propagation

Save RSSI of current connected AP when disconnection happens.
User space has to use getRSSI private IOCTL to query the RSSI value
after disconnected event.

Change-Id: I51ab69f2b0bda7c6b3a2689297ca56941471b1cc
CRs-Fixed: 910970
Sreelakshmi Konamki 8 years ago
parent
commit
58c7243aba

+ 1 - 0
core/hdd/inc/wlan_hdd_main.h

@@ -988,6 +988,7 @@ struct hdd_adapter_s {
 #endif
 
 	int8_t rssi;
+	int32_t rssi_on_disconnect;
 #ifdef WLAN_FEATURE_LPSS
 	bool rssi_send;
 #endif

+ 4 - 2
core/hdd/src/wlan_hdd_assoc.c

@@ -1569,8 +1569,10 @@ static QDF_STATUS hdd_dis_connect_handler(hdd_adapter_t *pAdapter,
 							);
 			}
 
-			hdd_info("sent disconnected event to nl80211, rssi: %d",
-				pAdapter->rssi);
+			hdd_info("sent disconnected event to nl80211, reason code %d",
+				(eCSR_ROAM_LOSTLINK == roamStatus) ?
+				pRoamInfo->reasonCode :
+				WLAN_REASON_UNSPECIFIED);
 		}
 		/*
 		 * During the WLAN uninitialization,supplicant is stopped

+ 9 - 0
core/hdd/src/wlan_hdd_cfg80211.h

@@ -3351,4 +3351,13 @@ struct cfg80211_bss *wlan_hdd_cfg80211_inform_bss_frame(hdd_adapter_t *pAdapter,
 #define NUM_NL80211_BANDS ((enum nl80211_band)IEEE80211_NUM_BANDS)
 #endif
 
+/**
+ * hdd_lost_link_info_cb() - callback function to get lost link information
+ * @context: HDD context
+ * @lost_link_info: lost link information
+ *
+ * Return: none
+ */
+void hdd_lost_link_info_cb(void *context,
+			struct sir_lost_link_info *lost_link_info);
 #endif

+ 6 - 0
core/hdd/src/wlan_hdd_main.c

@@ -8278,6 +8278,12 @@ int hdd_register_cb(hdd_context_t *hdd_ctx)
 	sme_set_link_layer_stats_ind_cb(hdd_ctx->hHal,
 				wlan_hdd_cfg80211_link_layer_stats_callback);
 
+	status = sme_set_lost_link_info_cb(hdd_ctx->hHal,
+					   hdd_lost_link_info_cb);
+	/* print error and not block the startup process */
+	if (!QDF_IS_STATUS_SUCCESS(status))
+		hdd_err("set lost link info callback failed");
+
 	wlan_hdd_dcc_register_for_dcc_stats_event(hdd_ctx);
 
 	EXIT();

+ 26 - 0
core/hdd/src/wlan_hdd_stats.c

@@ -1147,6 +1147,32 @@ void wlan_hdd_cfg80211_link_layer_stats_callback(void *ctx,
 	return;
 }
 
+void hdd_lost_link_info_cb(void *context,
+				  struct sir_lost_link_info *lost_link_info)
+{
+	hdd_context_t *hdd_ctx = (hdd_context_t *)context;
+	int status;
+	hdd_adapter_t *adapter;
+
+	status = wlan_hdd_validate_context(hdd_ctx);
+	if (0 != status)
+		return;
+
+	if (NULL == lost_link_info) {
+		hdd_err("lost_link_info is NULL");
+		return;
+	}
+
+	adapter = hdd_get_adapter_by_vdev(hdd_ctx, lost_link_info->vdev_id);
+	if (NULL == adapter) {
+		hdd_err("invalid adapter");
+		return;
+	}
+
+	adapter->rssi_on_disconnect = lost_link_info->rssi;
+	hdd_info("rssi on disconnect %d", adapter->rssi_on_disconnect);
+}
+
 const struct
 nla_policy
 	qca_wlan_vendor_ll_set_policy[QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX + 1] = {

+ 3 - 2
core/hdd/src/wlan_hdd_wext.c

@@ -1244,8 +1244,9 @@ QDF_STATUS wlan_hdd_get_rssi(hdd_adapter_t *pAdapter, int8_t *rssi_value)
 	pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
 
 	if (eConnectionState_Associated != pHddStaCtx->conn_info.connState) {
-		hdd_err("Not associated!, return last connected AP rssi!");
-		*rssi_value = pAdapter->rssi;
+		hdd_err("Not associated!, rssi on disconnect %d",
+			pAdapter->rssi_on_disconnect);
+		*rssi_value = pAdapter->rssi_on_disconnect;
 		return QDF_STATUS_SUCCESS;
 	}
 

+ 15 - 0
core/mac/inc/sir_api.h

@@ -5281,6 +5281,21 @@ typedef struct {
 	uint32_t led_x1;        /* led flashing parameter1 */
 } tSirLedFlashingReq, *tpSirLedFlashingReq;
 #endif
+
+/**
+ * struct sir_lost_link_info - lost link information structure.
+ *
+ * @vdev_id: vdev_id from WMA. some modules call sessionId.
+ * @rssi: rssi at disconnection time.
+ *
+ * driver uses this structure to communicate information collected at
+ * disconnection time.
+ */
+struct sir_lost_link_info {
+	uint32_t vdev_id;
+	int32_t rssi;
+};
+
 /* find the size of given member within a structure */
 #ifndef member_size
 #define member_size(type, member) (sizeof(((type *)0)->member))

+ 1 - 0
core/mac/inc/wni_api.h

@@ -261,6 +261,7 @@ enum eWniMsgTypes {
 	eWNI_SME_UPDATE_ACCESS_POLICY_VENDOR_IE,
 	eWNI_SME_DEFAULT_SCAN_IE,
 	eWNI_SME_ROAM_SCAN_OFFLOAD_REQ,
+	eWNI_SME_LOST_LINK_INFO_IND,
 	eWNI_SME_MSG_TYPES_END
 };
 

+ 11 - 0
core/mac/src/pe/include/lim_api.h

@@ -221,6 +221,17 @@ static inline QDF_STATUS pe_roam_synch_callback(tpAniSirGlobal mac_ctx,
 }
 #endif
 
+/**
+ * lim_update_lost_link_info() - update lost link information to SME
+ * @mac: global MAC handle
+ * @session: PE session
+ * @rssi: rssi value from the received frame
+ *
+ * Return: None
+ */
+void lim_update_lost_link_info(tpAniSirGlobal mac, tpPESession session,
+				int32_t rssi);
+
 /**
  * lim_mon_init_session() - create PE session for monitor mode operation
  * @mac_ptr: mac pointer

+ 33 - 0
core/mac/src/pe/lim/lim_api.c

@@ -2179,6 +2179,39 @@ tMgmtFrmDropReason lim_is_pkt_candidate_for_drop(tpAniSirGlobal pMac,
 	return eMGMT_DROP_NO_DROP;
 }
 
+void lim_update_lost_link_info(tpAniSirGlobal mac, tpPESession session,
+				int32_t rssi)
+{
+	struct sir_lost_link_info *lost_link_info;
+	tSirMsgQ mmh_msg;
+
+	if ((NULL == mac) || (NULL == session)) {
+		lim_log(mac, LOGE, FL("parameter NULL"));
+		return;
+	}
+	if (!LIM_IS_STA_ROLE(session)) {
+		lim_log(mac, LOGE, FL("not STA mode, do nothing"));
+		return;
+	}
+
+	lost_link_info = qdf_mem_malloc(sizeof(*lost_link_info));
+	if (NULL == lost_link_info) {
+		lim_log(mac, LOGE, FL("lost_link_info allocation failure"));
+		return;
+	}
+
+	lost_link_info->vdev_id = session->smeSessionId;
+	lost_link_info->rssi = rssi;
+	mmh_msg.type = eWNI_SME_LOST_LINK_INFO_IND;
+	mmh_msg.bodyptr = lost_link_info;
+	mmh_msg.bodyval = 0;
+	lim_log(mac, LOG1,
+		FL("post eWNI_SME_LOST_LINK_INFO_IND, bss_idx %d, rssi %d"),
+		lost_link_info->vdev_id, lost_link_info->rssi);
+
+	lim_sys_process_mmh_msg_api(mac, &mmh_msg, ePROT);
+}
+
 QDF_STATUS pe_acquire_global_lock(tAniSirLim *psPe)
 {
 	QDF_STATUS status = QDF_STATUS_E_INVAL;

+ 7 - 3
core/mac/src/pe/lim/lim_process_deauth_frame.c

@@ -81,10 +81,12 @@ lim_process_deauth_frame(tpAniSirGlobal pMac, uint8_t *pRxPacketInfo,
 #ifdef WLAN_FEATURE_11W
 	uint32_t frameLen;
 #endif
+	int32_t frame_rssi;
 
 	pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo);
 
 	pBody = WMA_GET_RX_MPDU_DATA(pRxPacketInfo);
+	frame_rssi = (int32_t)WMA_GET_RX_RSSI_NORMALIZED(pRxPacketInfo);
 
 	if (LIM_IS_STA_ROLE(psessionEntry) &&
 	    ((eLIM_SME_WT_DISASSOC_STATE == psessionEntry->limSmeState) ||
@@ -170,12 +172,12 @@ lim_process_deauth_frame(tpAniSirGlobal pMac, uint8_t *pRxPacketInfo,
 
 	PELOGE(lim_log(pMac, LOGE,
 		       FL("Received Deauth frame for Addr: " MAC_ADDRESS_STR
-			" (mlm state = %s,"
-			" sme state = %d systemrole  = %d) with reason code %d [%s] from "
+			"(mlm state = %s, sme state = %d systemrole = %d "
+			"RSSI = %d) with reason code %d [%s] from "
 			MAC_ADDRESS_STR), MAC_ADDR_ARRAY(pHdr->da),
 			lim_mlm_state_str(psessionEntry->limMlmState),
 			psessionEntry->limSmeState,
-			GET_LIM_SYSTEM_ROLE(psessionEntry),
+			GET_LIM_SYSTEM_ROLE(psessionEntry), frame_rssi,
 			reasonCode, lim_dot11_reason_str(reasonCode),
 			MAC_ADDR_ARRAY(pHdr->sa));
 	       )
@@ -593,6 +595,8 @@ lim_process_deauth_frame(tpAniSirGlobal pMac, uint8_t *pRxPacketInfo,
 	if (LIM_IS_STA_ROLE(psessionEntry))
 		wma_tx_abort(psessionEntry->smeSessionId);
 
+	lim_update_lost_link_info(pMac, psessionEntry, frame_rssi);
+
 	/* / Deauthentication from peer MAC entity */
 	if (LIM_IS_STA_ROLE(psessionEntry))
 		lim_post_sme_message(pMac, LIM_MLM_DEAUTH_IND,

+ 6 - 2
core/mac/src/pe/lim/lim_process_disassoc_frame.c

@@ -81,10 +81,13 @@ lim_process_disassoc_frame(tpAniSirGlobal pMac, uint8_t *pRxPacketInfo,
 #ifdef WLAN_FEATURE_11W
 	uint32_t frameLen;
 #endif
+	int32_t frame_rssi;
 
 	pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo);
 	pBody = WMA_GET_RX_MPDU_DATA(pRxPacketInfo);
 
+	frame_rssi = (int32_t)WMA_GET_RX_RSSI_NORMALIZED(pRxPacketInfo);
+
 	if (lim_is_group_addr(pHdr->sa)) {
 		/* Received Disassoc frame from a BC/MC address */
 		/* Log error and ignore it */
@@ -165,11 +168,11 @@ lim_process_disassoc_frame(tpAniSirGlobal pMac, uint8_t *pRxPacketInfo,
 
 	PELOG2(lim_log(pMac, LOGE,
 		       FL("Received Disassoc frame for Addr: " MAC_ADDRESS_STR
-			  "(mlm state=%s, sme state=%d),"
+			  "(mlm state=%s, sme state=%d RSSI=%d),"
 			  "with reason code %d [%s] from " MAC_ADDRESS_STR),
 		       MAC_ADDR_ARRAY(pHdr->da),
 		       lim_mlm_state_str(psessionEntry->limMlmState),
-		       psessionEntry->limSmeState, reasonCode,
+		       psessionEntry->limSmeState, frame_rssi, reasonCode,
 		       lim_dot11_reason_str(reasonCode), MAC_ADDR_ARRAY(pHdr->sa));
 	       )
 
@@ -368,6 +371,7 @@ lim_process_disassoc_frame(tpAniSirGlobal pMac, uint8_t *pRxPacketInfo,
 		return;
 	}
 
+	lim_update_lost_link_info(pMac, psessionEntry, frame_rssi);
 	lim_post_sme_message(pMac, LIM_MLM_DISASSOC_IND,
 			     (uint32_t *) &mlmDisassocInd);
 

+ 1 - 0
core/mac/src/sys/legacy/src/utils/src/mac_trace.c

@@ -368,6 +368,7 @@ uint8_t *mac_trace_get_sme_msg_string(uint16_t sme_msg)
 		CASE_RETURN_STRING(eWNI_SME_EXT_CHANGE_CHANNEL);
 		CASE_RETURN_STRING(eWNI_SME_EXT_CHANGE_CHANNEL_IND);
 		CASE_RETURN_STRING(eWNI_SME_REGISTER_P2P_ACK_CB);
+		CASE_RETURN_STRING(eWNI_SME_LOST_LINK_INFO_IND);
 		CASE_RETURN_STRING(eWNI_SME_MSG_TYPES_END);
 	default:
 		return (uint8_t *) "UNKNOWN";

+ 9 - 0
core/sme/inc/sme_api.h

@@ -1346,4 +1346,13 @@ QDF_STATUS sme_update_long_retry_limit_threshold(tHalHandle hal_handle,
 QDF_STATUS sme_update_sta_inactivity_timeout(tHalHandle hal_handle,
 		struct sme_sta_inactivity_timeout  *sta_inactivity_timer);
 
+/**
+ * sme_set_lost_link_info_cb() - plug in callback function for receiving
+ * @hal: HAL handle
+ * @cb: callback function
+ *
+ * Return: HAL status
+ */
+QDF_STATUS sme_set_lost_link_info_cb(tHalHandle hal,
+		void (*cb)(void *, struct sir_lost_link_info *));
 #endif /* #if !defined( __SME_API_H ) */

+ 2 - 0
core/sme/inc/sme_internal.h

@@ -246,6 +246,8 @@ typedef struct tagSmeStruct {
 	sme_send_oem_data_rsp_msg oem_data_rsp_callback;
 	void (*encrypt_decrypt_cb)(void *,
 			struct sir_encrypt_decrypt_rsp_params *);
+	void (*lost_link_info_cb)(void *context,
+			struct sir_lost_link_info *lost_link_info);
 } tSmeStruct, *tpSmeStruct;
 
 #endif /* #if !defined( __SMEINTERNAL_H ) */

+ 24 - 0
core/sme/src/common/sme_api.c

@@ -3068,6 +3068,12 @@ QDF_STATUS sme_process_msg(tHalHandle hHal, cds_msg_t *pMsg)
 	case eWNI_SME_NDP_PEER_DEPARTED_IND:
 		sme_ndp_msg_processor(pMac, pMsg);
 		break;
+	case eWNI_SME_LOST_LINK_INFO_IND:
+		if (pMac->sme.lost_link_info_cb)
+			pMac->sme.lost_link_info_cb(pMac->hHdd,
+				(struct sir_lost_link_info *)pMsg->bodyptr);
+		qdf_mem_free(pMsg->bodyptr);
+		break;
 	default:
 
 		if ((pMsg->type >= eWNI_SME_MSG_TYPES_BEGIN)
@@ -16997,6 +17003,24 @@ QDF_STATUS sme_update_tx_fail_cnt_threshold(tHalHandle hal_handle,
 				FL("Not able to post Tx fail count message to WDA"));
 		qdf_mem_free(tx_fail_cnt);
 	}
+	return status;
+}
+
+QDF_STATUS sme_set_lost_link_info_cb(tHalHandle hal,
+				void (*cb)(void *, struct sir_lost_link_info *))
+{
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	tpAniSirGlobal mac = PMAC_STRUCT(hal);
 
+	status = sme_acquire_global_lock(&mac->sme);
+	if (QDF_IS_STATUS_SUCCESS(status)) {
+		mac->sme.lost_link_info_cb = cb;
+		sme_release_global_lock(&mac->sme);
+		sms_log(mac, LOG1, FL("set lost link info callback"));
+	} else {
+		sms_log(mac, LOGE,
+			FL("sme_acquire_global_lock error status %d"),
+			status);
+	}
 	return status;
 }

+ 3 - 0
core/wma/inc/wma.h

@@ -1353,6 +1353,8 @@ struct extended_caps {
  * @wma_scan_comp_timer: scan completion timer
  * @dfs_phyerr_filter_offload: dfs phy error filter is offloaded or not
  * @suitable_ap_hb_failure: better ap found
+ * @suitable_ap_hb_failure_rssi: record the RSSI when suitable_ap_hb_failure
+ * for later usage to report RSSI at beacon miss scenario
  * @wma_ibss_power_save_params: IBSS Power Save config Parameters
  * @IsRArateLimitEnabled: RA rate limiti s enabled or not
  * @RArateLimitInterval: RA rate limit interval
@@ -1514,6 +1516,7 @@ typedef struct {
 	qdf_mc_timer_t wma_scan_comp_timer;
 	uint8_t dfs_phyerr_filter_offload;
 	bool suitable_ap_hb_failure;
+	uint32_t suitable_ap_hb_failure_rssi;
 	ibss_power_save_params wma_ibss_power_save_params;
 #ifdef FEATURE_WLAN_RA_FILTERING
 	bool IsRArateLimitEnabled;

+ 12 - 1
core/wma/inc/wma_internal.h

@@ -663,7 +663,8 @@ void wma_send_beacon(tp_wma_handle wma, tpSendbeaconParams bcn_info);
 void wma_set_keepalive_req(tp_wma_handle wma,
 				  tSirKeepAliveReq *keepalive);
 
-void wma_beacon_miss_handler(tp_wma_handle wma, uint32_t vdev_id);
+void wma_beacon_miss_handler(tp_wma_handle wma, uint32_t vdev_id,
+			     int32_t rssi);
 
 void wma_process_update_opmode(tp_wma_handle wma_handle,
 				      tUpdateVHTOpMode *update_vht_opmode);
@@ -1220,4 +1221,14 @@ static inline int wma_encrypt_decrypt_msg_handler(void *handle, uint8_t *data,
 }
 #endif
 
+/**
+ * wma_lost_link_info_handler() - collect lost link information and inform SME
+ * @wma: WMA handle
+ * @vdev_id: vdev ID
+ * @rssi: rssi at disconnection time
+ *
+ * Return: none
+ */
+void wma_lost_link_info_handler(tp_wma_handle wma, uint32_t vdev_id,
+				int32_t rssi);
 #endif

+ 46 - 3
core/wma/src/wma_features.c

@@ -3079,6 +3079,51 @@ static uint32_t wma_wow_get_wakelock_duration(int wake_reason)
 	return wake_lock_duration;
 }
 
+/**
+ * wma_wow_ap_lost_helper() - helper function to handle WOW_REASON_AP_ASSOC_LOST
+ * reason code and retrieve RSSI from the event.
+ * @wma: Pointer to wma handle
+ * @param: WMI_WOW_WAKEUP_HOST_EVENTID_param_tlvs buffer pointer
+ *
+ * Return: none
+ */
+static void wma_wow_ap_lost_helper(tp_wma_handle wma, void *param)
+{
+	WMI_WOW_WAKEUP_HOST_EVENTID_param_tlvs *param_buf;
+	WOW_EVENT_INFO_fixed_param *wake_info;
+	WMI_ROAM_EVENTID_param_tlvs event_param;
+	wmi_roam_event_fixed_param *roam_event;
+	u_int32_t wow_buf_pkt_len = 0;
+
+	param_buf = (WMI_WOW_WAKEUP_HOST_EVENTID_param_tlvs *) param;
+	wake_info = param_buf->fixed_param;
+	WMA_LOGA("%s: Beacon miss indication on vdev %d",
+		__func__, wake_info->vdev_id);
+
+	if (NULL == param_buf->wow_packet_buffer) {
+		WMA_LOGE("%s: invalid wow packet buffer", __func__);
+		goto exit_handler;
+	}
+
+	qdf_mem_copy((u_int8_t *) &wow_buf_pkt_len,
+		param_buf->wow_packet_buffer, 4);
+	WMA_LOGD("wow_packet_buffer dump");
+	qdf_trace_hex_dump(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_DEBUG,
+		param_buf->wow_packet_buffer, wow_buf_pkt_len);
+	if (wow_buf_pkt_len >= sizeof(event_param)) {
+		roam_event = (wmi_roam_event_fixed_param *)
+			(param_buf->wow_packet_buffer + 4);
+		wma_beacon_miss_handler(wma, wake_info->vdev_id,
+			roam_event->rssi);
+		return;
+	}
+
+exit_handler:
+	/* in the case that no RSSI is available from the event */
+	WMA_LOGE("%s: rssi is not available from wow_packet_buffer", __func__);
+	wma_beacon_miss_handler(wma, wake_info->vdev_id, 0);
+}
+
 /**
  * wma_wow_wakeup_host_event() - wakeup host event handler
  * @handle: wma handle
@@ -3181,9 +3226,7 @@ int wma_wow_wakeup_host_event(void *handle, uint8_t *event,
 
 	case WOW_REASON_AP_ASSOC_LOST:
 		wake_lock_duration = WMA_BMISS_EVENT_WAKE_LOCK_DURATION;
-		WMA_LOGA("Beacon miss indication on vdev %x",
-			 wake_info->vdev_id);
-		wma_beacon_miss_handler(wma, wake_info->vdev_id);
+		wma_wow_ap_lost_helper(wma, param_buf);
 		break;
 #ifdef FEATURE_WLAN_AUTO_SHUTDOWN
 	case WOW_REASON_HOST_AUTO_SHUTDOWN:

+ 10 - 4
core/wma/src/wma_mgmt.c

@@ -380,7 +380,8 @@ int wma_peer_sta_kickout_event_handler(void *handle, u8 *event, u32 len)
 			 */
 			WMA_LOGW("%s: WMI_PEER_STA_KICKOUT_REASON_XRETRY event for STA",
 				__func__);
-			wma_beacon_miss_handler(wma, vdev_id);
+			wma_beacon_miss_handler(wma, vdev_id,
+						kickout_event->rssi);
 			goto exit_handler;
 		}
 		break;
@@ -406,7 +407,8 @@ int wma_peer_sta_kickout_event_handler(void *handle, u8 *event, u32 len)
 			 */
 			WMA_LOGW("%s: WMI_PEER_STA_KICKOUT_REASON_UNSPECIFIED event for STA",
 				__func__);
-			wma_beacon_miss_handler(wma, vdev_id);
+			wma_beacon_miss_handler(wma, vdev_id,
+						kickout_event->rssi);
 			goto exit_handler;
 		}
 		break;
@@ -441,7 +443,8 @@ int wma_peer_sta_kickout_event_handler(void *handle, u8 *event, u32 len)
 	del_sta_ctx->rssi = kickout_event->rssi + WMA_TGT_NOISE_FLOOR_DBM;
 	wma_send_msg(wma, SIR_LIM_DELETE_STA_CONTEXT_IND, (void *)del_sta_ctx,
 		     0);
-
+	wma_lost_link_info_handler(wma, vdev_id, kickout_event->rssi +
+						 WMA_TGT_NOISE_FLOOR_DBM);
 exit_handler:
 	WMA_LOGD("%s: Exit", __func__);
 	return 0;
@@ -2452,12 +2455,13 @@ void wma_set_keepalive_req(tp_wma_handle wma,
  * wma_beacon_miss_handler() - beacon miss event handler
  * @wma: wma handle
  * @vdev_id: vdev id
+ * @riis: rssi value
  *
  * This function send beacon miss indication to upper layers.
  *
  * Return: none
  */
-void wma_beacon_miss_handler(tp_wma_handle wma, uint32_t vdev_id)
+void wma_beacon_miss_handler(tp_wma_handle wma, uint32_t vdev_id, int32_t rssi)
 {
 	tSirSmeMissedBeaconInd *beacon_miss_ind;
 
@@ -2473,6 +2477,8 @@ void wma_beacon_miss_handler(tp_wma_handle wma, uint32_t vdev_id)
 	beacon_miss_ind->bssIdx = vdev_id;
 
 	wma_send_msg(wma, WMA_MISSED_BEACON_IND, (void *)beacon_miss_ind, 0);
+	wma_lost_link_info_handler(wma, vdev_id, rssi +
+						 WMA_TGT_NOISE_FLOOR_DBM);
 }
 
 /**

+ 5 - 2
core/wma/src/wma_scan_roam.c

@@ -1922,7 +1922,8 @@ QDF_STATUS wma_process_roaming_config(tp_wma_handle wma_handle,
 			WMA_LOGE("%s: Sending heartbeat failure after preauth failures",
 				__func__);
 			wma_beacon_miss_handler(wma_handle,
-						roam_req->sessionId);
+				roam_req->sessionId,
+				wma_handle->suitable_ap_hb_failure_rssi);
 			wma_handle->suitable_ap_hb_failure = false;
 		}
 		break;
@@ -5809,7 +5810,8 @@ int wma_roam_event_callback(WMA_HANDLE handle, uint8_t *event_buf,
 	switch (wmi_event->reason) {
 	case WMI_ROAM_REASON_BMISS:
 		WMA_LOGD("Beacon Miss for vdevid %x", wmi_event->vdev_id);
-		wma_beacon_miss_handler(wma_handle, wmi_event->vdev_id);
+		wma_beacon_miss_handler(wma_handle, wmi_event->vdev_id,
+					wmi_event->rssi);
 		break;
 	case WMI_ROAM_REASON_BETTER_AP:
 		WMA_LOGD("%s:Better AP found for vdevid %x, rssi %d", __func__,
@@ -5819,6 +5821,7 @@ int wma_roam_event_callback(WMA_HANDLE handle, uint8_t *event_buf,
 		break;
 	case WMI_ROAM_REASON_SUITABLE_AP:
 		wma_handle->suitable_ap_hb_failure = true;
+		wma_handle->suitable_ap_hb_failure_rssi = wmi_event->rssi;
 		WMA_LOGD("%s:Bmiss scan AP found for vdevid %x, rssi %d",
 			 __func__, wmi_event->vdev_id, wmi_event->rssi);
 		wma_roam_better_ap_handler(wma_handle, wmi_event->vdev_id);

+ 132 - 0
core/wma/src/wma_utils.c

@@ -290,6 +290,85 @@ rate_found:
 	return match_rate ? index : INVALID_MCS_IDX;
 }
 
+/**
+ * wma_peek_vdev_req() - peek what request message is queued for response.
+ *			 the function does not delete the node after found
+ * @wma: WMA handle
+ * @vdev_id: vdev ID
+ * @type: request message type
+ *
+ * Return: the request message found
+ */
+static struct wma_target_req *wma_peek_vdev_req(tp_wma_handle wma,
+						uint8_t vdev_id, uint8_t type)
+{
+	struct wma_target_req *req_msg = NULL;
+	bool found = false;
+	qdf_list_node_t *node1 = NULL, *node2 = NULL;
+
+	qdf_spin_lock_bh(&wma->vdev_respq_lock);
+	if (QDF_STATUS_SUCCESS != qdf_list_peek_front(&wma->vdev_resp_queue,
+							&node2)) {
+		qdf_spin_unlock_bh(&wma->vdev_respq_lock);
+		WMA_LOGE(FL("unable to get target req from vdev resp queue"));
+		return NULL;
+	}
+
+	do {
+		node1 = node2;
+		req_msg = qdf_container_of(node1, struct wma_target_req, node);
+		if (req_msg->vdev_id != vdev_id)
+			continue;
+		if (req_msg->type != type)
+			continue;
+
+		found = true;
+		break;
+	} while (QDF_STATUS_SUCCESS == qdf_list_peek_next(&wma->vdev_resp_queue,
+							  node1, &node2));
+	qdf_spin_unlock_bh(&wma->vdev_respq_lock);
+	if (!found) {
+		WMA_LOGP(FL("target request not found for vdev_id %d type %d"),
+			 vdev_id, type);
+		return NULL;
+	}
+	WMA_LOGD(FL("target request found for vdev id: %d type %d msg %d"),
+		 vdev_id, type, req_msg->msg_type);
+	return req_msg;
+}
+
+void wma_lost_link_info_handler(tp_wma_handle wma, uint32_t vdev_id,
+					int32_t rssi)
+{
+	struct sir_lost_link_info *lost_link_info;
+	QDF_STATUS qdf_status;
+	cds_msg_t sme_msg = {0};
+
+	/* report lost link information only for STA mode */
+	if (wma->interfaces[vdev_id].vdev_up &&
+	    (WMI_VDEV_TYPE_STA == wma->interfaces[vdev_id].type) &&
+	    (0 == wma->interfaces[vdev_id].sub_type)) {
+		lost_link_info = qdf_mem_malloc(sizeof(*lost_link_info));
+		if (NULL == lost_link_info) {
+			WMA_LOGE("%s: failed to allocate memory", __func__);
+			return;
+		}
+		lost_link_info->vdev_id = vdev_id;
+		lost_link_info->rssi = rssi;
+		sme_msg.type = eWNI_SME_LOST_LINK_INFO_IND;
+		sme_msg.bodyptr = lost_link_info;
+		sme_msg.bodyval = 0;
+		WMA_LOGI("%s: post msg to SME, bss_idx %d, rssi %d",  __func__,
+			 lost_link_info->vdev_id, lost_link_info->rssi);
+
+		qdf_status = cds_mq_post_message(QDF_MODULE_ID_SME, &sme_msg);
+		if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
+			WMA_LOGE("%s: fail to post msg to SME", __func__);
+			qdf_mem_free(lost_link_info);
+		}
+	}
+}
+
 /**
  * host_map_smps_mode() - map fw smps mode to tSmpsModeValue
  * @fw_smps_mode: fw smps mode
@@ -1230,6 +1309,58 @@ static void wma_update_pdev_stats(tp_wma_handle wma,
 	}
 }
 
+/**
+ * wma_vdev_stats_lost_link_helper() - helper function to extract
+ * lost link information from vdev statistics event while deleting BSS.
+ * @wma: WMA handle
+ * @vdev_stats: statistics information from firmware
+ *
+ * This is for informing HDD to collect lost link information while
+ * disconnection. Following conditions to check
+ * 1. vdev is up
+ * 2. bssid is zero. When handling DELETE_BSS request message, it sets bssid to
+ * zero, hence add the check here to indicate the event comes during deleting
+ * BSS
+ * 3. DELETE_BSS is the request message queued. Put this condition check on the
+ * last one as it consumes more resource searching entries in the  list
+ *
+ * Return: none
+ */
+static void wma_vdev_stats_lost_link_helper(tp_wma_handle wma,
+					    wmi_vdev_stats *vdev_stats)
+{
+	struct wma_txrx_node *node;
+	int32_t rssi;
+	struct wma_target_req *req_msg;
+	static const uint8_t zero_mac[QDF_MAC_ADDR_SIZE] = {0};
+
+	node = &wma->interfaces[vdev_stats->vdev_id];
+	if (node->vdev_up &&
+	    qdf_mem_cmp(node->bssid, zero_mac, QDF_MAC_ADDR_SIZE)) {
+		req_msg = wma_peek_vdev_req(wma, vdev_stats->vdev_id,
+					    WMA_TARGET_REQ_TYPE_VDEV_STOP);
+		if ((NULL == req_msg) ||
+		    (WMA_DELETE_BSS_REQ != req_msg->msg_type)) {
+			WMA_LOGD(FL("cannot find DELETE_BSS request message"));
+			return;
+		 }
+		WMA_LOGD(FL("get vdev id %d, beancon snr %d, data snr %d"),
+			vdev_stats->vdev_id,
+			vdev_stats->vdev_snr.bcn_snr,
+			vdev_stats->vdev_snr.dat_snr);
+		if (WMA_TGT_INVALID_SNR != vdev_stats->vdev_snr.bcn_snr)
+			rssi = vdev_stats->vdev_snr.bcn_snr;
+		else if (WMA_TGT_INVALID_SNR != vdev_stats->vdev_snr.dat_snr)
+			rssi = vdev_stats->vdev_snr.dat_snr;
+		else
+			rssi = WMA_TGT_INVALID_SNR;
+
+		/* Get the absolute rssi value from the current rssi value */
+		rssi = rssi + WMA_TGT_NOISE_FLOOR_DBM;
+		wma_lost_link_info_handler(wma, vdev_stats->vdev_id, rssi);
+	}
+}
+
 /**
  * wma_update_vdev_stats() - update vdev stats
  * @wma: wma handle
@@ -1357,6 +1488,7 @@ static void wma_update_vdev_stats(tp_wma_handle wma,
 
 		node->psnr_req = NULL;
 	}
+	wma_vdev_stats_lost_link_helper(wma, vdev_stats);
 }
 
 /**