#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(struct bcm_mini_adapter *Adapter, UINT gpios)
{
	INT Status;
	Status = (Adapter->gpioBitMap & gpios) ^ gpios;
	if (Status)
		return FALSE;
	else
		return TRUE;
}

static INT LED_Blink(struct bcm_mini_adapter *Adapter, UINT GPIO_Num, UCHAR uiLedIndex,
		ULONG timeout, INT num_of_time, enum bcm_led_events 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(struct bcm_mini_adapter *Adapter, UCHAR GPIO_Num_tx,
		UCHAR uiTxLedIndex, UCHAR GPIO_Num_rx, UCHAR uiRxLedIndex,
		enum bcm_led_events 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(struct bcm_mini_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(struct bcm_mini_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(struct bcm_mini_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(struct bcm_mini_adapter *Adapter,
					BOOLEAN *bEnableThread)
{
	int Status = STATUS_SUCCESS;
	/* Array to store GPIO numbers from EEPROM */
	UCHAR GPIO_Array[NUM_OF_LEDS+1];
	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(struct bcm_mini_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(struct bcm_mini_adapter *Adapter, UCHAR *GPIO_num_tx,
		UCHAR *GPIO_num_rx, UCHAR *uiLedTxIndex, UCHAR *uiLedRxIndex,
		enum bcm_led_events 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(struct bcm_mini_adapter *Adapter)
{
	UINT uiIndex = 0;
	UCHAR GPIO_num = 0;
	UCHAR uiLedIndex = 0;
	UINT uiResetValue = 0;
	enum bcm_led_events 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(struct bcm_mini_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;
}