wlan: logger thread implemenation for fw logging

Adding support for:
1. queue/dequeue of vos pkt in logger thread.
2. Limit max number of pkts in a queue.
3. Detach skb and broadcast to userspace app
4. flush pkts in queue during rmmod/SSR
5. pkt_type will be provided to logger thread

Change-Id: I52474b5459b27895f5cb5352e451281f49f14f1c
CRs-Fixed: 845692
diff --git a/CORE/SVC/src/logging/wlan_logging_sock_svc.c b/CORE/SVC/src/logging/wlan_logging_sock_svc.c
index 1c1eeb6..7010e0c 100755
--- a/CORE/SVC/src/logging/wlan_logging_sock_svc.c
+++ b/CORE/SVC/src/logging/wlan_logging_sock_svc.c
@@ -49,17 +49,16 @@
 #define ANI_NL_MSG_LOG_TYPE 89
 #define ANI_NL_MSG_READY_IND_TYPE 90
 #define ANI_NL_MSG_LOG_PKT_TYPE 91
+#define ANI_NL_MSG_FW_LOG_PKT_TYPE 92
 #define INVALID_PID -1
 
 #define MAX_LOGMSG_LENGTH 4096
-#define LOGGER_PKT_POST_MASK   0x001
+#define LOGGER_MGMT_DATA_PKT_POST_MASK   0x001
 #define HOST_LOG_POST_MASK   0x002
+#define LOGGER_FW_LOG_PKT_POST_MASK   0x003
 
-#define LOGGER_MAX_PKT_Q_LEN   (8)
-
-enum {
-	LOG_PKT_TYPE_DATA_MGMT = 0x1
-};
+#define LOGGER_MAX_DATA_MGMT_PKT_Q_LEN   (8)
+#define LOGGER_MAX_FW_LOG_PKT_Q_LEN   (16)
 
 struct log_msg {
 	struct list_head node;
@@ -91,6 +90,12 @@
 	unsigned int data_mgmt_pkt_qcnt;
 	/* Lock to synchronize of queue/dequeue of pkts in logger pkt queue */
 	spinlock_t data_mgmt_pkt_lock;
+	/* Points to head of logger fw log pkt queue */
+	vos_pkt_t *fw_log_pkt_queue;
+	/* Holds number of pkts in fw log vos pkt queue */
+	unsigned int fw_log_pkt_qcnt;
+	/* Lock to synchronize of queue/dequeue of pkts in fw log pkt queue */
+	spinlock_t fw_log_pkt_lock;
 	/* Wait queue for Logger thread */
 	wait_queue_head_t wait_queue;
 	/* Logger thread */
@@ -103,6 +108,8 @@
 	unsigned int drop_count;
 	/* Holds number of dropped vos pkts*/
 	unsigned int pkt_drop_cnt;
+	/* Holds number of dropped fw log vos pkts*/
+	unsigned int fw_log_pkt_drop_cnt;
 	/* current logbuf to which the log will be filled to */
 	struct log_msg *pcur_node;
 	/* Event flag used for wakeup and post indication*/
@@ -133,8 +140,7 @@
 	static int nlmsg_seq;
 
 	if (radio < 0 || radio > ANI_MAX_RADIOS) {
-		LOGGING_TRACE(VOS_TRACE_LEVEL_ERROR,
-				"%s: invalid radio id [%d]",
+		pr_err("%s: invalid radio id [%d]",
 				__func__, radio);
 		return -EINVAL;
 	}
@@ -143,16 +149,14 @@
 	tot_msg_len = NLMSG_SPACE(payload_len);
 	skb = dev_alloc_skb(tot_msg_len);
 	if (skb == NULL) {
-		LOGGING_TRACE(VOS_TRACE_LEVEL_ERROR,
-				"%s: dev_alloc_skb() failed for msg size[%d]",
+		pr_err("%s: dev_alloc_skb() failed for msg size[%d]",
 				__func__, tot_msg_len);
 		return -ENOMEM;
 	}
 	nlh = nlmsg_put(skb, pid, nlmsg_seq++, src_mod, payload_len,
 		NLM_F_REQUEST);
 	if (NULL == nlh) {
-		LOGGING_TRACE(VOS_TRACE_LEVEL_ERROR,
-				"%s: nlmsg_put() failed for msg size[%d]",
+		pr_err("%s: nlmsg_put() failed for msg size[%d]",
 				__func__, tot_msg_len);
 		kfree_skb(skb);
 		return -ENOMEM;
@@ -164,8 +168,7 @@
 
 	err = nl_srv_ucast(skb, pid, MSG_DONTWAIT);
 	if (err) {
-		LOGGING_TRACE(VOS_TRACE_LEVEL_INFO,
-				"%s: Failed sending Msg Type [0x%X] to pid[%d]\n",
+		pr_info("%s: Failed sending Msg Type [0x%X] to pid[%d]\n",
 				__func__, wmsg->type, pid);
 	}
 
@@ -351,6 +354,99 @@
 	return 0;
 }
 
+static int send_fw_log_pkt_to_user(void)
+{
+	int ret = -1;
+	int extra_header_len, nl_payload_len;
+	struct sk_buff *skb = NULL;
+	static int nlmsg_seq;
+	vos_pkt_t *current_pkt;
+	vos_pkt_t *next_pkt;
+	VOS_STATUS status = VOS_STATUS_E_FAILURE;
+	unsigned long flags;
+
+	tAniNlHdr msg_header;
+
+	do {
+		spin_lock_irqsave(&gwlan_logging.fw_log_pkt_lock, flags);
+
+		if (!gwlan_logging.fw_log_pkt_queue) {
+			spin_unlock_irqrestore(
+				&gwlan_logging.fw_log_pkt_lock, flags);
+			return -EIO;
+		}
+
+		/* pick first pkt from queued chain */
+		current_pkt = gwlan_logging.fw_log_pkt_queue;
+
+		/* get the pointer to the next packet in the chain */
+		status = vos_pkt_walk_packet_chain(current_pkt, &next_pkt,
+							TRUE);
+
+		/* both "success" and "empty" are acceptable results */
+		if (!((status == VOS_STATUS_SUCCESS) ||
+					(status == VOS_STATUS_E_EMPTY))) {
+			++gwlan_logging.fw_log_pkt_drop_cnt;
+			spin_unlock_irqrestore(
+				&gwlan_logging.fw_log_pkt_lock, flags);
+			pr_err("%s: Failure walking packet chain", __func__);
+			return -EIO;
+		}
+
+		/* update queue head with next pkt ptr which could be NULL */
+		gwlan_logging.fw_log_pkt_queue = next_pkt;
+		--gwlan_logging.fw_log_pkt_qcnt;
+		spin_unlock_irqrestore(&gwlan_logging.fw_log_pkt_lock, flags);
+
+		status = vos_pkt_get_os_packet(current_pkt, (v_VOID_t **)&skb,
+						TRUE);
+		if (!VOS_IS_STATUS_SUCCESS(status)) {
+			++gwlan_logging.fw_log_pkt_drop_cnt;
+			pr_err("%s: Failure extracting skb from vos pkt",
+				__func__);
+			return -EIO;
+		}
+
+		/*return vos pkt since skb is already detached */
+		vos_pkt_return_packet(current_pkt);
+
+		extra_header_len = sizeof(msg_header.radio) + sizeof(tAniHdr);
+		nl_payload_len = NLMSG_ALIGN(extra_header_len + skb->len);
+
+		msg_header.nlh.nlmsg_type = ANI_NL_MSG_LOG;
+		msg_header.nlh.nlmsg_len = nl_payload_len;
+		msg_header.nlh.nlmsg_flags = NLM_F_REQUEST;
+		msg_header.nlh.nlmsg_pid = gapp_pid;
+		msg_header.nlh.nlmsg_seq = nlmsg_seq++;
+
+		msg_header.radio = 0;
+
+		msg_header.wmsg.type = ANI_NL_MSG_FW_LOG_PKT_TYPE;
+		msg_header.wmsg.length = skb->len;
+
+		if (unlikely(skb_headroom(skb) < sizeof(msg_header))) {
+			pr_err("VPKT [%d]: Insufficient headroom, head[%p],"
+				" data[%p], req[%zu]", __LINE__, skb->head,
+				skb->data, sizeof(msg_header));
+			return -EIO;
+		}
+
+		vos_mem_copy(skb_push(skb, sizeof(msg_header)), &msg_header,
+							sizeof(msg_header));
+
+		ret = nl_srv_bcast(skb);
+		if (ret < 0) {
+			pr_info("%s: Send Failed %d drop_count = %u\n",
+				__func__, ret, ++gwlan_logging.fw_log_pkt_drop_cnt);
+		} else {
+			ret = 0;
+		}
+
+	} while (next_pkt);
+
+	return ret;
+}
+
 static int send_data_mgmt_log_pkt_to_user(void)
 {
 	int ret = -1;
@@ -367,6 +463,12 @@
 	do {
 		spin_lock_irqsave(&gwlan_logging.data_mgmt_pkt_lock, flags);
 
+		if (!gwlan_logging.data_mgmt_pkt_queue) {
+			spin_unlock_irqrestore(
+				&gwlan_logging.data_mgmt_pkt_lock, flags);
+			return -EIO;
+		}
+
 		/* pick first pkt from queued chain */
 		current_pkt = gwlan_logging.data_mgmt_pkt_queue;
 
@@ -380,8 +482,7 @@
 			++gwlan_logging.pkt_drop_cnt;
 			spin_unlock_irqrestore(
 				&gwlan_logging.data_mgmt_pkt_lock, flags);
-			LOGGING_TRACE(VOS_TRACE_LEVEL_ERROR,
-				"%s: Failure walking packet chain", __func__);
+			pr_err("%s: Failure walking packet chain", __func__);
 			return -EIO;
 		}
 
@@ -394,8 +495,8 @@
 						TRUE);
 		if (!VOS_IS_STATUS_SUCCESS(status)) {
 			++gwlan_logging.pkt_drop_cnt;
-			LOGGING_TRACE(VOS_TRACE_LEVEL_ERROR,
-			"%s: Failure extracting skb from vos pkt", __func__);
+			pr_err("%s: Failure extracting skb from vos pkt",
+				__func__);
 			return -EIO;
 		}
 
@@ -420,8 +521,7 @@
 		msg_header.frameSize = WLAN_MGMT_LOGGING_FRAMESIZE_128BYTES;
 
 		if (unlikely(skb_headroom(skb) < sizeof(msg_header))) {
-			LOGGING_TRACE(VOS_TRACE_LEVEL_ERROR,
-				"VPKT [%d]: Insufficient headroom, head[%p],"
+			pr_err("VPKT [%d]: Insufficient headroom, head[%p],"
 				" data[%p], req[%zu]", __LINE__, skb->head,
 				skb->data, sizeof(msg_header));
 			return -EIO;
@@ -432,8 +532,7 @@
 
 		ret =  nl_srv_bcast(skb);
 		if (ret < 0) {
-			LOGGING_TRACE(VOS_TRACE_LEVEL_INFO,
-				"%s: Send Failed %d drop_count = %u\n",
+			pr_info("%s: Send Failed %d drop_count = %u\n",
 				__func__, ret, ++gwlan_logging.pkt_drop_cnt);
 		} else {
 			ret = 0;
@@ -551,7 +650,7 @@
 		  gwlan_logging.wait_queue,
 		  (test_bit(HOST_LOG_POST_MASK, &gwlan_logging.event_flag) ||
 		  gwlan_logging.exit ||
-		  test_bit(LOGGER_PKT_POST_MASK, &gwlan_logging.event_flag)));
+		  test_bit(LOGGER_MGMT_DATA_PKT_POST_MASK, &gwlan_logging.event_flag)));
 
 		if (ret_wait_status == -ERESTARTSYS) {
 			pr_err("%s: wait_event return -ERESTARTSYS", __func__);
@@ -571,7 +670,12 @@
 			}
 		}
 
-		if (test_and_clear_bit(LOGGER_PKT_POST_MASK,
+		if (test_and_clear_bit(LOGGER_FW_LOG_PKT_POST_MASK,
+			&gwlan_logging.event_flag)) {
+			send_fw_log_pkt_to_user();
+		}
+
+		if (test_and_clear_bit(LOGGER_MGMT_DATA_PKT_POST_MASK,
 			&gwlan_logging.event_flag)) {
 			send_data_mgmt_log_pkt_to_user();
 		}
@@ -599,8 +703,7 @@
 	type = wnl->nlh.nlmsg_type;
 
 	if (radio < 0 || radio > ANI_MAX_RADIOS) {
-		LOGGING_TRACE(VOS_TRACE_LEVEL_ERROR,
-				"%s: invalid radio id [%d]\n",
+		pr_err("%s: invalid radio id [%d]\n",
 				__func__, radio);
 		return -EINVAL;
 	}
@@ -628,8 +731,7 @@
 	ret = wlan_send_sock_msg_to_app(&wnl->wmsg, 0,
 			ANI_NL_MSG_LOG, wnl->nlh.nlmsg_pid);
 	if (ret < 0) {
-		LOGGING_TRACE(VOS_TRACE_LEVEL_ERROR,
-				"wlan_send_sock_msg_to_app: failed");
+		pr_err("wlan_send_sock_msg_to_app: failed");
 	}
 
 	return ret;
@@ -673,7 +775,7 @@
 	init_waitqueue_head(&gwlan_logging.wait_queue);
 	gwlan_logging.exit = false;
 	clear_bit(HOST_LOG_POST_MASK, &gwlan_logging.event_flag);
-	clear_bit(LOGGER_PKT_POST_MASK, &gwlan_logging.event_flag);
+	clear_bit(LOGGER_MGMT_DATA_PKT_POST_MASK, &gwlan_logging.event_flag);
 	init_completion(&gwlan_logging.shutdown_comp);
 	gwlan_logging.thread = kthread_create(wlan_logging_thread, NULL,
 					"wlan_logging_thread");
@@ -715,6 +817,20 @@
 					flags);
 	}
 
+	spin_lock_irqsave(&gwlan_logging.fw_log_pkt_lock, flags);
+	if (NULL != gwlan_logging.fw_log_pkt_queue) {
+		pkt_queue_head = gwlan_logging.fw_log_pkt_queue;
+		gwlan_logging.fw_log_pkt_queue = NULL;
+		gwlan_logging.fw_log_pkt_drop_cnt = 0;
+		gwlan_logging.fw_log_pkt_qcnt = 0;
+		spin_unlock_irqrestore(&gwlan_logging.fw_log_pkt_lock,
+					flags);
+		vos_pkt_return_packet(pkt_queue_head);
+	} else {
+		spin_unlock_irqrestore(&gwlan_logging.fw_log_pkt_lock,
+					flags);
+	}
+
 	return 0;
 }
 
@@ -753,6 +869,7 @@
 {
 	spin_lock_init(&gwlan_logging.spin_lock);
 	spin_lock_init(&gwlan_logging.data_mgmt_pkt_lock);
+	spin_lock_init(&gwlan_logging.fw_log_pkt_lock);
 	gapp_pid = INVALID_PID;
 	gwlan_logging.pcur_node = NULL;
 
@@ -767,36 +884,15 @@
        return 0;
 }
 
-int wlan_queue_logpkt_for_app(vos_pkt_t *pPacket, uint32 pkt_type)
+int wlan_queue_data_mgmt_pkt_for_app(vos_pkt_t *pPacket)
 {
 	unsigned long flags;
 	vos_pkt_t *next_pkt;
 	vos_pkt_t *free_pkt;
 	VOS_STATUS status = VOS_STATUS_E_FAILURE;
 
-	if (pPacket == NULL) {
-		LOGGING_TRACE(VOS_TRACE_LEVEL_ERROR,
-				"%s: Null param", __func__);
-		VOS_ASSERT(0);
-		return VOS_STATUS_E_FAILURE;
-	}
-
-	if (pkt_type != LOG_PKT_TYPE_DATA_MGMT) {
-		LOGGING_TRACE(VOS_TRACE_LEVEL_INFO,
-				"%s: Unknown pkt received", __func__);
-	}
-
-	if (gwlan_logging.is_active == false) {
-		/*return all packets queued*/
-		wlan_logging_flush_pkt_queue();
-
-		/*return currently received pkt*/
-		vos_pkt_return_packet(pPacket);
-		return VOS_STATUS_E_FAILURE;
-	}
-
 	spin_lock_irqsave(&gwlan_logging.data_mgmt_pkt_lock, flags);
-	if (gwlan_logging.data_mgmt_pkt_qcnt >= LOGGER_MAX_PKT_Q_LEN) {
+	if (gwlan_logging.data_mgmt_pkt_qcnt >= LOGGER_MAX_DATA_MGMT_PKT_Q_LEN) {
 		status = vos_pkt_walk_packet_chain(
 			gwlan_logging.data_mgmt_pkt_queue, &next_pkt, TRUE);
 		/*both "success" and "empty" are acceptable results*/
@@ -805,8 +901,7 @@
 			++gwlan_logging.pkt_drop_cnt;
 			spin_unlock_irqrestore(
 				&gwlan_logging.data_mgmt_pkt_lock, flags);
-			LOGGING_TRACE(VOS_TRACE_LEVEL_ERROR,
-				"%s: Failure walking packet chain", __func__);
+			pr_err("%s: Failure walking packet chain", __func__);
 			/*keep returning pkts to avoid low resource cond*/
 			vos_pkt_return_packet(pPacket);
 			return VOS_STATUS_E_FAILURE;
@@ -836,7 +931,7 @@
 
 	spin_unlock_irqrestore(&gwlan_logging.data_mgmt_pkt_lock, flags);
 
-	set_bit(LOGGER_PKT_POST_MASK, &gwlan_logging.event_flag);
+	set_bit(LOGGER_MGMT_DATA_PKT_POST_MASK, &gwlan_logging.event_flag);
 	wake_up_interruptible(&gwlan_logging.wait_queue);
 
 	return VOS_STATUS_SUCCESS;
@@ -854,5 +949,94 @@
 	set_default_logtoapp_log_level();
 }
 
+int wlan_queue_fw_log_pkt_for_app(vos_pkt_t *pPacket)
+{
+	unsigned long flags;
+	vos_pkt_t *next_pkt;
+	vos_pkt_t *free_pkt;
+	VOS_STATUS status = VOS_STATUS_E_FAILURE;
+
+	spin_lock_irqsave(&gwlan_logging.fw_log_pkt_lock, flags);
+	if (gwlan_logging.fw_log_pkt_qcnt >= LOGGER_MAX_FW_LOG_PKT_Q_LEN) {
+		status = vos_pkt_walk_packet_chain(
+			gwlan_logging.fw_log_pkt_queue, &next_pkt, TRUE);
+		/*both "success" and "empty" are acceptable results*/
+		if (!((status == VOS_STATUS_SUCCESS) ||
+				(status == VOS_STATUS_E_EMPTY))) {
+			++gwlan_logging.fw_log_pkt_drop_cnt;
+			spin_unlock_irqrestore(
+				&gwlan_logging.fw_log_pkt_lock, flags);
+			pr_err("%s: Failure walking packet chain", __func__);
+			/*keep returning pkts to avoid low resource cond*/
+			vos_pkt_return_packet(pPacket);
+			return VOS_STATUS_E_FAILURE;
+		}
+
+		free_pkt = gwlan_logging.fw_log_pkt_queue;
+		gwlan_logging.fw_log_pkt_queue = next_pkt;
+		/*returning head of pkt queue. latest pkts are important*/
+		--gwlan_logging.fw_log_pkt_qcnt;
+		spin_unlock_irqrestore(&gwlan_logging.fw_log_pkt_lock,
+					flags);
+		vos_pkt_return_packet(free_pkt);
+	} else {
+		spin_unlock_irqrestore(&gwlan_logging.fw_log_pkt_lock,
+					flags);
+	}
+
+	spin_lock_irqsave(&gwlan_logging.fw_log_pkt_lock, flags);
+
+	if (gwlan_logging.fw_log_pkt_queue) {
+		vos_pkt_chain_packet(gwlan_logging.fw_log_pkt_queue,
+					pPacket, TRUE);
+	} else {
+		gwlan_logging.fw_log_pkt_queue = pPacket;
+	}
+	++gwlan_logging.fw_log_pkt_qcnt;
+
+	spin_unlock_irqrestore(&gwlan_logging.fw_log_pkt_lock, flags);
+
+	set_bit(LOGGER_FW_LOG_PKT_POST_MASK, &gwlan_logging.event_flag);
+	wake_up_interruptible(&gwlan_logging.wait_queue);
+
+	return VOS_STATUS_SUCCESS;
+}
+
+int wlan_queue_logpkt_for_app(vos_pkt_t *pPacket, uint32 pkt_type)
+{
+	VOS_STATUS status = VOS_STATUS_E_FAILURE;
+
+	if (pPacket == NULL) {
+		pr_err("%s: Null param", __func__);
+		VOS_ASSERT(0);
+		return VOS_STATUS_E_FAILURE;
+	}
+
+	if (gwlan_logging.is_active == false) {
+		/*return all packets queued*/
+		wlan_logging_flush_pkt_queue();
+
+		/*return currently received pkt*/
+		vos_pkt_return_packet(pPacket);
+		return VOS_STATUS_E_FAILURE;
+	}
+
+	switch (pkt_type) {
+	case LOG_PKT_TYPE_DATA_MGMT:
+		status = wlan_queue_data_mgmt_pkt_for_app(pPacket);
+		break;
+
+	case LOG_PKT_TYPE_FW_LOG:
+		status = wlan_queue_fw_log_pkt_for_app(pPacket);
+		break;
+
+	default:
+		pr_info("%s: Unknown pkt received %d", __func__, pkt_type);
+		status = VOS_STATUS_E_INVAL;
+		break;
+	};
+
+	return status;
+}
 
 #endif /* WLAN_LOGGING_SOCK_SVC_ENABLE */
diff --git a/CORE/VOSS/inc/vos_trace.h b/CORE/VOSS/inc/vos_trace.h
index 2f3fd2b..5ed4944 100644
--- a/CORE/VOSS/inc/vos_trace.h
+++ b/CORE/VOSS/inc/vos_trace.h
@@ -81,6 +81,11 @@
     
 } VOS_TRACE_LEVEL;
 
+enum {
+	LOG_PKT_TYPE_DATA_MGMT = 0x1,
+	LOG_PKT_TYPE_FW_LOG    = 0x2
+};
+
 /*-------------------------------------------------------------------------- 
   Preprocessor definitions and constants
   ------------------------------------------------------------------------*/