//
// Embedded 802.11b wireless network interface
// MAC layer hardware interface
// Copyright 2003 Digi International
//
#include "mac.h"
#include "cs.h"
#include "reg_def.h"
#ifdef NDS_SPECIFIC
#include "pll.h"
#endif 

#ifdef NET_OS_SPECIFIC
#include "npttypes.h"
#else
#include "bcomm.h"
#endif

//
// Hardware register defines
//
#define MAC_BASE		0x03100000		// Register base address
#define MAC_SIZE		0x00004000U		// Register segment size
#define MAC_PROG		(MAC_BASE+MAC_SIZE)	// FPGA program address

#define	REG8(offset)	(* (volatile uint8  *) (MAC_BASE + (offset) + 1))
#define	REG16(offset)	(* (volatile uint16 *) (MAC_BASE + (offset)))

// Baseband control registers
#define	HW_GEN_CONTROL	REG8(0x02)		// General control
#define	HW_CORR_ENERGY	REG8(0x0a)		// Correlation energy
#define	HW_SRCH_THRESH	REG8(0x0e)		// Search threashold
#define	HW_TEST_MODE	REG8(0x18)		// Transmit test mode
#define	HW_INTR_MASK	REG8(0x1a)		// Interrupt mask
#define	HW_INTR_STATUS	REG8(0x1c)		// Interrupt status

// RF control registers
#define	HW_SSI_ADDR		REG8(0x40)		// RF SSI address
#define	HW_SSI_DATA0	REG8(0x42)		// RF SSI data (bits 0-7)
#define	HW_SSI_DATA1	REG8(0x44)		// RF SSI data (bits 8-15)
#define	HW_SSI_DATA2	REG8(0x46)		// RF SSI data (bits 16-23)
#define	HW_AGC_CONTROL	REG8(0x48)		// AGC control
#define	HW_AGC_RESET	REG8(0x4a)		// AGC reset

// MAC control registers
// 16-bit registers must be byte swapped on big endian
#define	HW_STAID0		REG16(0x1000)	// Station ID (3 words)
#define	HW_STAID1		REG16(0x1002)
#define	HW_STAID2		REG16(0x1004)
#define	HW_BSSID0		REG16(0x1006)	// BSS ID (3 words)
#define	HW_BSSID1		REG16(0x1008)
#define	HW_BSSID2		REG16(0x100a)
#define	HW_SSID_LEN		REG16(0x100c)	// SSID length (MSB), basic rates (LSB)
#define	HW_BACKOFF		REG16(0x100e)	// Backoff interval
#define	HW_LISTEN_INT	REG16(0x1010)	// Listen interval
#define	HW_DTIM_CFP		REG16(0x1012)	// DTIM period (MSB), CFP period (LSB)
#define	HW_CFP_ATIM		REG16(0x1014)	// CFP max duration/ATIM period
#define	HW_BEACON_INT	REG16(0x1016)	// Beacon interval
#define	HW_MAC_STATUS	REG16(0x1018)	// MAC status
#define	HW_MAC_CONTROL	REG16(0x101a)	// MAC control
#define	HW_REMAIN_BO	REG16(0x101c)	// Remaining backoff interval
#define	HW_BEACON_BO	REG16(0x101e)	// Beacon backoff interval
#define	HW_RSSI			REG16(0x1028)	// Receive signal strength
#define	HW_RX_OVERRUN	REG16(0x102a)	// Receive overrun count
#define	HW_RSSI_EN		REG16(0x102e)	// RSSI enable
#define	HW_SSID			REG16(0x2000)	// Service set ID

// Receive buffer
#define	HW_RX_BUFFER	(&REG16(0x3040))// Start of RX buffer
#define	HW_RX_SIZE		0x7c0			// Size in bytes

// Transmit buffer
#define	HW_TX_BUFFER	(&REG16(0x2040))// Start of TX buffer
#define	HW_TX_SIZE		0x7c0			// Size in bytes

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

// General control register bits
#define	GEN_BBEN		0x01			// Baseband enable
#define	GEN_SHPRE		0x02			// TX short preamble
#define	GEN_ANTDIV		0x04			// Antenna diversity
#define	GEN_ANTSEL		0x08			// Antenna select

// Transmit test mode register bits
#define	TEST_SPRD_DIS	0x01			// Spread disable
#define	TEST_SCR_DIS	0x02			// Scramble disable
#define	TEST_CONT_TX	0x04			// Continuous transmission
#define	TEST_DATA_BUF	0x00			// Transmit first 64 bytes of buffer
#define	TEST_DATA_ZEROS	0x10			// Transmit all zeros
#define	TEST_DATA_ONES	0x20			// Transmit all ones

// Interrupt mask and status register bits
#define	INTR_RXEND		0x01			// Receive complete
#define	INTR_TXEND		0x02			// Transmit complete
#define	INTR_TIMEOUT	0x04			// CTS/ACK receive timeout
#define	INTR_ABORT		0x08			// CTS transmit abort
#define	INTR_TBTT		0x10			// Beacon transmission time
#define	INTR_ATIM		0x20			// ATIM interval end

// MAC status register bits
#define	STAT_DIR_MSG	SWAP16(0x0001)	// Directed message
#define	STAT_BC_MSG		SWAP16(0x0002)	// Broadcast message
#define	STAT_FCS		SWAP16(0x0004)	// CRC correct
#define	STAT_MAC_ERR	SWAP16(0x0008)	// MAC protocol error

// MAC control register bits
#define	CTRL_TXREQ		SWAP16(0x0001)	// Transmit request
#define	CTRL_AUTOTXDIS	SWAP16(0x0002)	// Auto-transmit disable
#define	CTRL_BEACONTX	SWAP16(0x0004)	// Beacon transmit enable
#define	CTRL_PROMISC	SWAP16(0x0008)	// Promiscuous mode
#define	CTRL_IBSS		SWAP16(0x0010)	// IBBS mode

// RF control register bits
#define	SSI_ADDR_WR		0x80			// Write indicator

//
// PIO pins
//
#define PIN_PROG		4				// FPGA program (port A4)

#define PIN_INTR		0				// Interrupt (port C0)

#ifdef WiSP_ENABLED
#define PIN_DONE		3				// FPGA program done (port C2)
#define PIN_INIT		7				// FPGA initiailized (port C6)
#else
#define PIN_DONE		2				// FPGA program done (port C2)
#define PIN_INIT		6				// FPGA initiailized (port C6)
#endif

#define PIN_LED			PIN_INIT		// Link status LED (port C6)

// Get/set/clear a PIO pin
#define PIO_GET(addr, pin)		(*(addr) &   (1 << (pin)))
#define PIO_SET(addr, pin)		(*(addr) |=  (1 << (pin)))
#define PIO_CLR(addr, pin)		(*(addr) &= ~(1 << (pin)))

// Set PIO pin mode
#define PIO_OUTPUT(addr, pin)	(PIO_CLR(addr, (pin)+24), PIO_SET(addr, (pin)+16))
#define PIO_INPUT(addr, pin)	(PIO_CLR(addr, (pin)+24), PIO_CLR(addr, (pin)+16))
#define PIO_INTR(addr, pin)		(PIO_SET(addr, (pin)+24), PIO_SET(addr, (pin)+16))

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


// RF transceiver VCO register value for each channel
// Values for 44MHz oscillator
static const uint32 vcoTable[] =
{
	0,
	0x00096C, // 1
	0x080970, // 2
	0x100974, // 3
	0x180978, // 4
	0x000980, // 5
	0x080984, // 6
	0x100988, // 7
	0x18098C, // 8
	0x000994, // 9
	0x080998, // 10
	0x10099C, // 11
	0x1809A0, // 12
	0x0009A8, // 13
	0x0009B4, // 14
};

// Map low RSSI values to dbm
static const int8_t rssiTable[] = 
{
    -87, -83, -81, -79, -78, -76, -75, -74, -73, -71
};

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

static uint8 mac_txpower = WLN_TXPOWER_DEFAULT; // Current power level
static uint8 mac_channel = 1;                   // Current channel

static void HandleIR (int ignore);


//
// 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 = 100;

	// 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 = 10000/machw_time;

#ifdef DEBUG
	DPRINTF ("time=%ld ms, loops=%ld\n", (long)machw_time, (long)timerLoops);

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

	DPRINTF ("time=%ld ms (should be 100)\n", (long)machw_time);
#endif
}

//
// Write data to the RF tranceiver
// @param addr Register address
// @param data Data to write (24 bits)
//
static void WriteRF (uint8 addr, uint32 data)
{
	HW_SSI_ADDR  = addr | SSI_ADDR_WR;
	HW_SSI_DATA0 = (uint8) data;
	HW_SSI_DATA1 = (uint8) (data >> 8);
	HW_SSI_DATA2 = (uint8) (data >> 16);
	waitUS (50);
}

//
// Initialize the RF tranceiver
// @param channel Channel number: 1-14
//
static void InitRF (int channel)
{
	// Disable PHP speedup.
	// This also keeps speedup disabled when we write to register 0,
	// so we must write register 3 before register 0.
	WriteRF (0x03, 0x010000);

	WriteRF (0x02, 0x000080);
	WriteRF (0x01, 0x00b250);

	// Must write register 0 after 1
	WriteRF (0x00, vcoTable[channel]);

	// Must write another register (4 recommended) after 0
    WriteRF (0x04, 0x0191a1); // TX/RX mode
}

//
// Resets the radio.  Called after changing channel or power variables.
//
static void ResetRF(void)
{
    HW_GEN_CONTROL &= ~GEN_BBEN;

    WriteRF (0x07, mac_txpower << 4);	// TX power
    InitRF (mac_channel);

    HW_AGC_RESET = 1;
    HW_GEN_CONTROL |= GEN_BBEN;
}

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

	for (; len >= 8; len -= 8)
	{
		*dst++ = *src++;
		*dst++ = *src++;
		*dst++ = *src++;
		*dst++ = *src++;
	}
	for (; len > 0; len -= 2)
		*dst++ = *src++;
}

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

	while (PIO_GET (NARM_PORTC_ADDR, pin) == 0)
		if (getTimeMS() - machw_time > LOAD_TIMEOUT)
			return FALSE;

	return TRUE;
}

//
// Load the baseband controller firmware
//
static MacReturn LoadHW (void)
{
	// Baseband firmware image
#ifdef NET_OS_SPECIFIC
	extern const char bbfw_bin_data[];
	extern const int bbfw_bin_data_len;
#endif
#ifdef NDS_SPECIFIC
	extern uint8 _binary_bbfw_start[];
	extern uint8 _binary_bbfw_size[];
#endif

	static BOOL loaded = FALSE;
	int i;

// We must set the registers and chip selects even if already programmed,
// to support the PLL vector scheme of programming.

    // CS3 is used for wireless baseband registers.
    // Map to MAC_BASE address, size MAC_SIZE, 16 bit width,
    // async reads and writes, 4 wait states.
    *NARM_CSAR3_ADDR = 0;
    *NARM_CSOR3_ADDR = 0;
    *NARM_CSORB3_ADDR = 0;

	narm_write_reg (NARM_CSOR_REG, NARM_CSOR3_ADDR, mask, (-MAC_SIZE) >> CS_BASE_MASK_SHIFT);
	narm_write_reg (NARM_CSOR_REG, NARM_CSOR3_ADDR, wait, 4);
	narm_write_reg (NARM_CSOR_REG, NARM_CSOR3_ADDR, ps, CSOR_16_BIT_PORT_SIZE);

	narm_write_reg (NARM_CSAR_REG, NARM_CSAR3_ADDR, base, MAC_BASE >> CS_BASE_MASK_SHIFT);
	narm_write_reg (NARM_CSAR_REG, NARM_CSAR3_ADDR, v, TRUE);

	// Load only once after reset
	if (!loaded)
	{

	DPRINTF ("HW load\n");

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

	// On the Wi-ME, Wi-SP, and original Wi-EM hardware,
	// GPIO pin A4 is used to put the FPGA into program mode.
	// On the new Wi-EM, this pin is used as a SPI bus clock.
	// If it has already been programmed for SPI, don't change it.
	if (!PIO_GET(NARM_PORTA_ADDR, PIN_PROG+24))
	{
		PIO_OUTPUT (NARM_PORTA_ADDR, PIN_PROG);

		// Put FPGA into program mode
		PIO_CLR (NARM_PORTA_ADDR, PIN_PROG); // assert low
		PIO_SET (NARM_PORTA_ADDR, PIN_PROG); // deassert
	}

    // On the new Wi-EM, CS2 is used to put the FPGA into program mode.
    // On other hardware, CS2 is not connected, so it's safe to change it.
    // Map to MAC_PROG address, size MAC_SIZE, 16 bit width,
    // async reads and writes, max wait states.
    *NARM_CSAR2_ADDR = 0;
    *NARM_CSOR2_ADDR = 0;
    *NARM_CSORB2_ADDR = 0;

	narm_write_reg (NARM_CSOR_REG, NARM_CSOR2_ADDR, mask, (-MAC_SIZE) >> CS_BASE_MASK_SHIFT);
	narm_write_reg (NARM_CSOR_REG, NARM_CSOR2_ADDR, wait, 15);
	narm_write_reg (NARM_CSOR_REG, NARM_CSOR2_ADDR, ps, CSOR_16_BIT_PORT_SIZE);

	narm_write_reg (NARM_CSORB_REG, NARM_CSORB2_ADDR, wait, 3);

	narm_write_reg (NARM_CSAR_REG, NARM_CSAR2_ADDR, base, MAC_PROG >> CS_BASE_MASK_SHIFT);
	narm_write_reg (NARM_CSAR_REG, NARM_CSAR2_ADDR, v, TRUE);

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

	// Disable CS2
    *NARM_CSAR2_ADDR = 0;
    *NARM_CSOR2_ADDR = 0;
    *NARM_CSORB2_ADDR = 0;

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

	// Must use 0 wait states, CS controlled (synchronous) mode for download
	narm_write_reg (NARM_CSOR_REG, NARM_CSOR3_ADDR, wait, 0);
	narm_write_reg (NARM_CSOR_REG, NARM_CSOR3_ADDR, wsync, TRUE);

	// Write each byte to LSB at register 0
#ifdef NET_OS_SPECIFIC
	for (i = 0; i < bbfw_bin_data_len; i++)
		REG16(0) = bbfw_bin_data[i];
#endif
#ifdef NDS_SPECIFIC
	for (i = 0; i < (int) _binary_bbfw_size; i++)
		REG16(0) = _binary_bbfw_start[i];
#endif

	if (!WaitForPIO (PIN_DONE))
		return ERR_LOAD;
	}

	// Use 4 wait states, WE controlled (asynchronous) mode for operation
	narm_write_reg (NARM_CSOR_REG, NARM_CSOR3_ADDR, wait, 4);
	narm_write_reg (NARM_CSOR_REG, NARM_CSOR3_ADDR, wsync, FALSE);

	loaded = TRUE;
	return ERR_OK;
}

#ifdef NDS_SPECIFIC
int ProgramNetworkFpga(void)
{
    int ret;
    if (bc_kludge_pll())
    {
	PllChange(2);
	//blink(0x222);
    }
    ret = LoadHW();
    if (bc_kludge_pll())
    {
	PllChange(7);
	//blink(0x333);
    }
    return ret;
}
#endif


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

	DPRINTF ("HW init\n");

	// Hardware supports only 802.11b and channels 1-14
	rxFrame.frameHdr.rx.modType = MOD_PSKCCK;
	macParams.options |= WLN_OPT_BONLY;
	macParams.chan_mask &= 0x3fff;

	// Disable RTS and fragmentation due to interrupt latency problems
	macParams.rts_thresh = INT_MAX;
	macParams.frag_thresh = INT_MAX;

    // 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 ret;

	// After loading, PIO pins are LED outputs
	PIO_OUTPUT (NARM_PORTC_ADDR, PIN_DONE);
	PIO_OUTPUT (NARM_PORTC_ADDR, PIN_INIT);
	PIO_SET (NARM_PORTC_ADDR, PIN_DONE);
	PIO_SET (NARM_PORTC_ADDR, PIN_INIT);

	// Configure interrupt pin
	PIO_INTR (NARM_PORTC_ADDR, PIN_INTR);

	// Init baseband processor
	HW_AGC_RESET = 1;
	HW_AGC_CONTROL = 0;  // Auto AGC mode

	// Init MAC assist
	HW_GEN_CONTROL = 0;
	HW_RSSI_EN = 1;
	HWMemcpy (&HW_STAID0, macParams.sta_addr, WLN_ADDR_SIZE);
	intrMask = INTR_RXEND|INTR_TXEND|INTR_TIMEOUT;
	HW_INTR_MASK = intrMask;

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

    // Set transmit power
    MacSetTxPower (macParams.tx_power);

	// Set synthesizer settings
    InitRF (mac_channel);

	// Set to sleep mode
	WriteRF (0x04, 0x0191a0);
 
 
    // Set AGC settings
    // The WiSP-specific AGC settings below ought to be extended to WIEM/WIME products 
    // that are using the same transceiver chip. We decided to start implementing 
	// the changes on the WISP platform only. All three platforms have been successfully
	// tested against. 
    // Reduce AGC MAXGAIN to 73dBm from 79dBm 
	// Tighten the AGC loop
    // in order to handle higher than expected SA2400A transceiver noise level
	WriteRF (0x05, 0xb98421); 

	//old AGC settings WriteRF (0x05, 0xbc9dfb);


	// Set to VCO calibration mode
	WriteRF (0x04, 0x0191a8);
	waitUS(3000);	    // Spec says 2200 us

	// Set to sleep mode
	WriteRF (0x04, 0x0191a0);
	WriteRF (0x04, 0x0191a0);

	// Reset synthesizer settings
    InitRF (mac_channel);

	// Set to frequency calibration mode
	WriteRF (0x04, 0x0191a4);
	waitUS (10);	    // Spec says 3 us

	// Set to tx/rx mode
	WriteRF (0x04, 0x0191a1);

	// Install interrupt handler
#ifdef NET_OS_SPECIFIC
	naIsrInstall (PIN_INTR, (NA_ISR_HANDLER) HandleIR, 0);
#endif
#ifdef NDS_SPECIFIC
	NAInstallIsr (PIN_INTR, (NA_ISR_HANDLER) HandleIR, 0);
#endif

	return ERR_OK;
}

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

	HW_GEN_CONTROL = 0;

#ifdef NET_OS_SPECIFIC
	naIsrUninstall (PIN_INTR);
#endif
#ifdef NDS_SPECIFIC
	NAUninstallIsr (PIN_INTR);
#endif
}

//
// Select a channel
// @param channel Channel number: 1-14
//
void SetChannel (int channel)
{
	mac_channel = (uint8) channel;
	ResetRF();
}

//
// 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)
{
    NA_UNUSED_PARAM(basic);

	// 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 = SWAP16((ssid_len << 8) |
		(basic & (RATE_MASK_PSK|RATE_MASK_CCK)));

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

		// 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;
	}

	// 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;
	}
}

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 activity LED (stubbed function, taken care in FPGA level)
// @param on TRUE to turn LED on
//
void SetActivityLED (BOOL on)
{
    /* taken care in FPGA level */
	if (on)
        return;
}

//
// Set the link status LED
// @param on TRUE to turn LED on
//
void SetLED (BOOL on)
{
	if (on)
		PIO_CLR (NARM_PORTC_ADDR, PIN_LED);
	else
		PIO_SET (NARM_PORTC_ADDR, PIN_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)
{
	// 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)
			HW_BEACON_BO = SWAP16(cw);
		else
			HW_BACKOFF = SWAP16(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

	// If no TX after RX, disable auto TX
	if (next == SF_RX)
		HW_MAC_CONTROL |= CTRL_AUTOTXDIS; // disable
	else
		HW_MAC_CONTROL &= ~CTRL_AUTOTXDIS; // enable

	// Copy frame to TX buffer.
	HWMemcpy (HW_TX_BUFFER, &buf->plcpHdr, sizeof(PlcpHeader) + buf->length-FCS_SIZE);

	// Start transmit
	HW_MAC_CONTROL |= CTRL_TXREQ;
}

//
// Handle an interrupt request
//
static void HandleIR (int ignore)
{
    int rssi;
    
    NA_UNUSED_PARAM(ignore);

	while (1)
	{
		// Get interrupt status
		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;
		}

		// 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_RXEND)
		{
			// For fast response time, get only the PLCP and MAC headers (up to addr2 field).
			// The rest is gotten by GetFrame() if needed.
			HWMemcpy (&rxFrame.plcpHdr, HW_RX_BUFFER, 4);
			HWMemcpy (&rxFrame.macHdr, HW_RX_BUFFER+3, 16);

			// Get received signal strength, convert to dbm
            rssi = HW_RSSI;
            if (rssi < 8)
                rxFrame.rx_signal = -93;
            else if (rssi < 18)
                rxFrame.rx_signal = rssiTable[rssi-8];
            else
                rxFrame.rx_signal = rssi-88;
            
            // 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;
	}
}

//
// Get the complete frame that is being received
// @param buf Buffer to copy frame into, length field contains frame size
//
BOOL GetFrame (MacBuffer *buf)
{
	// Clear interrupt to indicate receive buffer is available
	HW_INTR_STATUS = INTR_RXEND;
	intrStatus &= ~INTR_RXEND;

	// Copy data from RX buffer
	HWMemcpy (&buf->macHdr, HW_RX_BUFFER+3, buf->length);

	buf->plcpHdr = rxFrame.plcpHdr;
	buf->rx_signal = rxFrame.rx_signal;
	macStats.rxDropOverrun = SWAP16(HW_RX_OVERRUN);

	return TRUE;
}

//
// Received frame is not needed
// @param length MAC frame size
//
void DropFrame (int length)
{
    NA_UNUSED_PARAM(length);
}

#ifdef NET_OS_SPECIFIC
//
// Public function to load the FPGA image
//
int InitWirelessHw(void)
{
    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 3-16 dbm.
//
void MacSetTxPower (int value)
{
    // Map 3-16 dbm to register values 0-14.
    // Limit register value to 14 to avoid hardware problems.
    value = (14*(value-3)+6)/13;
    if (value < 0)
        value = 0;
    else if (value > 14)
        value = 14;

    mac_txpower = value;
    ResetRF();
}

static wln_bss bss_save = WLN_BSS_NONE;

//
// 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 */
        bss_save = macParams.bss_type;
        macParams.bss_type = WLN_BSS_NONE;
		staState = SS_WAIT;

        /* disable common mac interrupts */
        HW_INTR_MASK = 0;

        /* enable rx data path */
        HW_INTR_MASK = INTR_RXEND;
    }
    else
    {
        /* enable common mac interrupts */
        HW_INTR_MASK |= INTR_RXEND|INTR_TXEND|INTR_TIMEOUT|INTR_ABORT;

        /* enable the mac driver */
        macParams.bss_type = bss_save;
        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];
/* mfg rf test buffer */
static unsigned char radio_txbuffer[1024];

//
// 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 */
        bss_save = macParams.bss_type;
        macParams.bss_type = WLN_BSS_NONE;
		staState = SS_WAIT;

        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;

        macParams.bss_type = bss_save;
        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;
}

void MacWriteReg32(unsigned int offset, uint32 data)
{

   /* Not supported */
   (void) offset;
   (void) data;

}

uint32 MacReadReg32(unsigned int offset)
{
    /* Not supported */
   (void) offset;

   return 0xFFFFFFFF;
}

/*Transmit a frame every tx_frame_period milliseconds*/
void MacRadioPrepareTXBeacon(unsigned int tx_frame_period)
{

    //HW_BEACON_INT = SWAP16(0x0014);

    if(tx_frame_period)
         /* 1-100 milliseconds */
         HW_BEACON_INT = SWAP16(tx_frame_period); 
    else
        /* 0 = 20 msecs default tx frame period */ 
        HW_BEACON_INT = SWAP16(0x0014);   

    /*set IBSS (ad-hoc) mode of operation */
    HW_MAC_CONTROL = SWAP16(0x0014);

    /* place transceiver in loopback mode to prevent receiver interference */
    HW_TEST_MODE = 0x08;
}


/*builds a 1024/128 bytes frame*/
void MacRadioTXRandomBuffer( unsigned int transmit_rate, unsigned int tx_frame_length)
{
    int i;

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

        if(!tx_frame_length)
	{
           radio_txbuffer[2] = 0x00;
           radio_txbuffer[3] = 0x20;
        }
        else
	{
           radio_txbuffer[2] = 0xe8;
           radio_txbuffer[3] = 0x03;
        } 


        break;

    case 1:
        /* 2mpbs */
        radio_txbuffer[0] = 0x14;
        radio_txbuffer[1] = 0x04;

        if(!tx_frame_length)
	{
          radio_txbuffer[2] = 0x00;
          radio_txbuffer[3] = 0x10;
	}
	else
	{
          radio_txbuffer[2] = 0xF4;
          radio_txbuffer[3] = 0x01;
        }
        break;

    case 2:
        /* 5.5mpbs */
        radio_txbuffer[0] = 0x37;
        radio_txbuffer[1] = 0x04;

        if(!tx_frame_length)
	{
          radio_txbuffer[2] = 0xd2;
          radio_txbuffer[3] = 0x05;
        }
	else
	{
          radio_txbuffer[2] = 0xB5;
          radio_txbuffer[3] = 0x00;   
        }
        break;

    case 3:
        /* 11mpbs */
        radio_txbuffer[0] = 0x6e;
        radio_txbuffer[1] = 0x04;

        if(!tx_frame_length)
	{
          radio_txbuffer[2] = 0xe9;
          radio_txbuffer[3] = 0x02;
	}
	else
	{
          radio_txbuffer[2] = 0x5A;
          radio_txbuffer[3] = 0x00;
	}
        break;

    default:
        radio_txbuffer[0] = 0x0a;
        radio_txbuffer[1] = 0x04;

        if(!tx_frame_length)
	{
          radio_txbuffer[2] = 0x00;
          radio_txbuffer[3] = 0x20;
	}
	else
	{
          radio_txbuffer[2] = 0xe8;
          radio_txbuffer[3] = 0x03;
	}
        break;
    }

    /* fill the rest of the frame with random data */
    for (i=4; i < 1023; i++)
    {
        radio_txbuffer[i] = (unsigned char )wln_rand();
    }

    /* copy to transmit buffer */
    HWMemcpy (HW_TX_BUFFER, radio_txbuffer, 1024);

}


void MacRadioTXBeacon(int enable, unsigned int transmit_rate, unsigned int tx_frame_period, unsigned int tx_frame_length)
{
    int level;

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

    if (enable)
    {
        /* disable the mac driver */
        bss_save = macParams.bss_type;
        macParams.bss_type = WLN_BSS_NONE;
		staState = SS_WAIT;

	MacRadioPrepareTXBeacon(tx_frame_period);

        MacRadioTXRandomBuffer(transmit_rate, tx_frame_length);

        /* disable mac interrupts */
        HW_INTR_MASK = 0;

        /* set to continuous transmit */
        HW_TEST_MODE = 0x08;
 
        /* start transmiting */
        HW_MAC_CONTROL |= CTRL_TXREQ;
    }
    else
    {
        /* clear test mode */
        HW_TEST_MODE = 0x0;

	// Disable IBSS mode
        HW_MAC_CONTROL &= ~CTRL_IBSS;

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

        /* restart the mac driver */
        macParams.bss_type = bss_save;
        staState = SS_WAIT;
    }

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

//
// This function is used to put the mac driver in a quiet mode where
// no transmitting is done, but the FPGA is still running.  
void MacRadioRXTest(int enable, uint32* rx_frames)
{
    int level;

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

    if (enable)
    {

        /* disable the mac driver */
        bss_save = macParams.bss_type;
        macParams.bss_type = WLN_BSS_NONE;
		staState = SS_WAIT;

      /* reset macStats  */
      memset (&macStats, 0, sizeof macStats);

      SetPromiscuousMode(TRUE);
      MacSetQuiet(1);
    }
    else
    {
      SetPromiscuousMode(FALSE);

      *rx_frames =  (uint32) macStats.rxFrames;  

      MacSetQuiet(0);

      macParams.bss_type = bss_save;
      staState = SS_WAIT;

    }

    /* enable interrupts */
    ENABLE (level);

}
