#include "headers.h"

#define STATUS_IMAGE_CHECKSUM_MISMATCH -199
#define EVENT_SIGNALED 1

static B_UINT16 CFG_CalculateChecksum(B_UINT8 *pu8Buffer, B_UINT32 u32Size)
{
	B_UINT16 	u16CheckSum=0;
	while(u32Size--) {
		u16CheckSum += (B_UINT8)~(*pu8Buffer);
	    pu8Buffer++;
	}
	return u16CheckSum;
}
BOOLEAN IsReqGpioIsLedInNVM(PMINI_ADAPTER Adapter, UINT gpios)
{
	INT Status ;
	Status = (Adapter->gpioBitMap & gpios) ^ gpios ;
	if(Status)
		return FALSE;
	else
		return TRUE;
}

static INT LED_Blink(PMINI_ADAPTER Adapter, UINT GPIO_Num, UCHAR uiLedIndex, ULONG timeout, INT num_of_time, LedEventInfo_t currdriverstate)
{
	int Status = STATUS_SUCCESS;
	BOOLEAN bInfinite = FALSE;

	/*Check if num_of_time is -ve. If yes, blink led in infinite loop*/
	if(num_of_time < 0)
	{
		bInfinite = TRUE;
		num_of_time = 1;
	}
	while(num_of_time)
	{

		if(currdriverstate == Adapter->DriverState)
			TURN_ON_LED(GPIO_Num, uiLedIndex);

		/*Wait for timeout after setting on the LED*/
		Status = wait_event_interruptible_timeout(Adapter->LEDInfo.notify_led_event,
					currdriverstate != Adapter->DriverState || kthread_should_stop(),
					msecs_to_jiffies(timeout));

		if(kthread_should_stop())
		{
			BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL, "Led thread got signal to exit..hence exiting");
			Adapter->LEDInfo.led_thread_running= BCM_LED_THREAD_DISABLED;
			TURN_OFF_LED(GPIO_Num, uiLedIndex);
			Status=EVENT_SIGNALED;
			break;
		}
		if(Status)
		{
			TURN_OFF_LED(GPIO_Num, uiLedIndex);
			Status=EVENT_SIGNALED;
			break;
		}

		TURN_OFF_LED(GPIO_Num, uiLedIndex);
		Status = wait_event_interruptible_timeout(Adapter->LEDInfo.notify_led_event,
					currdriverstate!= Adapter->DriverState || kthread_should_stop(),
					msecs_to_jiffies(timeout));
		if(bInfinite == FALSE)
			num_of_time--;
	}
	return Status;
}

static INT ScaleRateofTransfer(ULONG rate)
{
	if(rate <= 3)
		return rate;
	else if((rate > 3) && (rate <= 100))
		return 5;
	else if((rate > 100) && (rate <= 200))
		return 6;
	else if((rate > 200) && (rate <= 300))
		return 7;
	else if((rate > 300) && (rate <= 400))
		return 8;
	else if((rate > 400) && (rate <= 500))
		return 9;
	else if((rate > 500) && (rate <= 600))
		return 10;
	else
		return MAX_NUM_OF_BLINKS;
}



static INT LED_Proportional_Blink(PMINI_ADAPTER Adapter, UCHAR GPIO_Num_tx,
		UCHAR uiTxLedIndex, UCHAR GPIO_Num_rx, UCHAR uiRxLedIndex, LedEventInfo_t currdriverstate)
{
	/* Initial values of TX and RX packets*/
	ULONG64 Initial_num_of_packts_tx = 0, Initial_num_of_packts_rx = 0;
	/*values of TX and RX packets after 1 sec*/
	ULONG64 Final_num_of_packts_tx = 0, Final_num_of_packts_rx = 0;
	/*Rate of transfer of Tx and Rx in 1 sec*/
	ULONG64 rate_of_transfer_tx = 0, rate_of_transfer_rx = 0;
	int Status = STATUS_SUCCESS;
	INT num_of_time = 0, num_of_time_tx = 0, num_of_time_rx = 0;
	UINT remDelay = 0;
	BOOLEAN bBlinkBothLED = TRUE;
	//UINT GPIO_num = DISABLE_GPIO_NUM;
	ulong timeout = 0;

	/*Read initial value of packets sent/received */
	Initial_num_of_packts_tx = Adapter->dev->stats.tx_packets;
	Initial_num_of_packts_rx = Adapter->dev->stats.rx_packets;

	/*Scale the rate of transfer to no of blinks.*/
	num_of_time_tx= ScaleRateofTransfer((ULONG)rate_of_transfer_tx);
	num_of_time_rx= ScaleRateofTransfer((ULONG)rate_of_transfer_rx);

	while((Adapter->device_removed == FALSE))
	{
		timeout = 50;
		/*Blink Tx and Rx LED when both Tx and Rx is in normal bandwidth*/
		if(bBlinkBothLED)
		{
			/*Assign minimum number of blinks of either Tx or Rx.*/
			if(num_of_time_tx > num_of_time_rx)
				num_of_time = num_of_time_rx;
			else
				num_of_time = num_of_time_tx;
			if(num_of_time > 0)
			{
				/*Blink both Tx and Rx LEDs*/
				if(LED_Blink(Adapter, 1<<GPIO_Num_tx, uiTxLedIndex, timeout, num_of_time,currdriverstate)
							== EVENT_SIGNALED)
				{
					return EVENT_SIGNALED;
				}
				if(LED_Blink(Adapter, 1<<GPIO_Num_rx, uiRxLedIndex, timeout, num_of_time,currdriverstate)
							== EVENT_SIGNALED)
				{
					return EVENT_SIGNALED;
				}

			}

			if(num_of_time == num_of_time_tx)
			{
				/*Blink pending rate of Rx*/
				if(LED_Blink(Adapter, (1 << GPIO_Num_rx), uiRxLedIndex, timeout,
						num_of_time_rx-num_of_time,currdriverstate) == EVENT_SIGNALED)
				{
					return EVENT_SIGNALED;
				}
				num_of_time = num_of_time_rx;
			}
			else
			{
				/*Blink pending rate of Tx*/
				if(LED_Blink(Adapter, 1<<GPIO_Num_tx, uiTxLedIndex, timeout,
					num_of_time_tx-num_of_time,currdriverstate) == EVENT_SIGNALED)
				{
					return EVENT_SIGNALED;
				}
				num_of_time = num_of_time_tx;
			}
		}
		else
		{
			if(num_of_time == num_of_time_tx)
			{
				/*Blink pending rate of Rx*/
				if(LED_Blink(Adapter, 1<<GPIO_Num_tx, uiTxLedIndex, timeout, num_of_time,currdriverstate)
							== EVENT_SIGNALED)
				{
					return EVENT_SIGNALED;
				}
			}
			else
			{
				/*Blink pending rate of Tx*/
				if(LED_Blink(Adapter, 1<<GPIO_Num_rx, uiRxLedIndex, timeout,
						num_of_time,currdriverstate) == EVENT_SIGNALED)
				{
					return EVENT_SIGNALED;
				}
			}
		}
		/* If Tx/Rx rate is less than maximum blinks per second,
			 * wait till delay completes to 1 second
			 */
		remDelay = MAX_NUM_OF_BLINKS - num_of_time;
		if(remDelay > 0)
		{
			timeout= 100 * remDelay;
			Status = wait_event_interruptible_timeout(Adapter->LEDInfo.notify_led_event,
						currdriverstate!= Adapter->DriverState ||kthread_should_stop() ,
						msecs_to_jiffies (timeout));

			if(kthread_should_stop())
			{
				BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL, "Led thread got signal to exit..hence exiting");
				Adapter->LEDInfo.led_thread_running= BCM_LED_THREAD_DISABLED;
				return EVENT_SIGNALED;
			}
			if(Status)
				return EVENT_SIGNALED;
		}

		/*Turn off both Tx and Rx LEDs before next second*/
		TURN_OFF_LED(1<<GPIO_Num_tx, uiTxLedIndex);
		TURN_OFF_LED(1<<GPIO_Num_rx, uiTxLedIndex);

		/*
 		 * Read the Tx & Rx packets transmission after 1 second and
 		 * calculate rate of transfer
 		 */
		Final_num_of_packts_tx = Adapter->dev->stats.tx_packets;
		Final_num_of_packts_rx = Adapter->dev->stats.rx_packets;

		rate_of_transfer_tx = Final_num_of_packts_tx - Initial_num_of_packts_tx;
		rate_of_transfer_rx = Final_num_of_packts_rx - Initial_num_of_packts_rx;

		/*Read initial value of packets sent/received */
		Initial_num_of_packts_tx = Final_num_of_packts_tx;
		Initial_num_of_packts_rx = Final_num_of_packts_rx ;

		/*Scale the rate of transfer to no of blinks.*/
		num_of_time_tx= ScaleRateofTransfer((ULONG)rate_of_transfer_tx);
		num_of_time_rx= ScaleRateofTransfer((ULONG)rate_of_transfer_rx);

	}
	return Status;
}


//-----------------------------------------------------------------------------
// Procedure:   ValidateDSDParamsChecksum
//
// Description: Reads DSD Params and validates checkusm.
//
// Arguments:
//      Adapter - Pointer to Adapter structure.
//      ulParamOffset - Start offset of the DSD parameter to be read and validated.
//      usParamLen - Length of the DSD Parameter.
//
// Returns:
//  <OSAL_STATUS_CODE>
//-----------------------------------------------------------------------------

static INT ValidateDSDParamsChecksum(
													PMINI_ADAPTER Adapter,
													ULONG  ulParamOffset,
													USHORT usParamLen )
{
	INT Status = STATUS_SUCCESS;
	PUCHAR puBuffer 		    = NULL;
	USHORT usChksmOrg		    = 0;
	USHORT usChecksumCalculated = 0;

	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL,"LED Thread:ValidateDSDParamsChecksum: 0x%lx 0x%X",ulParamOffset, usParamLen);

	puBuffer = kmalloc(usParamLen, GFP_KERNEL);
	if(!puBuffer)
	{
		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL,"LED Thread: ValidateDSDParamsChecksum Allocation failed");
		return -ENOMEM;

	}

    //
    //	Read the DSD data from the parameter offset.
    //
	if(STATUS_SUCCESS != BeceemNVMRead(Adapter,(PUINT)puBuffer,ulParamOffset,usParamLen))
	{
		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL,"LED Thread: ValidateDSDParamsChecksum BeceemNVMRead failed");
		Status=STATUS_IMAGE_CHECKSUM_MISMATCH;
		goto exit;
	}

	//
	//	Calculate the checksum of the data read from the DSD parameter.
	//
	usChecksumCalculated = CFG_CalculateChecksum(puBuffer,usParamLen);
	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL,"LED Thread: usCheckSumCalculated = 0x%x\n", usChecksumCalculated);

	//
	//	End of the DSD parameter will have a TWO bytes checksum stored in it. Read it and compare with the calculated
	//	Checksum.
	//
	if(STATUS_SUCCESS != BeceemNVMRead(Adapter,(PUINT)&usChksmOrg,ulParamOffset+usParamLen,2))
	{
		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL,"LED Thread: ValidateDSDParamsChecksum BeceemNVMRead failed");
		Status=STATUS_IMAGE_CHECKSUM_MISMATCH;
		goto exit;
	}
	usChksmOrg = ntohs(usChksmOrg);
	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL,"LED Thread: usChksmOrg = 0x%x", usChksmOrg);

	//
	//  	Compare the checksum calculated with the checksum read from DSD section
	//
	if(usChecksumCalculated ^ usChksmOrg)
	{
		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL,"LED Thread: ValidateDSDParamsChecksum: Checksums don't match");
		Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
		goto exit;
	}

exit:
	kfree(puBuffer);
	return Status;
}


//-----------------------------------------------------------------------------
// Procedure:   ValidateHWParmStructure
//
// Description: Validates HW Parameters.
//
// Arguments:
//      Adapter - Pointer to Adapter structure.
//      ulHwParamOffset - Start offset of the HW parameter Section to be read and validated.
//
// Returns:
//  <OSAL_STATUS_CODE>
//-----------------------------------------------------------------------------

static INT ValidateHWParmStructure(PMINI_ADAPTER Adapter, ULONG ulHwParamOffset)
{

	INT Status = STATUS_SUCCESS ;
	USHORT HwParamLen = 0;
	// Add DSD start offset to the hwParamOffset to get the actual address.
	ulHwParamOffset += DSD_START_OFFSET;

	/*Read the Length of HW_PARAM structure*/
	BeceemNVMRead(Adapter,(PUINT)&HwParamLen,ulHwParamOffset,2);
	HwParamLen = ntohs(HwParamLen);
	if(0==HwParamLen || HwParamLen > Adapter->uiNVMDSDSize)
	{
		return STATUS_IMAGE_CHECKSUM_MISMATCH;
	}

	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL, "LED Thread:HwParamLen = 0x%x", HwParamLen);
	Status =ValidateDSDParamsChecksum(Adapter,ulHwParamOffset,HwParamLen);
	return Status;
} /* ValidateHWParmStructure() */

static int ReadLEDInformationFromEEPROM(PMINI_ADAPTER Adapter, UCHAR GPIO_Array[])
{
	int Status = STATUS_SUCCESS;

	ULONG  dwReadValue 		= 0;
	USHORT usHwParamData 	= 0;
	USHORT usEEPROMVersion  = 0;
	UCHAR  ucIndex 			= 0;
	UCHAR  ucGPIOInfo[32] 	= {0};

	BeceemNVMRead(Adapter,(PUINT)&usEEPROMVersion,EEPROM_VERSION_OFFSET,2);

	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL,"usEEPROMVersion: Minor:0x%X Major:0x%x",usEEPROMVersion&0xFF, ((usEEPROMVersion>>8)&0xFF));


	if(((usEEPROMVersion>>8)&0xFF) < EEPROM_MAP5_MAJORVERSION)
	{
		BeceemNVMRead(Adapter,(PUINT)&usHwParamData,EEPROM_HW_PARAM_POINTER_ADDRESS,2);
		usHwParamData = ntohs(usHwParamData);
		dwReadValue   = usHwParamData;
	}
	else
	{
		//
		// Validate Compatibility section and then read HW param if compatibility section is valid.
		//
		Status = ValidateDSDParamsChecksum(Adapter,
			                   DSD_START_OFFSET,
			                   COMPATIBILITY_SECTION_LENGTH_MAP5);

		if(Status != STATUS_SUCCESS)
		{
			return Status;
		}
		BeceemNVMRead(Adapter,(PUINT)&dwReadValue,EEPROM_HW_PARAM_POINTER_ADDRRES_MAP5,4);
		dwReadValue = ntohl(dwReadValue);
	}


	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL,"LED Thread: Start address of HW_PARAM structure = 0x%lx",dwReadValue);

	//
	// Validate if the address read out is within the DSD.
	// Adapter->uiNVMDSDSize gives whole DSD size inclusive of Autoinit.
	// lower limit should be above DSD_START_OFFSET and
	// upper limit should be below (Adapter->uiNVMDSDSize-DSD_START_OFFSET)
	//
	if(dwReadValue < DSD_START_OFFSET ||
	   dwReadValue > (Adapter->uiNVMDSDSize-DSD_START_OFFSET))
	{
		return STATUS_IMAGE_CHECKSUM_MISMATCH;
	}

	Status = ValidateHWParmStructure(Adapter, dwReadValue);
	if(Status){
		return Status;
	}

	/*
	  Add DSD_START_OFFSET to the offset read from the EEPROM.
	  This will give the actual start HW Parameters start address.
	  To read GPIO section, add GPIO offset further.
	*/

	dwReadValue += DSD_START_OFFSET; // = start address of hw param section.
	dwReadValue += GPIO_SECTION_START_OFFSET; // = GPIO start offset within HW Param section.

	/* Read the GPIO values for 32 GPIOs from EEPROM and map the function
 	 * number to GPIO pin number to GPIO_Array
 	 */
	BeceemNVMRead(Adapter, (UINT *)ucGPIOInfo,dwReadValue,32);
	for(ucIndex = 0; ucIndex < 32; ucIndex++)
	 {

		 switch(ucGPIOInfo[ucIndex])
			{
				case RED_LED:
				{
				 	GPIO_Array[RED_LED] = ucIndex;
				 	Adapter->gpioBitMap |= (1<<ucIndex);
					break;
				}
				case BLUE_LED:
				{
					GPIO_Array[BLUE_LED] = ucIndex;
					Adapter->gpioBitMap |= (1<<ucIndex);
					break;
				}
				case YELLOW_LED:
				{
					 GPIO_Array[YELLOW_LED] = ucIndex;
					 Adapter->gpioBitMap |= (1<<ucIndex);
					 break;
				}
				case GREEN_LED:
				{
					GPIO_Array[GREEN_LED] = ucIndex;
				 	Adapter->gpioBitMap |= (1<<ucIndex);
					break;
				}
				default:
					break;
			}

		}
		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL,"GPIO's bit map correspond to LED :0x%X",Adapter->gpioBitMap);
	 return Status;
}


static int ReadConfigFileStructure(PMINI_ADAPTER Adapter, BOOLEAN *bEnableThread)
{
	int Status = STATUS_SUCCESS;
	UCHAR GPIO_Array[NUM_OF_LEDS+1]; /*Array to store GPIO numbers from EEPROM*/
	UINT uiIndex = 0;
	UINT uiNum_of_LED_Type = 0;
	PUCHAR puCFGData	= NULL;
	UCHAR bData = 0;
	memset(GPIO_Array, DISABLE_GPIO_NUM, NUM_OF_LEDS+1);

	if(!Adapter->pstargetparams || IS_ERR(Adapter->pstargetparams))
	{
		BCM_DEBUG_PRINT (Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL, "Target Params not Avail.\n");
		return -ENOENT;
	}

	/*Populate GPIO_Array with GPIO numbers for LED functions*/
	/*Read the GPIO numbers from EEPROM*/
	Status = ReadLEDInformationFromEEPROM(Adapter, GPIO_Array);
	if(Status == STATUS_IMAGE_CHECKSUM_MISMATCH)
	{
		*bEnableThread = FALSE;
		return STATUS_SUCCESS;
	}
	else if(Status)
	{
		*bEnableThread = FALSE;
		return Status;
	}
  /*
     * CONFIG file read successfully. Deallocate the memory of
     * uiFileNameBufferSize
     */
	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL,"LED Thread: Config file read successfully\n");
	puCFGData = (PUCHAR) &Adapter->pstargetparams->HostDrvrConfig1;

	/*
 	 * Offset for HostDrvConfig1, HostDrvConfig2, HostDrvConfig3 which
 	 * will have the information of LED type, LED on state for different
 	 * driver state and LED blink state.
 	 */

	for(uiIndex = 0; uiIndex < NUM_OF_LEDS; uiIndex++)
	{
		bData = *puCFGData;

		/*Check Bit 8 for polarity. If it is set, polarity is reverse polarity*/
		if(bData & 0x80)
		{
			Adapter->LEDInfo.LEDState[uiIndex].BitPolarity = 0;
			/*unset the bit 8*/
			bData = bData & 0x7f;
		}

		Adapter->LEDInfo.LEDState[uiIndex].LED_Type = bData;
		if(bData <= NUM_OF_LEDS)
			Adapter->LEDInfo.LEDState[uiIndex].GPIO_Num = GPIO_Array[bData];
		else
			Adapter->LEDInfo.LEDState[uiIndex].GPIO_Num = DISABLE_GPIO_NUM;

		puCFGData++;
		bData = *puCFGData;
		Adapter->LEDInfo.LEDState[uiIndex].LED_On_State = bData;
		puCFGData++;
		bData = *puCFGData;
		Adapter->LEDInfo.LEDState[uiIndex].LED_Blink_State= bData;
		puCFGData++;
	}

	/*Check if all the LED settings are disabled. If it is disabled, dont launch the LED control thread.*/
	for(uiIndex = 0; uiIndex<NUM_OF_LEDS; uiIndex++)
	{
		if((Adapter->LEDInfo.LEDState[uiIndex].LED_Type == DISABLE_GPIO_NUM) ||
	 		(Adapter->LEDInfo.LEDState[uiIndex].LED_Type == 0x7f) ||
			(Adapter->LEDInfo.LEDState[uiIndex].LED_Type == 0))
			uiNum_of_LED_Type++;
	}
	if(uiNum_of_LED_Type >= NUM_OF_LEDS)
		*bEnableThread = FALSE;

	return Status;
}
//--------------------------------------------------------------------------
// Procedure:   LedGpioInit
//
// Description: Initializes LED GPIOs. Makes the LED GPIOs to OUTPUT mode and make the
//			  initial state to be OFF.
//
// Arguments:
//      Adapter - Pointer to MINI_ADAPTER structure.
//
// Returns: VOID
//
//-----------------------------------------------------------------------------

static VOID LedGpioInit(PMINI_ADAPTER Adapter)
{
	UINT uiResetValue = 0;
	UINT uiIndex      = 0;

	/* Set all LED GPIO Mode to output mode */
	if(rdmalt(Adapter, GPIO_MODE_REGISTER, &uiResetValue, sizeof(uiResetValue)) <0)
		BCM_DEBUG_PRINT (Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL,"LED Thread: RDM Failed\n");
	for(uiIndex = 0; uiIndex < NUM_OF_LEDS; uiIndex++)
	{
		if(Adapter->LEDInfo.LEDState[uiIndex].GPIO_Num != DISABLE_GPIO_NUM)
			uiResetValue |= (1 << Adapter->LEDInfo.LEDState[uiIndex].GPIO_Num);
		TURN_OFF_LED(1<<Adapter->LEDInfo.LEDState[uiIndex].GPIO_Num,uiIndex);
	}
	if(wrmalt(Adapter, GPIO_MODE_REGISTER, &uiResetValue, sizeof(uiResetValue)) < 0)
		BCM_DEBUG_PRINT (Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL,"LED Thread: WRM Failed\n");

	Adapter->LEDInfo.bIdle_led_off =  FALSE;
}
//-----------------------------------------------------------------------------

static INT BcmGetGPIOPinInfo(PMINI_ADAPTER Adapter, UCHAR *GPIO_num_tx, UCHAR *GPIO_num_rx ,UCHAR *uiLedTxIndex, UCHAR *uiLedRxIndex,LedEventInfo_t currdriverstate)
{
	UINT uiIndex = 0;

	*GPIO_num_tx = DISABLE_GPIO_NUM;
	*GPIO_num_rx = DISABLE_GPIO_NUM;

	for(uiIndex = 0; uiIndex < NUM_OF_LEDS; uiIndex++)
	{

		if((currdriverstate == NORMAL_OPERATION)||
			(currdriverstate == IDLEMODE_EXIT)||
			(currdriverstate == FW_DOWNLOAD))
		{
			if(Adapter->LEDInfo.LEDState[uiIndex].LED_Blink_State & currdriverstate)
			{
				if(Adapter->LEDInfo.LEDState[uiIndex].GPIO_Num != DISABLE_GPIO_NUM)
				{
					if(*GPIO_num_tx == DISABLE_GPIO_NUM)
					{
						*GPIO_num_tx = Adapter->LEDInfo.LEDState[uiIndex].GPIO_Num;
						*uiLedTxIndex = uiIndex;
					}
					else
					{
						*GPIO_num_rx = Adapter->LEDInfo.LEDState[uiIndex].GPIO_Num;
						*uiLedRxIndex = uiIndex;
					}
				}
			}
		}
		else
		{
			if(Adapter->LEDInfo.LEDState[uiIndex].LED_On_State & currdriverstate)
			{
				if(Adapter->LEDInfo.LEDState[uiIndex].GPIO_Num != DISABLE_GPIO_NUM)
				{
					*GPIO_num_tx = Adapter->LEDInfo.LEDState[uiIndex].GPIO_Num;
					*uiLedTxIndex = uiIndex;
				}
			}
		}
	}
	return STATUS_SUCCESS ;
}
static VOID LEDControlThread(PMINI_ADAPTER Adapter)
{
	UINT uiIndex = 0;
	UCHAR GPIO_num = 0;
	UCHAR uiLedIndex = 0 ;
	UINT uiResetValue = 0;
	LedEventInfo_t currdriverstate = 0;
	ulong timeout = 0;

	INT Status = 0;

	UCHAR  dummyGPIONum = 0;
	UCHAR  dummyIndex = 0;

	//currdriverstate = Adapter->DriverState;
	Adapter->LEDInfo.bIdleMode_tx_from_host = FALSE;

	/*Wait till event is triggered*/
	//wait_event(Adapter->LEDInfo.notify_led_event,
			//	currdriverstate!= Adapter->DriverState);

	GPIO_num = DISABLE_GPIO_NUM ;

	while(TRUE)
	{
		/*Wait till event is triggered*/
		if( (GPIO_num == DISABLE_GPIO_NUM)
						||
			((currdriverstate != FW_DOWNLOAD) &&
			 (currdriverstate != NORMAL_OPERATION) &&
			 (currdriverstate != LOWPOWER_MODE_ENTER))
			 			||
			 (currdriverstate == LED_THREAD_INACTIVE)	)
		{
			Status = wait_event_interruptible(Adapter->LEDInfo.notify_led_event,
				currdriverstate != Adapter->DriverState || kthread_should_stop());
		}

		if(kthread_should_stop() || Adapter->device_removed )
		{
			BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL, "Led thread got signal to exit..hence exiting");
			Adapter->LEDInfo.led_thread_running = BCM_LED_THREAD_DISABLED;
			TURN_OFF_LED(1<<GPIO_num, uiLedIndex);
			return ;//STATUS_FAILURE;
		}

		if(GPIO_num != DISABLE_GPIO_NUM)
		{
			TURN_OFF_LED(1<<GPIO_num, uiLedIndex);
		}

		if(Adapter->LEDInfo.bLedInitDone == FALSE)
		{
			LedGpioInit(Adapter);
			Adapter->LEDInfo.bLedInitDone = TRUE;
		}

		switch(Adapter->DriverState)
		{
			case DRIVER_INIT:
			{
				currdriverstate = DRIVER_INIT;//Adapter->DriverState;
				BcmGetGPIOPinInfo(Adapter, &GPIO_num, &dummyGPIONum, &uiLedIndex, &dummyIndex, currdriverstate);

				if(GPIO_num  != DISABLE_GPIO_NUM)
				{
					TURN_ON_LED(1<<GPIO_num, uiLedIndex);
				}
			}
			break;
			case FW_DOWNLOAD:
			{
				//BCM_DEBUG_PRINT (Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL,"LED Thread: FW_DN_DONE called\n");
				currdriverstate = FW_DOWNLOAD;
				BcmGetGPIOPinInfo(Adapter, &GPIO_num, &dummyGPIONum,  &uiLedIndex, &dummyIndex, currdriverstate);

				if(GPIO_num != DISABLE_GPIO_NUM)
				{
					timeout = 50;
					LED_Blink(Adapter, 1<<GPIO_num, uiLedIndex, timeout, -1,currdriverstate);
				}
			}
			break;
			case FW_DOWNLOAD_DONE:
			{
				currdriverstate = FW_DOWNLOAD_DONE;
				BcmGetGPIOPinInfo(Adapter, &GPIO_num, &dummyGPIONum, &uiLedIndex, &dummyIndex,currdriverstate);
				if(GPIO_num != DISABLE_GPIO_NUM)
				{
					TURN_ON_LED(1<<GPIO_num, uiLedIndex);
				}
			}
			break;

			case SHUTDOWN_EXIT:
			//no break, continue to NO_NETWORK_ENTRY state as well.

			case NO_NETWORK_ENTRY:
			{
				currdriverstate = NO_NETWORK_ENTRY;
				BcmGetGPIOPinInfo(Adapter, &GPIO_num, &dummyGPIONum, &uiLedIndex,&dummyGPIONum,currdriverstate);
				if(GPIO_num != DISABLE_GPIO_NUM)
				{
					TURN_ON_LED(1<<GPIO_num, uiLedIndex);
				}
			}
			break;
			case NORMAL_OPERATION:
			{
				UCHAR GPIO_num_tx = DISABLE_GPIO_NUM;
				UCHAR GPIO_num_rx = DISABLE_GPIO_NUM;
				UCHAR uiLEDTx = 0;
				UCHAR uiLEDRx = 0;
				currdriverstate = NORMAL_OPERATION;
				Adapter->LEDInfo.bIdle_led_off =  FALSE;

				BcmGetGPIOPinInfo(Adapter, &GPIO_num_tx, &GPIO_num_rx, &uiLEDTx,&uiLEDRx,currdriverstate);
				if((GPIO_num_tx == DISABLE_GPIO_NUM) && (GPIO_num_rx == DISABLE_GPIO_NUM))
				{
					GPIO_num = DISABLE_GPIO_NUM ;
				}
				else
				{
					/*If single LED is selected, use same for both Tx and Rx*/
					if(GPIO_num_tx == DISABLE_GPIO_NUM)
					{
						GPIO_num_tx = GPIO_num_rx;
						uiLEDTx = uiLEDRx;
					}
					else if(GPIO_num_rx == DISABLE_GPIO_NUM)
					{
						GPIO_num_rx = GPIO_num_tx;
						uiLEDRx = uiLEDTx;
					}
				/*Blink the LED in proportionate to Tx and Rx transmissions.*/
					LED_Proportional_Blink(Adapter, GPIO_num_tx, uiLEDTx, GPIO_num_rx, uiLEDRx,currdriverstate);
				}
			}
			break;
			case LOWPOWER_MODE_ENTER:
			{
				currdriverstate  = LOWPOWER_MODE_ENTER;
				if( DEVICE_POWERSAVE_MODE_AS_MANUAL_CLOCK_GATING == Adapter->ulPowerSaveMode)
				{
					/* Turn OFF all the LED */
					uiResetValue = 0;
					for(uiIndex =0; uiIndex < NUM_OF_LEDS; uiIndex++)
					{
						if(Adapter->LEDInfo.LEDState[uiIndex].GPIO_Num != DISABLE_GPIO_NUM)
						TURN_OFF_LED((1<<Adapter->LEDInfo.LEDState[uiIndex].GPIO_Num),uiIndex);
					}

				}
				/* Turn off LED And WAKE-UP for Sendinf IDLE mode ACK */
				Adapter->LEDInfo.bLedInitDone = FALSE;
				Adapter->LEDInfo.bIdle_led_off =  TRUE;
				wake_up(&Adapter->LEDInfo.idleModeSyncEvent);
				GPIO_num = DISABLE_GPIO_NUM;
				break;
			}
			case IDLEMODE_CONTINUE:
			{
				currdriverstate = IDLEMODE_CONTINUE;
				GPIO_num = DISABLE_GPIO_NUM;
			}
			break;
			case IDLEMODE_EXIT:
			{
			}
			break;
			case DRIVER_HALT:
			{
				currdriverstate = DRIVER_HALT;
				GPIO_num = DISABLE_GPIO_NUM;
				for(uiIndex = 0; uiIndex < NUM_OF_LEDS; uiIndex++)
				{
					if(Adapter->LEDInfo.LEDState[uiIndex].GPIO_Num !=
						DISABLE_GPIO_NUM)
						TURN_OFF_LED((1<<Adapter->LEDInfo.LEDState[uiIndex].GPIO_Num),uiIndex);
				}
				//Adapter->DriverState = DRIVER_INIT;
			}
			break;
			case LED_THREAD_INACTIVE :
			{
				BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL,"InActivating LED thread...");
				currdriverstate = LED_THREAD_INACTIVE;
				Adapter->LEDInfo.led_thread_running = BCM_LED_THREAD_RUNNING_INACTIVELY ;
				Adapter->LEDInfo.bLedInitDone = FALSE ;
				//disable ALL LED
				for(uiIndex = 0; uiIndex < NUM_OF_LEDS; uiIndex++)
				{
					if(Adapter->LEDInfo.LEDState[uiIndex].GPIO_Num !=
						DISABLE_GPIO_NUM)
						TURN_OFF_LED((1<<Adapter->LEDInfo.LEDState[uiIndex].GPIO_Num),uiIndex);
				}
			}
			break;
			case LED_THREAD_ACTIVE :
			{
				BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL,"Activating LED thread again...");
				if(Adapter->LinkUpStatus == FALSE)
					Adapter->DriverState = NO_NETWORK_ENTRY;
				else
					Adapter->DriverState = NORMAL_OPERATION;

				Adapter->LEDInfo.led_thread_running = BCM_LED_THREAD_RUNNING_ACTIVELY ;
			}
			break;
			//return;
			default:
				break;
		}
	}
	Adapter->LEDInfo.led_thread_running = BCM_LED_THREAD_DISABLED;
}

int InitLedSettings(PMINI_ADAPTER Adapter)
{
	int Status = STATUS_SUCCESS;
	BOOLEAN bEnableThread = TRUE;
	UCHAR uiIndex = 0;

	/*Initially set BitPolarity to normal polarity. The bit 8 of LED type
 * 	  is used to change the polarity of the LED.*/

	for(uiIndex = 0; uiIndex < NUM_OF_LEDS; uiIndex++) {
		Adapter->LEDInfo.LEDState[uiIndex].BitPolarity = 1;
	}

	/*Read the LED settings of CONFIG file and map it to GPIO numbers in EEPROM*/
	Status = ReadConfigFileStructure(Adapter, &bEnableThread);
	if(STATUS_SUCCESS != Status)
	{
		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL,"LED Thread: FAILED in ReadConfigFileStructure\n");
		return Status;
	}

	if(Adapter->LEDInfo.led_thread_running)
	{
		if(bEnableThread)
			;
		else
		{
			Adapter->DriverState = DRIVER_HALT;
			wake_up(&Adapter->LEDInfo.notify_led_event);
			Adapter->LEDInfo.led_thread_running = BCM_LED_THREAD_DISABLED;
		}

	}

	else if(bEnableThread)
	{
		/*Create secondary thread to handle the LEDs*/
		init_waitqueue_head(&Adapter->LEDInfo.notify_led_event);
		init_waitqueue_head(&Adapter->LEDInfo.idleModeSyncEvent);
		Adapter->LEDInfo.led_thread_running = BCM_LED_THREAD_RUNNING_ACTIVELY;
		Adapter->LEDInfo.bIdle_led_off =  FALSE;
		Adapter->LEDInfo.led_cntrl_threadid = kthread_run((int (*)(void *))
            LEDControlThread, Adapter, "led_control_thread");
		if(IS_ERR(Adapter->LEDInfo.led_cntrl_threadid))
    	{
        	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, LED_DUMP_INFO, DBG_LVL_ALL, "Not able to spawn Kernel Thread\n");
			Adapter->LEDInfo.led_thread_running = BCM_LED_THREAD_DISABLED;
        	return PTR_ERR(Adapter->LEDInfo.led_cntrl_threadid);
    	}
	}
	return Status;
}