/*
 *  Copyright (c) 1996-2007 Digi International Inc., All Rights Reserved
 *
 *  This software contains proprietary and confidential information of Digi
 *  International Inc.  By accepting transfer of this copy, Recipient agrees
 *  to retain this software in confidence, to prevent disclosure to others,
 *  and to make no use of this software other than that for which it was
 *  delivered.  This is an unpublished copyrighted work of Digi International
 *  Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 *  prohibited.
 *
 *  Restricted Rights Legend
 *
 *  Use, duplication, or disclosure by the Government is subject to
 *  restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 *  Technical Data and Computer Software clause at DFARS 252.227-7031 or
 *  subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 *  Restricted Rights at 48 CFR 52.227-19, as applicable.
 *
 *  Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 */

//
// Embedded 802.11a/g wireless network interface
// MAC layer hardware interface for ConnectCore Wi-9C
// Copyright 2006 Digi International
//
#include "mac.h"
#include <mcsys_reg.h>
#include "processorgpio.h"
#include "reg_def.h"

//
// Hardware register defines
//
#define MAC_BASE		0x60000000		// Register base address
#define MAC_PROG		0x70000000		// FPGA program address
#define MAC_MASK		0xffffc001		// Size mask and enable bit

#define	REG32(offset)	(* (volatile uint32 *) (MAC_BASE + (offset)))

// Baseband control registers
#define	HW_VERSION		REG32(0x00)		// Version
#define	HW_GEN_CONTROL	REG32(0x04)		// General control
#define	HW_GEN_STATUS	REG32(0x08)		// General status
#define	HW_RSSI_AES		REG32(0x0c)		// RSSI and AES status
#define	HW_INTR_MASK	REG32(0x10)		// Interrupt mask
#define	HW_INTR_STATUS	REG32(0x14)		// Interrupt status
#define	HW_SPI			REG32(0x18)		// RF SPI interface
#define HW_BOOTMUX      REG32(0x1c)     // Bootmux?
#define	HW_DATA_FIFO	REG32(0x20)		// Data FIFO
#define	HW_AES_FIFO		REG32(0x30)		// AES FIFO
#define	HW_AES_MODE		REG32(0x38)		// ARS mode

// MAC control registers.
#define	HW_STAID0		REG32(0x40)		// Station ID (6 bytes)
#define	HW_STAID1		REG32(0x44)
#define	HW_BSSID0		REG32(0x48)		// BSS ID (6 bytes)
#define	HW_BSSID1		REG32(0x4c)
#define	HW_SSID_LEN		REG32(0x50)		// Basic rates (high 16 bits), SSID length (low 8 bits)
#define	HW_BACKOFF		REG32(0x54)		// Backoff period (16 bits)
#define	HW_LISTEN		REG32(0x58)		// Listen interval (16 bits), CFP (8 bits), DTIM (8 bits)
#define	HW_CFP_ATIM		REG32(0x5c)		// CFP max duration/ATIM period (16 bits), beacon interval (16 bits)
#define	HW_MAC_STATUS	REG32(0x60)		// MAC status (8 bits)
#define	HW_MAC_CONTROL	REG32(0x64)		// MAC control (8 bits)
#define	HW_REMAIN_BO	REG32(0x68)		// Remaining backoff (16 bits)
#define	HW_BEACON_BO	REG32(0x6c)		// Beacon backoff (16 bits), beacon mask (8 bits)
#define	HW_SSID			REG32(0x80)		// Service set ID (32 bytes)

// FIFO sizes in bytes
#define	HW_TX_FIFO_SIZE	1792
#define	HW_RX_FIFO_SIZE	2048

#if (MAX_FRAME_SIZE > HW_TX_FIFO_SIZE)
#error	MAX_FRAME_SIZE too big
#endif

// Min fragment size to allow interrupt latency between frames
#define	MIN_FRAG_SIZE	400

// Size of CTS frame in FIFO
#define	CTS_OVERHEAD	20
// Max overhead per fragment (CCMP is worst case)
#define	FRAG_OVERHEAD	(8+DATA_SIZE+CCMP_SIZE-FCS_SIZE)
// Max fragment size to fit CTS and 2 fragments in transmit FIFO
#define	MAX_FRAG_SIZE	(DATA_SIZE + (HW_TX_FIFO_SIZE-CTS_OVERHEAD-2*FRAG_OVERHEAD)/2)

// General control register bits
#define	GEN_RXEN		0x00000001		// Receive enable
#define	GEN_ANTDIV		0x00000002		// Antenna diversity
#define	GEN_ANTSEL		0x00000004		// Antenna select
#define	GEN_5GEN		0x00000008		// 5 GHz band enable
#define	GEN_SHPRE		0x00000010		// Transmit short preamble
#define	GEN_RXFIFORST	0x00000020		// Receive FIFO reset
#define	GEN_TXFIFORST	0x00000040		// Transmit FIFO reset
#define	GEN_TXHOLD		0x00000080		// Transmit FIFO hold
#define	GEN_BEACEN		0x00000100		// Beacon enable
#define	GEN_TXFIFOEMPTY	0x00004000		// Transmit FIFO empty
#define	GEN_TXFIFOFULL	0x00008000		// Transmit FIFO full

#define	GEN_RESET		0x527f0000		// Reset state
#define	GEN_INIT		0x377a0000		// Initial state

// General status register bits
#define	STAT_RXFE		0x00000010		// Receive FIFO empty
#define STAT_DLLLOCK    0x00000020      // DLL lock

// AES status register bits
#define	AES_EMPTY		0x00010000		// AES receive FIFO empty
#define	AES_FULL		0x00020000		// AES transmit FIFO full
#define	AES_BUSY		0x00040000		// AES engine busy
#define	AES_MIC			0x00080000		// AES MIC correct

// Interrupt mask and status register bits
#define	INTR_RXFIFO		0x00000001		// Receive FIFO not empty
#define	INTR_TXEND		0x00000002		// Transmit complete
#define	INTR_TIMEOUT	0x00000004		// CTS/ACK receive timeout
#define	INTR_ABORT		0x00000008		// CTS transmit abort
#define	INTR_TBTT		0x00000010		// Beacon transmission time
#define	INTR_ATIM		0x00000020		// ATIM interval end
#define	INTR_RXOVERRUN	0x00000040		// Receive FIFO overrun

// MAC control register bits
#define	CTRL_TXREQ		0x00000001		// Transmit request
#define	CTRL_AUTOTXDIS	0x00000002		// Auto-transmit disable
#define	CTRL_BEACONTX	0x00000004		// Beacon transmit enable
#define	CTRL_PROMISC	0x00000008		// Promiscuous mode
#define	CTRL_IBSS		0x00000010		// IBBS mode

//
// PIO pins
//
#define PIN_INIT		58				// FPGA program init
#define PIN_DONE		65				// FPGA program done
#define PIN_INTR		65				// Interrupt (same as done)
#define PIN_LED			67				// Link status LED
#define PIN_ACTIVITY_LED  66            // Link activity LED
#define PIN_BOOTMUX  66       // Enables serial ports


#define INTR_ID			ETH_PHY_INTERRUPT	// Interrupt ID for PIN_INTR

#define	gpio_args(pin)	(pin)/32, (pin)%32

// Get/set/clear a PIO pin
#define PIO_GET(pin)	ProcessorGpioGetValue (gpio_args (pin))
#define PIO_SET(pin)	ProcessorGpioSetOutputValue (gpio_args (pin), 1)
#define PIO_CLR(pin)	ProcessorGpioSetOutputValue (gpio_args (pin), 0)

// Set PIO pin mode
#define PIO_OUTPUT(pin)	ProcessorGpioSetOutput (gpio_args (pin), 1)
#define PIO_INPUT(pin)	ProcessorGpioSetInput (gpio_args (pin))
#define PIO_INTR(pin)	ProcessorGpioSetInterrupt (gpio_args (pin), 0, 0)

#define	LOAD_TIMEOUT	1000			// FPGA load timeout (ms)


// RF transceiver transmitter gain for each power level.
// This is the 0-15 power level, bit reversed.
static uint8 powerTable[] = {
	0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
	0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf
};

// RF transceiver frequency divider for each channel
static const struct {
	uint16 integer;
	uint16 fraction;
} freqTable[] = {
	{ 0, 0 },
	{ 0x6847, 0x0999 }, // 1 (2412 MHz)
	{ 0x6847, 0x099b }, // 2 (2417 MHz)
	{ 0x6867, 0x0998 }, // 3
	{ 0x6867, 0x099a }, // 4
	{ 0x6867, 0x0999 }, // 5
	{ 0x6867, 0x099b }, // 6
	{ 0x6857, 0x0998 }, // 7
	{ 0x6857, 0x099a }, // 8
	{ 0x6857, 0x0999 }, // 9
	{ 0x6857, 0x099b }, // 10
	{ 0x6877, 0x0998 }, // 11
	{ 0x6877, 0x099a }, // 12
	{ 0x6877, 0x0999 }, // 13 (2472 MHz)
	{ 0x684f, 0x0ccc }, // 14 (2484 MHz)
};

// Map LNA value to gain value
static const uint8 lnaTable[] =
{
    0, 0, 23, 39
};

// Map high gain values to dbm
static const signed char gainTable[] =
{
    -82, -84, -85, -86, -87, -89, -90, -92, -94, -98
};

static MacBuffer rxFrame;	// Frame being received
static uint32 intrMask;		// Interrupt mask register
static uint32 timerLoops;	// Loops for 10 usec delay

static int HandleIR (void *ignore);


/* set the MAX AGC field in control register */
/*
 * For UBEC   :     Before association, BG band, use 0x78;  for A band,  use 0x77.
 *                  After association, , BG band, use no less than 0x72;  for A band,  use no less than 0x72.
 *
 */
void adjustMaxAgc_wi9c(MacBuffer *buf)
{
#define LOWEST_MAXAGC        (0x72)
#define HIGHEST_MAXAGC       (0x7A)
#define RSSI_AVG_COUNT  8

    unsigned char maxgain = 0;
    unsigned char lowest = 0, highest = 0;
    static int k=0, j=0, i =0, tempRssi=0;

    if (buf == NULL)
        return;


    lowest = LOWEST_MAXAGC;
    highest = HIGHEST_MAXAGC;

    if (staState == SS_ASSOC)
    {

        if (macStatus.bss_caps & CAP_IBSS)
        {
            //Monitor the receive signal strength from Ad-hoc network
            if (EQUAL_ADDR (macStatus.bss_addr, buf->macHdr.addr3))
            {
                /* we don't do avareging on all the signals here because it may come from different
                 * unit in that Ad-hoc network. Instead, we do avareging on the signals with higher rssi
                 */

                if ((buf->rssi + 4) > lowest)
                {
                    k++;
                    tempRssi += buf->rssi;

                    if (k >= RSSI_AVG_COUNT)
                    {
                        maxgain = (((tempRssi/k) + 4) > highest)? highest : ((tempRssi/k) + 4) ;
                        k = 0;
                        tempRssi = 0;
                        i =0;
                    }
                }
                else
                {
                    i++;
                    if (i >= (RSSI_AVG_COUNT*4))
                    {
                        maxgain = lowest;
                        i = 0;
                    }

                }
            }
        }
        else
        {
            //Monitor the receive signal strength from the frames we received from the associated AP
            if (EQUAL_ADDR (macStatus.bss_addr, buf->macHdr.addr2))
            {
                //avareging all the signals because it come from the same AP
                k++;
                tempRssi += buf->rssi;

                if (k >= RSSI_AVG_COUNT*2)
                {
                    if (((tempRssi/k) + 4) > lowest)
                        maxgain = (((tempRssi/k) + 4) > highest)? highest : ((tempRssi/k) + 4) ;
                    else
                        maxgain = lowest;

                    k = 0;
                    tempRssi = 0;
                }
            }
        }
        j = 0;
    }
    else
    {
        if (macStatus.state == WLN_ST_STOPPED)
            maxgain = highest;
        else
        {
            j++;
            if (j >= (RSSI_AVG_COUNT*4))
            {
                maxgain = highest;
                j = 0;
            }
            k = 0;
            tempRssi = 0;
        }
    }

    if (maxgain && (maxgain != (HW_GEN_CONTROL & 0x007F0000) >> 16))
    {
        uint32 mg = HW_GEN_CONTROL & 0xFF80FFFF;
        HW_GEN_CONTROL = mg | ((maxgain & 0x7F) << 16);
    }
}


//
// Delay for usec microseconds
//
static void waitUS (uint32 usec)
{
	volatile int n = usec*timerLoops/10;

	while (n-- > 0)
		;
}

//
// Calibrate the spin loop in waitUS()
//
// We need 10us resolution, but the threadx timer is only accurate to 10ms.
// Use it to calibrate a large number of iterations of the spin loop in waitUS().
//
static void InitTimer (void)
{
	uint32 machw_time;
    UINT saved_preemption;
    UINT unused_preemption;

	// Initial guess
	timerLoops = 500;

	// Time 10000 iterations using hardware timer
    tx_thread_preemption_change(tx_thread_identify(), 1, &saved_preemption);
	machw_time = getTimeMS();
	waitUS (100000);
	machw_time = getTimeMS() - machw_time;
    tx_thread_preemption_change(tx_thread_identify(), saved_preemption, &unused_preemption);

	// Calculate loops for 10us delay
	if (machw_time != 0)
		timerLoops = 50000/machw_time;

#ifdef DEBUG
	DPRINTF ("time=%lu ms, loops=%lu\n", (long unsigned) t, (long unsigned) timerLoops);

	t = getTimeMS();
	waitUS (100000);
	t = getTimeMS() - t;

	DPRINTF ("time=%lu ms (should be 100)\n", (long unsigned) t);
#endif
}

//
// Write data to an RF tranceiver register
// @param addr Register address (4 bits)
// @param data Data to write (20 bits, bit reversed)
//
static void WriteRF (uint8 addr, uint32 data)
{
	HW_SPI = (addr << 20) | data;
	waitUS (10);
}

//
// Copy memory to/from hardware using 32-bit operations
//
static void HWMemcpy (volatile void *dst_, volatile void *src_, int len)
{
	volatile uint32 *dst = (uint32 *) dst_;
	volatile uint32 *src = (uint32 *) src_;

	for (; len >= 16; len -= 16)
	{
		*dst++ = *src++;
		*dst++ = *src++;
		*dst++ = *src++;
		*dst++ = *src++;
	}
	for (; len > 0; len -= 4)
		*dst++ = *src++;
}

//
// Copy from receive FIFO
//
static void HWReadFifo (void *dst_, int len)
{
	uint32 *dst = (uint32 *) dst_;

	for (; len >= 16; len -= 16)
	{
		*dst++ = HW_DATA_FIFO;
		*dst++ = HW_DATA_FIFO;
		*dst++ = HW_DATA_FIFO;
		*dst++ = HW_DATA_FIFO;
	}
	for (; len > 0; len -= 4)
		*dst++ = HW_DATA_FIFO;
}

//
// Copy to transmit FIFO
//
static void HWWriteFifo (void *src_, int len)
{
	uint32 *src = (uint32 *) src_;

	for (; len >= 16; len -= 16)
	{
		HW_DATA_FIFO = *src++;
		HW_DATA_FIFO = *src++;
		HW_DATA_FIFO = *src++;
		HW_DATA_FIFO = *src++;
	}
	for (; len > 0; len -= 4)
		HW_DATA_FIFO = *src++;
}

//
// Copy from AES read FIFO
//
static void HWReadAES (void *dst_, int len)
{
	uint32 *dst = (uint32 *) dst_;

	while (len > 0)
		if (!(HW_RSSI_AES & AES_EMPTY))
		{
			*dst++ = HW_AES_FIFO;
			len -= 4;
		}
}

//
// Copy to AES write FIFO
//
static void HWWriteAES (void *src_, int len)
{
	uint32 *src = (uint32 *) src_;

	while (len > 0)
		if (!(HW_RSSI_AES & AES_FULL))
		{
			HW_AES_FIFO = *src++;
			len -= 4;
		}
}

//
// Wait for a PIO pin to be set
//
static BOOL WaitForPIO (int pin)
{
	uint32 t = getTimeMS();

	while (PIO_GET (pin) == 0)
	{
		if (getTimeMS() - t > LOAD_TIMEOUT)
			return FALSE;

        if (PIN_DONE == pin)
            *(volatile uint32 *)MAC_BASE = 0;
  }
	return TRUE;
}

//
// Load the baseband controller firmware
//
static MacReturn LoadHW (void)
{
	// Baseband firmware image
	extern const char bbfw_bin_data[];
	extern const int bbfw_bin_data_len;

	static BOOL loaded = FALSE;
	int i;

	// Load only once after reset
	if (loaded)
		return ERR_OK;

	//DPRINTF ("HW load\n");

	// Set up PIO pins for programming
	PIO_INPUT (PIN_INIT);
	PIO_INPUT (PIN_DONE);

    // CS3 is used to put the FPGA into program mode.
    // Map to MAC_PROG address, 32 bit width, 24 wait states.
	*NA_MPMCStaticConfig3_ADDRESS   = 0x82;
	*NA_MPMCStaticWaitWen3_ADDRESS  = 0;
	*NA_MPMCStaticWaitOen3_ADDRESS  = 0;
	*NA_MPMCStaticWaitRd3_ADDRESS   = 24;
	*NA_MPMCStaticWaitPage3_ADDRESS = 0;
	*NA_MPMCStaticWaitWr3_ADDRESS   = 24;
	*NA_MPMCStaticWaitTurn3_ADDRESS = 0;

	*NA_SCM_SMCS_STATIC_MEM_BASE(3) = MAC_PROG;
	*NA_SCM_SMCS_STATIC_MEM_MASK(3) = MAC_MASK;

	// Put FPGA into program mode
	*(volatile uint32 *)MAC_PROG = 0;

	// Disable CS3
	*NA_SCM_SMCS_STATIC_MEM_MASK(3) = 0;


	// Wait for FPGA to enter program mode
	if (!WaitForPIO (PIN_INIT))
		return ERR_LOAD;

    // CS2 is used for FPGA code download.
    // Map to MAC_BASE address, 32 bit width, 0 wait states.
	*NA_MPMCStaticConfig2_ADDRESS   = 0x82;
	*NA_MPMCStaticWaitWen2_ADDRESS  = 0;
	*NA_MPMCStaticWaitOen2_ADDRESS  = 0;
	*NA_MPMCStaticWaitRd2_ADDRESS   = 0;
	*NA_MPMCStaticWaitPage2_ADDRESS = 0;
	*NA_MPMCStaticWaitWr2_ADDRESS   = 0;
	*NA_MPMCStaticWaitTurn2_ADDRESS = 0;

	*NA_SCM_SMCS_STATIC_MEM_BASE(2) = MAC_BASE;
	*NA_SCM_SMCS_STATIC_MEM_MASK(2) = MAC_MASK;

	// Write each byte to LSB
	for (i = 0; i < bbfw_bin_data_len; i++)
		*(volatile uint32 *)MAC_BASE = bbfw_bin_data[i];

	// Wait for FPGA program complete
	if (!WaitForPIO (PIN_DONE))
		return ERR_LOAD;

	loaded = TRUE;
	return ERR_OK;
}

//
// Initialize the wireless hardware
//
MacReturn InitHW (void)
{
	int ret;

	DPRINTF ("HW init\n");

	// Hardware supports 802.11b/g, channels 1-14, AES acceleration
	hwAES = TRUE;
	macParams.chan_mask &= 0x3fff;

	// Minimum fragment size
	if (macParams.frag_thresh < MIN_FRAG_SIZE)
		macParams.frag_thresh = MIN_FRAG_SIZE;
	//if configured with an odd fragmentation threashhold, the device should
	//round down to the nearst even mumber Vantive #19867
	if (macParams.frag_thresh % 2)
		macParams.frag_thresh = macParams.frag_thresh - 1;

	// Max fragment size applies when more than one fragment.
	// Leave threshold possibly higher for single fragment.
	maxFragSize = macParams.frag_thresh;
	if (maxFragSize > MAX_FRAG_SIZE)
		maxFragSize = MAX_FRAG_SIZE;

    // Set RSSI average to a middle value so it
    // converges to its initial value faster
    macStatus.rx_signal = -50*RSSI_SCALE;

	InitTimer();

	// Load the baseband controller firmware
	ret = LoadHW();
	if (ret != ERR_OK)
		return (MacReturn)ret;

	// Configure PIO pins
	PIO_OUTPUT (PIN_LED);
	PIO_INTR (PIN_INTR);

    // CS2 is used for wireless baseband registers.
    // Map to MAC_BASE address, 32 bit width, 8 wait states.
	*NA_MPMCStaticConfig2_ADDRESS   = 0x82;
	*NA_MPMCStaticWaitWen2_ADDRESS  = 0;
	*NA_MPMCStaticWaitOen2_ADDRESS  = 2;
	*NA_MPMCStaticWaitRd2_ADDRESS   = 8;
	*NA_MPMCStaticWaitPage2_ADDRESS = 0;
	*NA_MPMCStaticWaitWr2_ADDRESS   = 4;
	*NA_MPMCStaticWaitTurn2_ADDRESS = 2;

	*NA_SCM_SMCS_STATIC_MEM_BASE(2) = MAC_BASE;
	*NA_SCM_SMCS_STATIC_MEM_MASK(2) = MAC_MASK;

	// Init baseband processor and MAC assist
	HW_GEN_CONTROL = GEN_INIT;
	intrMask = INTR_RXFIFO|INTR_TXEND|INTR_TIMEOUT|INTR_RXOVERRUN;
	HW_INTR_MASK = intrMask;
	HWMemcpy (&HW_STAID0, macParams.sta_addr, WLN_ADDR_SIZE);

    // Transmit on primary antenna
    HW_RSSI_AES = 0x88000000;

	// Set anternna diversity if enabled
	if (macParams.options & WLN_OPT_ANTDIV)
		HW_GEN_CONTROL |= GEN_ANTDIV;

	// Initialize RF tranceiver
    WriteRF (7, 0x27ffe); // TEST register
    WriteRF (6, 0xf81ac); // Filter Register
    WriteRF (5, 0x19e4f); // Transmitter Gain -- Low power setting
    WriteRF (4, 0x0002b); // Receiver Gain
    WriteRF (3, 0x46662); // VCO Control -- Low power setting
	WriteRF (0, 0x27fdc/*0x25f9c*/); // Mode Control - Calibrate Filter
	waitUS (10);

	MacSetTxPower (macParams.tx_power);

	// Install interrupt handler
	NAInstallIsr (INTR_ID, HandleIR, NULL);

    // Added so that calling wln_start() to restart the driver will re-enable the interrupt.
    NAEnableIsr (INTR_ID);

	return ERR_OK;
}

//
// Shutdown the wireless hardware
//
void ShutdownHW (void)
{
	DPRINTF ("HW shutdown\n");

	//HW_GEN_CONTROL = GEN_RESET; //this is causing the ccwi9c hang
	HW_GEN_CONTROL &= ~GEN_RXEN; //disable receiving
	HW_MAC_CONTROL = 0;
	HW_INTR_MASK = 0;

	NADisableIsr (INTR_ID);
}

//
// Select a channel
// @param channel Channel number: 1-14
//
void SetChannel (int channel)
{
    HW_GEN_CONTROL &= ~GEN_RXEN;

	// Set frequency divider for channel
    WriteRF (1, freqTable[channel].integer);
    WriteRF (2, freqTable[channel].fraction);

    // Filter Calibration
	WriteRF (0, 0x27fdc/*0x25f9c*/);
	waitUS (10);

    // VCO Calibration
	WriteRF (0,0x27fda /*0x25f9a*/);
	waitUS (80);

	// Mode Control - RX/TX mode
	WriteRF (0, 0x27fd4/*0x25f94*/);
	//allow the transceiver to settle in the new mode of operation
	waitUS (10);

    HW_GEN_CONTROL |= GEN_RXEN;
}

//
// Set BSS mode and IDs
// @param bssCaps BSS capabilities, OR of CAP_xxx
// @param bssid ID of BSS to join or start
// @param ssid Service set ID
// @param ssid_len Service set ID length
// @param basic Basic rates, OR of WLN_RATE_xxx
// @param atim IBSS ATIM window size in TU
//
void SetBSS (int bssCaps, MacAddr bssid, uint8 *ssid, int ssid_len, uint16 basic, int atim)
{
	// Set BSSID in hardware
	HWMemcpy (&HW_BSSID0, bssid, WLN_ADDR_SIZE);

	// Set SSID and basic rates in hardware
	HWMemcpy (&HW_SSID, ssid, ssid_len);
	HW_SSID_LEN = ssid_len |
		((basic & RATE_MASK_OFDM) << 20) |
		((basic & (RATE_MASK_PSK|RATE_MASK_CCK)) << 16);

	// IBSS mode
	if (bssCaps & CAP_IBSS)
	{
		// If starting IBSS, set beacon and ATIM intervals
		HW_CFP_ATIM = atim | (BEACON_INT << 16);

		// Write beacon frame to beacon buffer
		if (bcnFrame)
		{
			HW_GEN_CONTROL |= GEN_BEACEN;
			HWWriteFifo (&bcnFrame->frameHdr,
				sizeof(FrameHeader) + sizeof(PlcpHeader) + bcnFrame->length - FCS_SIZE);
			HW_GEN_CONTROL &= ~GEN_BEACEN;
		}

		// Set interrupt mask to enable TBTT and ATIM interrupts
		HW_INTR_STATUS = INTR_TBTT|INTR_ATIM; // clear
		intrMask |= INTR_TBTT|INTR_ATIM;      // enable
		HW_INTR_MASK = intrMask;

		// Enable IBSS mode
		HW_MAC_CONTROL |= CTRL_IBSS|CTRL_BEACONTX;
	}

	// ESS mode
	else
	{
		// Set interrupt mask to disable TBTT and ATIM interrupts
		intrMask &= ~(INTR_TBTT|INTR_ATIM); // disable
		HW_INTR_MASK = intrMask;

		// Disable IBSS mode
		HW_MAC_CONTROL &= ~(CTRL_IBSS|CTRL_BEACONTX);
	}
}

void SetPromiscuousMode(BOOLEAN enable)
{
    if (enable == TRUE)    // Enable PROMISC mode
    {
		HW_MAC_CONTROL |= CTRL_PROMISC;
	}

    else    //disable
    {
		HW_MAC_CONTROL &= ~CTRL_PROMISC;
	}
}

//
// Set the link status LED
// @param on TRUE to turn LED on
//
void SetLED (BOOL on)
{
	if (on)
		PIO_CLR (PIN_LED);
	else
		PIO_SET (PIN_LED);
}

//
// Set the link activity LED
// @param on TRUE to turn LED on
//
void SetActivityLED (BOOL on)
{
	if (on)
		PIO_CLR (PIN_ACTIVITY_LED);
	else
		PIO_SET (PIN_ACTIVITY_LED);
}

//
// Transmit a frame
// @param buf Frame to transmit
// @param cw Contention window size, 0 if no backoff
// @param next Next expected event, one of SF_xxx
//
void MacSendFrame (MacBuffer *buf, int cw, int next)
{
	CcmpData data;

	// Set backoff timer
	if (cw)
	{
		// Pick a random time up to cw, convert to usecs
		cw = 10*((int)wln_rand() & cw);
		if (buf->macHdr.fc.type == TYPE_BEACON)
		{
			// Beacon frame is already in beacon buffer.
			// Only set backoff timer here.
			HW_BEACON_BO = cw;

			// Simulate TxEnd
			HandleTxEnd();
			return;
		}
		else
			HW_BACKOFF = cw;
	}

	// Use short preamble?
	if (USE_SHORTPRE (buf->tx_rate))
		HW_GEN_CONTROL |= GEN_SHPRE;
	else
		HW_GEN_CONTROL &= ~GEN_SHPRE;

	// If expecting TxEnd, enable interrupt
	if (next == SF_TXEND)
		HW_INTR_MASK |= INTR_TXEND; // enable
	else
		HW_INTR_MASK &= ~INTR_TXEND; // disable

	// Use hardware AES if encrypted with AES
	if (hwAES && buf->macHdr.fc.protected && CcmpGetData (buf, &data))
	{
		while (HW_RSSI_AES & AES_BUSY)
			;

		// Prevent transmit until encrypted data is ready
		HW_GEN_CONTROL |= GEN_TXHOLD;

		// Write frame headers to transmit FIFO
		HWWriteFifo (&buf->frameHdr, 4*10);

		// Set transmit mode
		HW_AES_MODE = 0;

		// Write key, init block, headers to AES FIFO
		HWWriteAES (data.key->rd_key, 4*44);
		HWWriteAES (data.init, 4*12);

		// Write plaintext data to AES FIFO
		HWWriteAES (buf->body+EXTIV_SIZE, buf->length-DATA_SIZE-CCMP_SIZE);
		
		// Start transmit
		HW_GEN_CONTROL &= ~GEN_TXHOLD;
	}

	else
	{
	    // Prevent transmit FIFO underrun
		HW_GEN_CONTROL |= GEN_TXHOLD;
		
		// Write frame to transmit FIFO, excluding FCS
		HWWriteFifo (&buf->frameHdr,
			sizeof(FrameHeader) + sizeof(PlcpHeader) + buf->length - FCS_SIZE);
			
		// Start transmit
		HW_GEN_CONTROL &= ~GEN_TXHOLD;
    }
}

//
// Handle an interrupt request
//
static int HandleIR (void *ignore)
{
    int gain;

    (void)ignore;

	while (1)
	{
		// Get interrupt status
		uint32 intrStatus = HW_INTR_STATUS & intrMask;
		if (intrStatus == 0)
			break;

		// Handle errors
		if (intrStatus & (INTR_TIMEOUT|INTR_ABORT))
		{
			HandleMacError (intrStatus);

			// Clear txEnd after timeout
			HW_INTR_STATUS = INTR_TXEND;
			intrStatus &= ~INTR_TXEND;
		}
		if (intrStatus & INTR_RXOVERRUN)
			macStats.rxDropOverrun++;

		// Handle beacon/ATIM timer
		if (intrStatus & (INTR_TBTT|INTR_ATIM))
			HandleBeacon (intrStatus & INTR_TBTT);

		// Transmit complete
		if (intrStatus & INTR_TXEND)
			HandleTxEnd();

		// Received a frame
		if (intrStatus & INTR_RXFIFO)
		{
			// Read frames until FIFO empty
			while (!(HW_GEN_STATUS & STAT_RXFE))
			{
				// Get only the headers (up to MAC addr2 field).
				// This is enough to hold the smallest frame (ACK).
				// The rest is gotten by GetFrame() if needed.
				HWReadFifo (&rxFrame.frameHdr, 24);

                // Convert received signal strength to dbm
                rxFrame.rssi = (rxFrame.frameHdr.rx.rssiLNA << 5) | rxFrame.frameHdr.rx.rssiVGA;
                gain = lnaTable[rxFrame.frameHdr.rx.rssiLNA] + 2*rxFrame.frameHdr.rx.rssiVGA;
                if (gain > 96)
                    rxFrame.rx_signal = -98;
                else if (gain > 86)
                    rxFrame.rx_signal = gainTable[gain-87];
                else
                    rxFrame.rx_signal = 5-gain;

				// Add into average RSSI
				macStatus.rx_signal += rxFrame.rx_signal - macStatus.rx_signal/RSSI_SCALE;

				// Process the frame
				HandleRxEnd (&rxFrame);
			}
		}

		// Clear interrupts
		HW_INTR_STATUS = intrStatus;
	}

	return 0;
}

//
// Get the complete frame that is being received
// @param buf Buffer to copy frame into, length field contains frame size
//
BOOL GetFrame (MacBuffer *buf)
{
	CcmpData data;
	volatile uint32 tmp;

	// Copy data already read from rxFrame
	buf->rx_signal = rxFrame.rx_signal;
    buf->rssi = rxFrame.rssi;
	memcpy (&buf->frameHdr, &rxFrame.frameHdr, 24);

	// Use hardware AES if encrypted with AES
	if (hwAES && buf->macHdr.fc.protected && buf->length >= DATA_SIZE+CCMP_SIZE)
	{
		// Get frame headers and init vector from receive FIFO
		HWReadFifo (buf->macHdr.addr3, 16);

		// Get AES key, init block, headers
		if (CcmpGetData (buf, &data))
		{
			while (HW_RSSI_AES & AES_BUSY)
				;
			// Set receive mode
			tmp = HW_AES_MODE;

			// Write key, init vector, headers to AES FIFO
			HWWriteAES (data.key->rd_key, 4*44);
			HWWriteAES (data.init, 4*12);

			// Get plaintext data from AES FIFO
			HWReadAES (buf->body+EXTIV_SIZE, buf->length-DATA_SIZE-CCMP_SIZE);

			while (HW_RSSI_AES & AES_BUSY)
				;

			// Check if MIC is correct
			if (!(HW_RSSI_AES & AES_MIC))
			{
				macStats.rxDropDecrypt++;
				macStats.rxDropMICFail++;
				return FALSE;
			}
		}

		// Not AES, get remaining data from receive FIFO
		else
			HWReadFifo (buf->body+EXTIV_SIZE, buf->length-32);
	}

	// Get remaining data from receive FIFO
	else
		HWReadFifo (buf->macHdr.addr3, buf->length-16);

	return TRUE;
}

//
// Received frame is not needed, remove from FIFO
// @param length MAC frame size
//
void DropFrame (int length)
{
	volatile uint32 tmp;

	if (length <= HW_RX_FIFO_SIZE)
	{
		// First 16 bytes already read
		length -= 16;
		for (; length > 0; length -= 4)
			tmp = HW_DATA_FIFO;
	}
	else
	{
		// Length is unreasonable.
		// We might not be looking at the start of a frame.
		// Reset the receive FIFO.
		HW_GEN_CONTROL |=  GEN_RXFIFORST;
		HW_GEN_CONTROL &= ~GEN_RXFIFORST;
	}
}

#ifdef NET_OS_SPECIFIC
//
// Public function to load the FPGA image
//
int InitWirelessHw(void)
{
	if (LoadHW() != ERR_OK)
		return 1;
	else
		return 0;
}
#endif

//
// Convert a received signal strength value in dbm
// to a percentage value (0-100).
//
int MacGetSignalQuality (int dbm)
{
    int n;

    if (dbm == 0)
        return 0;

// Useful RSSI range in dbm
#define RSSI_LOW    (-90)
#define RSSI_HIGH   (-30)

    n = 100*(dbm-RSSI_LOW)/(RSSI_HIGH-RSSI_LOW);
    if (n < 0)
        n = 0;
    else if (n > 100)
        n = 100;

    return n;
}

//
// This is used to set the transmit power.
// Value is in dbm and will be limited to 0-15 dbm.
//
void MacSetTxPower(int value)
{
    if (value < 0)
        value = 0;
    else if ((value > 10) && (macParams.options & WLN_OPT_BONLY))
    	value = 10;
    else if (value > 15)
        value = 15;

    WriteRF (5, 0x19e40 | powerTable[value]);
}


#ifdef FCC_ENABLED // TODO

//
// This function is used to put the mac driver in a quiet mode where
// no transmitting is done, but the FPGA is still running.  It is my
// hope that by disabling interrupts the FPGA will not do any automatic
// acking of received data.
//
void MacSetQuiet(int enable_quiet)
{
    int level;

    /* disable interrupts */
    level = DISABLE();

    if (enable_quiet)
    {
        /* disable the mac driver */
        macStatus.state = WLN_ST_STOPPED;

        //fcc_transmit_disabled = 1;

        /* disable mac interrupts */
        HW_INTR_MASK = 0;
    }
    else
    {
        //fcc_transmit_disabled = 0;

        /* enable common mac interrupts */
        HW_INTR_MASK |= INTR_RXEND|INTR_TXEND|INTR_TIMEOUT|INTR_ABORT;

        /* enable the mac driver */
        macStatus.state = WLN_ST_SCANNING;
        staState = SS_WAIT;
    }

    /* enable interrupts */
    ENABLE(level);
}

// This buffer is used as the transmit buffer for testing the
// continuous transmit FCC test.
static unsigned char fcc_txbuffer[64];

//
// This function is used to fill the fcc_txbuffer with random data
// for the transmit test.
//
void MacUpdateFccBuffer(unsigned int transmit_mode, unsigned int transmit_rate)
{
    int i;

    switch (transmit_rate)
    {
    case 0:
        /* 1mpbs */
        fcc_txbuffer[0] = 0x0a;
        break;

    case 1:
        /* 2mpbs */
        fcc_txbuffer[0] = 0x14;
        break;

    case 2:
        /* 5.5mpbs */
        fcc_txbuffer[0] = 0x37;
        break;

    case 3:
        /* 11mpbs */
        fcc_txbuffer[0] = 0x6e;
        break;

    default:
        fcc_txbuffer[0] = 0x0a;
        break;
    }

    if (transmit_mode == 4)
    {
        /* for fcc transmit 01 pattern */
        for (i=1; i < 64; i++)
        {
            fcc_txbuffer[i] = 0xaa;
        }
    }
    else
    {
        for (i=1; i < 64; i++)
        {
            fcc_txbuffer[i] = (unsigned char )wln_rand();
        }
    }

    /* copy to transmit buffer */
    HWMemcpy (HW_TX_BUFFER, fcc_txbuffer, 64);
}

//
// This function puts the driver into continuous transfer mode.  When doing
// so it attempts to shutdown normal driver functionality so the driver
// will not interfere with the continuous transfer.  However I do not think
// the process of shutting down the driver while enabling continous transfer
// mode is completely void of interference, but I think it will work well
// enough for FCC testing.
//
void MacContinuousTransmit(int enable, unsigned int transmit_mode, unsigned int transmit_rate)
{
    int level;
    uint8 data;

    switch (transmit_mode)
    {
    case 0:
        /* random */
        data = 0x4;
        break;

    case 1:
        /* zeros */
        data = 0x14;
        break;

    case 2:
        /* ones */
        data = 0x24;
        break;

    case 3:
        /* Unmodulated signal, carrier only, continuous
           transmit.  Used for Japan emmissions testing. */
        data = 0x17;
        break;

    case 4:
        /* FCC - continuous transmit, scramble disable, 010101...*/
        data = 0x06;
        break;

    default:
        data = 0x4;
        break;
    }


    /* disable interrupts */
    level = DISABLE();

    if (enable)
    {
        /* disable the mac driver */
        macStatus.state = WLN_ST_STOPPED;

        MacUpdateFccBuffer(transmit_mode, transmit_rate);

        /* disable mac interrupts */
        HW_INTR_MASK = 0;

        /* set to continuous transmit */
        HW_TEST_MODE = data;

        /* start transmiting */
        HW_MAC_CONTROL = CTRL_TXREQ;
    }
    else
    {
        /* clear test mode */
        HW_TEST_MODE = 0x0;

        /* enable common mac interrupts */
        HW_INTR_MASK |= INTR_RXEND|INTR_TXEND|INTR_TIMEOUT|INTR_ABORT;

        macStatus.state = WLN_ST_SCANNING;
        staState = SS_WAIT;
    }

    /* enable interrupts */
    ENABLE (level);
}

void MacWriteRF(uint8 addr, uint32 data)
{
    uint8 data0;
    uint8 data1;
    uint8 data2;

    data0 = data & 0xff;
    data1 = (data >> 8) & 0xff;
    data2 = (data >> 16) & 0xff;

    HW_SSI_ADDR  = addr | SSI_ADDR_WR;

    HW_SSI_DATA0 = data0;
    HW_SSI_DATA1 = data1;
    HW_SSI_DATA2 = data2;
    waitUS (50);
}

void MacReadRF(uint8 addr, uint32 *data)
{
    uint8 data0;
    uint8 data1;
    uint8 data2;

    addr &= ~SSI_ADDR_WR;

    HW_SSI_ADDR  = addr;
    waitUS(50);
    data0 = HW_SSI_DATA0;
    data1 = HW_SSI_DATA1;
    data2 = HW_SSI_DATA2;

    *data = ((uint32)data0) | (((uint32)data1) << 8) | (((uint32)data2) << 16);
}

void MacWriteReg8(unsigned int offset, uint8 data)
{
    REG8(offset) = data;
}

uint8 MacReadReg8(unsigned int offset)
{
    uint8 data;

    data = REG8(offset);
    return data;
}

void MacWriteReg16(unsigned int offset, uint16 data)
{
    REG16(offset) = data;
}


uint16 MacReadReg16(unsigned int offset)
{
    uint16 data;

    data = REG16(offset);
    return data;
}

#endif
