// Embedded 802.11a/g wireless network interface
// MAC layer hardware interface for ConnectCore Wi-9C
// Copyright 2006 Digi International
//
// #include "bsp.h"
#include "mac.h"
#include "assert.h"
#include "mcsys_reg.h"
#include "WiFi_USB.h"
#include "mac_hw_wiwave.h"

#ifdef NET_OS_SPECIFIC
#include <bsp_api.h>
#else
#include <libutils.h>
#include "digi_products.h"
#include "hwtypes.h"
#include "gpio.h"
#include "cellbar.h"
#include "cmdm_registry.hh"
#endif

#if (USB_WIFI_ENABLED == 1)

#ifdef NET_OS_SPECIFIC
#define MAC_PRINTF(y,...) printf(__VA_ARGS__)
#else
#define MAC_PRINTF(y,...) trace(WIRELESS_TRACE,y, __VA_ARGS__)
#endif

#ifdef NET_OS_SPECIFIC
#define MAC_USB_EEPROM_ACCESS_MS    21
#else
#define MAC_USB_EEPROM_ACCESS_MS    6
#endif

// Register read timeout
#define MAC_USB_READ_TIMEOUT_MS     1000

// Number of bytes in a USB 'Write Packet'
#define USB_REG_WRITE_LEN 8

// Size of the initialization vector (4*12)
// This is in four byte words
#define AES_INIT_DATA_LEN 48 

// Memory pools used to store data for mult-part USB meaages.
// One pool for the message header and the other for the body
static TX_BLOCK_POOL usb_message_hdr_pool;
static TX_BLOCK_POOL usb_message_pool;

// This is the number of USB messages
#define MAX_MULTI_MSG 512

// This is the size of a USB message
#define MAX_MULTI_MSG_SIZE 512

// Size of memory pools. threadX requires an extra sizeof(void *)
#define USB_MESSAGE_HDR_POOL_BLOCK_SIZE  (sizeof(MacUsbMsg)+sizeof(void *))
#define USB_MESSAGE_HDR_POOL_SIZE        (USB_MESSAGE_HDR_POOL_BLOCK_SIZE*MAX_MULTI_MSG)

#define USB_MESSAGE_POOL_BLOCK_SIZE (MAX_MULTI_MSG_SIZE+sizeof(void *))
#define USB_MESSAGE_POOL_SIZE       (USB_MESSAGE_POOL_BLOCK_SIZE*MAX_MULTI_MSG)


static void WriteRF (uint8 addr, uint32 data);
static int mac_usb_read_req_init(void);
static MacUsbMsg *MacUsbInitMsg(void);

static int MacUsbWriteData(uint8 reg, uint16 len, void *data);
static int MacUsbWriteFifoData(uint8 reg, uint16 len, void *data, int block);

//
// 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)))
#define	REG16(offset)	(* (volatile uint16 *) (MAC_BASE + (offset)))
#define REG8(offset)	(* (volatile uint8  *) (MAC_BASE + (offset) + 1))


// Baseband control registers
#define HW_VERSION      0x00     // Version
#define HW_GEN_CONTROL  0x04     // General control
#define HW_GEN_STATUS   0x08     // General status
#define HW_RSSI_AES     0x0c     // RSSI and AES status
#define HW_INTR_MASK    0x10     // Interrupt mask
#define HW_INTR_STATUS  0x14     // Interrupt status
#define HW_SPI          0x18     // RF SPI interface
#define HW_DATA_FIFO_END  0x20   // Data FIFO
#define HW_DATA_FIFO      0x21   // Data FIFO addr to prevent under-run condition
#define HW_AES_FIFO_END   0x30   // AES FIFO
#define HW_AES_FIFO       0x31	 // AES FIFO addr to prevent under-run condition
#define HW_AES_CONTROL    0x38   // AES Control  
// MAC control registers.
#define HW_STAID0       0x40     // Station ID (6 bytes)
#define HW_STAID1       0x44
#define HW_BSSID0       0x48     // BSS ID (6 bytes)
#define HW_BSSID1       0x4c
#define HW_SSID_LEN     0x50     // Basic rates (high 16 bits), SSID length (low 8 bits)
#define HW_BACKOFF      0x54     // Backoff period (16 bits)
#define HW_LISTEN       0x58     // Listen interval (16 bits), CFP (8 bits), DTIM (8 bits)
#define HW_CFP_ATIM     0x5c     // CFP max duration/ATIM period (16 bits), beacon interval (16 bits)
#define HW_MAC_STATUS   0x60     // FW version (high 16 bits), MAC status (low 8 bits)
#define HW_MAC_CONTROL  0x64     // MAC control (8 bits)
#define HW_REMAIN_BO    0x68     // Remaining backoff (16 bits)
#define HW_BEACON_BO    0x6c     // Beacon backoff (16 bits), beacon mask (8 bits)
#define HW_SSID         0x80     // Service set ID (32 bytes)

#define HW_EEPROM_CONTROL 0xF0 // EEPROM Control register addr
#define HW_EEPROM_DATA    0xF8 // EEPROM Data register addr
// FIFO sizes in bytes
#define HW_TX_FIFO_SIZE 1792
#define HW_RX_FIFO_SIZE 2048

// AES_CONTROL_REGISTER
#define AES_MODE_BIT         0x10
#define AES_KEY_LOAD_BIT     0x04

// 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_TESTMODE	0x80000000	// Mode of operation: test/normal

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

// General status register bits
#define STAT_RXFE       0x00000010      // Receive FIFO empty
#define STAT_SFTRST     0x40000000      // Soft reset

// 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
#define CTRL_AES_DISABLE 0x00000020     // AES decryption disable

// GPIO output values for LEDs
#define LED_OFF         1
#define LED_ON          0

// 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 int8_t 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

unsigned char *current_rx_frame_ptr;    // Pointer to the current frame
int            current_rx_frame_len=0;  // Number of bytes left in the frame

uint8 wless_mac_addr[6] = {0x0, 0xA, 0x0, 0xA, 0x0, 0xA};


/* 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_wiwave(MacBuffer *buf)
{
#define LOWEST_MAXAGC        (0x72)
#define HIGHEST_MAXAGC       (0x7A)
#define RSSI_AVG_COUNT  8*4 /*need to be careful not to write the max_agc register 
                                            too often since we are going over usb. */

    unsigned char maxgain = 0;
    unsigned char lowest = 0, highest = 0;
    static int k=0, j=0, i =0, tempRssi=0;
    static uint8 old_mg = (GEN_INIT & 0x007F0000) >> 16;

    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 != old_mg))
    {
        //uint32 mg;
        //if (MacUsbReadReg(HW_GEN_CONTROL, (void *)&mg, 4) != 0)
        //    return;
                    
        MacUsbWriteReg(HW_GEN_CONTROL, 0xFF80FFFF, 
            MAC_COMPLETE_MSG, MAC_OP_AND, (MacUsbMsg *)NULL);
        MacUsbWriteReg(HW_GEN_CONTROL, ((maxgain & 0x7F) << 16), 
            MAC_COMPLETE_MSG, MAC_OP_OR, (MacUsbMsg *)NULL);
            
        old_mg = maxgain;
    }
}


//
// 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
//    MAC_PRINTF(TRACE_INFO, "time=%ld ms, loops=%ld\n", ntime, timerLoops);

    ntime = getTimeMS();
    waitUS (100000);
    ntime = getTimeMS() - ntime;
    
//    MAC_PRINTF(TRACE_INFO, "time=%ld ms (should be 100)\n", ntime);
#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;
    
    MacUsbWriteReg(HW_SPI,  (addr << 20) | data, MAC_COMPLETE_MSG, MAC_OP_WRITE,
                  (MacUsbMsg *)NULL);

    waitUS (10);
}

// Write to transmit FIFO 0x20 or 0x21
static void HWWriteFifo(void *src_, int len, BOOL last)
{
   if (last)
       MacUsbWriteFifoData(HW_DATA_FIFO_END, len, src_, 0);
   else
       MacUsbWriteFifoData(HW_DATA_FIFO, len, src_, 0);
}


// Write AES frame headers to 0x21
static void HWWriteAESHeaderFifo (void *src_, int len)
{
   MacUsbWriteFifoData(HW_DATA_FIFO, len, src_, 1);
}

// Write plaintext key, IV and data to 0x30 or 0x31 
static void HWWriteAESFifo (void *src_, int len, BOOL last)
{
    if (last)
        MacUsbWriteFifoData(HW_AES_FIFO_END, len, src_, 1);
    else
        MacUsbWriteFifoData(HW_AES_FIFO, len, src_, 1);
}

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

    static BOOL loaded = FALSE;
    
    // Load only once after reset
    if (loaded)
        return ERR_OK;

    MAC_PRINTF(TRACE_INFO, "HW load\n");

    loaded = TRUE;
    return ERR_OK;
}
#endif

extern int usb_wifi_read(unsigned char *, int , int* , int*, MacBuffer*);
extern void reset_system(int);

static MacReturn InitPolling(void)
{
    MacBuffer     *mac_buffer = NULL;
    MacReturn     outcome = ERR_OK;
    unsigned int  len = BUF_SIZE; /* Size of the MacBuffer portion that is filled in by USB: 512*3+24 */
    unsigned int  bytesTransferred = 0;
    unsigned int  errorCode=0;

    /*Allocate a MacBuffer Start polling the WiFi device */ 
    mac_buffer = MacGetBuffer();
    if (mac_buffer == (MacBuffer *)NULL)
    {
      outcome = ERR_MEMORY;
    }
    else
    {
            /* Start polling the WiWave device */
            usb_wifi_read((unsigned char *)&mac_buffer->usb_encoded_length, len,
                                   (int *)&bytesTransferred, (int *)&errorCode, mac_buffer);

            if (errorCode)
            {     
#if 0
              /* 
                     Last failure place for Vantive 22604. Unclear why the call
                     to usb_wifi_read() API sporadically fails. Until this is fixed,
                     we reset the device if unable to start polling it.                      
                 */ 

                  /* power-on reset */
              reset_system(0);
#endif

                  // neither "panic creature seems to be alive :("
                  //panic("Wiwave: unable to start polling the device");  
                  //panic_num("Wiwave: unable to start polling the device", errorCode,0);
                  outcome = ERR_MEMORY; 
            }
    }

    return outcome;
}

//
// Initialize the wireless hardware
//

static void *msg_hdr_pool_ptr=0, *msg_pool_ptr=0;

MacReturn InitHW (void)
{
    unsigned int ret;

    // Wait for the Wi-Wave card to be connected. 
    if (usb_wifi_wait(USB_WIFI_DEVICE_CONNECTED) != 0) 
    {
        MAC_PRINTF(TRACE_CRITICAL, "Wi-Wave not connected\n");
        return ERR_MEMORY;
    }
    
    // Create the block pools used for multi-part USB messages.
    msg_hdr_pool_ptr = malloc(USB_MESSAGE_HDR_POOL_SIZE);
   
    ret = tx_block_pool_create(&usb_message_hdr_pool,
                               "USB message hdr pool",
                                USB_MESSAGE_HDR_POOL_BLOCK_SIZE,
                                msg_hdr_pool_ptr,
                                USB_MESSAGE_HDR_POOL_SIZE);

    if (ret != TX_SUCCESS) 
             return ERR_MEMORY;

    msg_pool_ptr = malloc(USB_MESSAGE_POOL_SIZE);
 
    ret = tx_block_pool_create(&usb_message_pool,
                               "USB message pool",
                               USB_MESSAGE_POOL_BLOCK_SIZE,
                               msg_pool_ptr,
                               USB_MESSAGE_POOL_SIZE);

    if (ret != TX_SUCCESS) 
             return ERR_MEMORY;
   
    // Initalize read request packet
    if (mac_usb_read_req_init() != 0) 
            return ERR_MEMORY;

    // Start USB IN tokens  
    ret = InitPolling();
    if (ret != ERR_OK)
        return ret;
    
    // Hardware supports 802.11b/g, channels 1-14, AES acceleration
    hwAES = TRUE;
    macParams.chan_mask &= 0x3fff;
    
    if (macParams.frag_thresh < MIN_FRAG_SIZE)
        macParams.frag_thresh = MIN_FRAG_SIZE;
    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();
    
    // Init baseband processor and MAC assist
    
    // Reset baseband processor in case it was already running
    MacUsbWriteReg(HW_GEN_STATUS, STAT_SFTRST, 
                   MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);

    // HW_GEN_CONTROL = GEN_INIT;
    MacUsbWriteReg(HW_GEN_CONTROL, GEN_INIT, 
                   MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);
                   
    // ***Phillips USB peripheral workaround***
    // In full speed mode, after a soft reset, the USB controller on the 
    // Wi-Wave sends the first two USB data packets as length 64 bytes,
    // regardless of actual size.  The host controller combines these and
    // the next packet into a single transfer.  This causes the first
    // register read (MAC address) to fail.  Work around this by sending
    // 3 small read requests. MacUsbReadReqHandleResponse() will ignore the 
    // response because no request buffer has been set up.
    MacUsbWriteReg (0xFD, 1 << 16, MAC_COMPLETE_MSG, 0, NULL);
    MacUsbWriteReg (0xFD, 1 << 16, MAC_COMPLETE_MSG, 0, NULL);
    MacUsbWriteReg (0xFD, 1 << 16, MAC_COMPLETE_MSG, 0, NULL);
    
    // get module mac address from perm storage
    MacUsbReadEEPROM((unsigned int)VPD_WIWAVE_MAC_OFFSET, (char* )&wless_mac_addr, WLN_ADDR_SIZE);
    memcpy (macParams.sta_addr, (uint8 *)&wless_mac_addr, WLN_ADDR_SIZE);

    // HWMemcpy (&HW_STAID0, macParams.sta_addr, WLN_ADDR_SIZE);
    MacUsbWriteData(HW_STAID0, WLN_ADDR_SIZE, macParams.sta_addr);

    // Transmit on primary antenna
    MacUsbWriteReg(HW_RSSI_AES, 0x88000000, 
                   MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);

    // Set anternna diversity if enabled
    if (macParams.options & WLN_OPT_ANTDIV)
    {
        // HW_GEN_CONTROL |= GEN_ANTDIV;
        MacUsbWriteReg(HW_GEN_CONTROL, GEN_ANTDIV, 
                       MAC_COMPLETE_MSG, MAC_OP_OR, (MacUsbMsg *)NULL);
    }

    // Enable HW AES mode=1 
    MacUsbWriteReg(HW_AES_CONTROL, AES_MODE_BIT, 
                  MAC_COMPLETE_MSG, MAC_OP_WRITE,
                  (MacUsbMsg *)NULL);

    // Initialize RF tranceiver 
    WriteRF (7, 0x27ffe); // test
    WriteRF (6, 0xf81ac); // filter register
    WriteRF (5, 0x19e4f); // transmitter gain
    WriteRF (4, 0x0002b); // receiver gain
    WriteRF (3, 0x46662); // vco control
    WriteRF (0, 0x27fdc); // mode control
    waitUS (10);
    
    MacSetTxPower(macParams.tx_power);

    // Enable interrupts
    intrMask = INTR_RXFIFO|INTR_TXEND|INTR_TIMEOUT|INTR_RXOVERRUN;
    // HW_INTR_MASK = intrMask;
    MacUsbWriteReg(HW_INTR_MASK, intrMask, 
                   MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);

    return ERR_OK;
}

//
// Shutdown the wireless hardware
//
void ShutdownHW (void)
{
    int ret;
    
    // This function may be called more than once. 
    if (msg_hdr_pool_ptr)
    {
    
        MAC_PRINTF(TRACE_INFO, "HW shutdown\n");

        // HW_GEN_CONTROL = GEN_RESET;
        MacUsbWriteReg(HW_GEN_CONTROL, ~GEN_RESET, 
                   MAC_COMPLETE_MSG, MAC_OP_AND, (MacUsbMsg *)NULL);

         // HW_MAC_CONTROL = 0;
        MacUsbWriteReg(HW_MAC_CONTROL, 0, 
                   MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);
                   
        // HW_INTR_MASK = 0;
        MacUsbWriteReg(HW_INTR_MASK, 0, 
                   MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);

        // This function may be called more than once. 
        ret = tx_block_pool_delete(&usb_message_hdr_pool);
        if (ret != TX_SUCCESS) 
            panic("tx_block_delete failed1");
    
        free(msg_hdr_pool_ptr);

        ret = tx_block_pool_delete(&usb_message_pool);
        if (ret != TX_SUCCESS)
            panic("tx_block_delete failed2");
    
        free(msg_pool_ptr);
        msg_hdr_pool_ptr = 0;
     }

}

//
// Select a channel
// @param channel Channel number: 1-14
//
void SetChannel (int channel)
{
    MacUsbWriteReg(HW_GEN_CONTROL, ~GEN_RXEN,
                   MAC_COMPLETE_MSG, MAC_OP_AND, (MacUsbMsg *)NULL);

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

    WriteRF (0, 0x27fdc); 
    waitUS (10);
    WriteRF (0, 0x27fda);
    waitUS (80);
    WriteRF (0, 0x27fd4);
    waitUS (10);
 
    // HW_GEN_CONTROL |= GEN_RXEN;
    MacUsbWriteReg(HW_GEN_CONTROL, GEN_RXEN,
                   MAC_COMPLETE_MSG, MAC_OP_OR, (MacUsbMsg *)NULL);
                 
}

int wifi_num_set_bss=0;

//
// 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 atim IBSS ATIM window size in TU
//
void SetBSS (int bssCaps, MacAddr bssid, uint8 *ssid, int ssid_len, uint16 basic, int atim)
{
    wifi_num_set_bss++;

    // Set BSSID in hardware
    // HWMemcpy (&HW_BSSID0, bssid, WLN_ADDR_SIZE);
    MacUsbWriteData(HW_BSSID0, WLN_ADDR_SIZE, bssid);


    // HWMemcpy (&HW_SSID, ssid, ssid_len);
    MacUsbWriteData(HW_SSID, ssid_len, ssid);

    // Set SSID in hardware
    // HW_SSID_LEN = ssid_len;
    MacUsbWriteReg(HW_SSID_LEN, 
    ssid_len |
    ((basic & RATE_MASK_OFDM) << 20) |
    ((basic & (RATE_MASK_PSK|RATE_MASK_CCK)) << 16), 
     MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);

    // IBSS mode
    if (bssCaps & CAP_IBSS)
    {
        // If starting IBSS, set beacon and ATIM intervals
        // HW_CFP_ATIM = atim | (BEACON_INT << 16);
        MacUsbWriteReg(HW_CFP_ATIM, atim | (BEACON_INT << 16), 
                       MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);
 
        // Write beacon frame to beacon buffer
        if (bcnFrame)
        {
            // HW_GEN_CONTROL |= GEN_BEACEN;
            MacUsbWriteReg(HW_GEN_CONTROL, GEN_BEACEN, 
                           MAC_COMPLETE_MSG, MAC_OP_OR, (MacUsbMsg *)NULL);
                           
            HWWriteFifo (&bcnFrame->usb_status[2], 
                sizeof(FrameHeader) + sizeof(PlcpHeader) + bcnFrame->length - FCS_SIZE + 4, 1);

            // HW_GEN_CONTROL &= ~GEN_BEACEN;
            MacUsbWriteReg(HW_GEN_CONTROL, ~GEN_BEACEN, 
                           MAC_COMPLETE_MSG, MAC_OP_AND, (MacUsbMsg *)NULL);
        }

        // Set interrupt mask to enable TBTT and ATIM interrupts
        // HW_INTR_STATUS = INTR_TBTT|INTR_ATIM; // clear
        MacUsbWriteReg(HW_INTR_STATUS, INTR_TBTT|INTR_ATIM, 
                       MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);
 
        
        intrMask |= INTR_TBTT|INTR_ATIM;      // enable
        // HW_INTR_MASK = intrMask;
        MacUsbWriteReg(HW_INTR_MASK, intrMask, 
                       MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);

        
        // Enable IBSS mode, turn off HW AES decryption
        // HW_MAC_CONTROL |= CTRL_IBSS|CTRL_BEACONTX|CTRL_AES_DISABLE;
        MacUsbWriteReg(HW_MAC_CONTROL,   
                       CTRL_IBSS|CTRL_BEACONTX|CTRL_AES_DISABLE,
                       MAC_COMPLETE_MSG, MAC_OP_OR, (MacUsbMsg *)NULL);

    }
    else // ESS mode
    {
        // Set interrupt mask to disable TBTT and ATIM interrupts
        intrMask &= ~(INTR_TBTT|INTR_ATIM); // disable
        // HW_INTR_MASK = intrMask;
        MacUsbWriteReg(HW_INTR_MASK, intrMask, 
                       MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);


        // Disable IBSS mode
        // HW_MAC_CONTROL &= ~(CTRL_IBSS|CTRL_BEACONTX);
        MacUsbWriteReg(HW_MAC_CONTROL, ~(CTRL_IBSS|CTRL_BEACONTX), 
                           MAC_COMPLETE_MSG, MAC_OP_AND, (MacUsbMsg *)NULL);
                           
        // Turn off HW AES decryption until a key is set
        // HW_MAC_CONTROL |= CTRL_AES_DISABLE;
        MacUsbWriteReg(HW_MAC_CONTROL, CTRL_AES_DISABLE,
                       MAC_COMPLETE_MSG, MAC_OP_OR, (MacUsbMsg *)NULL);
    }
}


void SetPromiscuousMode(BOOLEAN enable)
{
    if (enable == TRUE)    // Enable PROMISC mode
    {
//	HW_MAC_CONTROL |= CTRL_PROMISC;
        MacUsbWriteReg(HW_MAC_CONTROL, CTRL_PROMISC, MAC_COMPLETE_MSG, MAC_OP_OR, (MacUsbMsg *)NULL);
    }
		
    else    //disable
    {
//	HW_MAC_CONTROL &= ~CTRL_PROMISC;
        MacUsbWriteReg(HW_MAC_CONTROL, ~CTRL_PROMISC, MAC_COMPLETE_MSG, MAC_OP_AND, (MacUsbMsg *)NULL);
	}
}

//
// Set the link status LED
// @param on TRUE to turn LED on
//
void SetLED (BOOL on)
{
#ifndef NET_OS_SPECIFIC
#define NUM_LEDS    5

    // Set the link status LED
    GpioSetOutputValue (GPIO_WIFI_LINK_GREEN, on ? LED_ON : LED_OFF);
        
#ifdef CELLULAR_ENABLED        
    // Set signal strength LEDs if not used by cellular
    if (CellModemId() == MODEM_TYPE_UNDISCOVERED)
#endif
    {
        // Scale signal strength average by number of samples
        int n = (macStatus.rx_signal + RSSI_SCALE/2) / RSSI_SCALE;
        
        // Get signal quality (0-100) and scale to 0 to NUM_LEDS.
        // 0 maps to no LEDs, 1 maps to 1 LED, 100 maps to NUM_LEDS.
        n = (NUM_LEDS*MacGetSignalQuality (n) + 99) / 100;
        NAIndicateSignalStrength (n);
    }
#else
    NA_UNUSED_PARAM(on);
#endif
}

//
// Set the link activity LED
// @param on TRUE to turn LED on
//
void SetActivityLED (BOOL on)
{
#ifndef NET_OS_SPECIFIC
    GpioSetOutputValue (GPIO_WIFI_ACT, on ? LED_ON : LED_OFF);
#else
    NA_UNUSED_PARAM(on);
#endif
}

//
// Transmit a frame
// This cannot be called from an ISR context
// @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;
    MacUsbMsg *msg_ptr;
    BOOL      last=1;

    msg_ptr = MacUsbInitMsg();
    if (msg_ptr == (MacUsbMsg *)NULL) 
        return;
    

    if ((buf->macHdr.fc.type == TYPE_CTS) ||
        (buf->macHdr.fc.type == TYPE_RTS))
    {
        // For a MultiFrame transmission we write to 0x21/0x30
        // Instead of 0x20/0x30 for all but the last frame 
        last = 0;  
    }

    // 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;
            MacUsbWriteReg(HW_BEACON_BO, cw, 
                           MAC_COMPLETE_MSG, MAC_OP_WRITE, msg_ptr);

            // Simulate TxEnd
            HandleTxEnd();
            return;
        }
        else
        {
             // HW_BACKOFF = cw;
             MacUsbWriteReg(HW_BACKOFF, cw, 
                            MAC_CONTINUE_MSG, MAC_OP_WRITE, msg_ptr);

        }
    }
    
    // Use short preamble?
    if (USE_SHORTPRE (buf->tx_rate))
    {
        // HW_GEN_CONTROL |= GEN_SHPRE;
        MacUsbWriteReg(HW_GEN_CONTROL, GEN_SHPRE, 
                       MAC_CONTINUE_MSG, MAC_OP_OR, msg_ptr);


    }
    else
    {
        // HW_GEN_CONTROL &= ~GEN_SHPRE;
        MacUsbWriteReg(HW_GEN_CONTROL, ~GEN_SHPRE, 
                       MAC_CONTINUE_MSG, MAC_OP_AND, msg_ptr);

    }
    if (hwAES && buf->macHdr.fc.protected && CcmpGetData (buf, &data))
    {
        if (next == SF_TXEND)
        {
            // HW_INTR_MASK |= INTR_TXEND; // enable
            MacUsbWriteReg(HW_INTR_MASK, INTR_TXEND, 
                           MAC_CONTINUE_MSG, MAC_OP_OR, msg_ptr);
        } 
        else
        {
            // HW_INTR_MASK &= ~INTR_TXEND; // disable
            MacUsbWriteReg(HW_INTR_MASK, ~INTR_TXEND, 
                           MAC_CONTINUE_MSG, MAC_OP_AND, msg_ptr);
        }
    }
    else
    {
        if (next == SF_TXEND)
        {
            // HW_INTR_MASK |= INTR_TXEND; // enable
            MacUsbWriteReg(HW_INTR_MASK, INTR_TXEND, 
                           MAC_COMPLETE_MSG, MAC_OP_OR, msg_ptr);
        } 
        else
        {
            // HW_INTR_MASK &= ~INTR_TXEND; // disable
            MacUsbWriteReg(HW_INTR_MASK, ~INTR_TXEND, 
                          MAC_COMPLETE_MSG, MAC_OP_AND, msg_ptr);
        }
    }

    // Use hardware AES if encrypted with AES 
    if (hwAES && buf->macHdr.fc.protected &&  CcmpGetData(buf, &data))
    {
        char *ptr;
        unsigned long aes_mode;
#ifdef NETOS_GNU_TOOLS
        static unsigned char buffer[2000] __attribute__ ((aligned(32)));
#else
#pragma alignvar (32)
        static unsigned char buffer[2000];
#endif
        aes_mode = (AES_MODE_BIT | (buf->body[3] >> 6));

        MacUsbWriteReg(HW_AES_CONTROL, aes_mode, 
                       MAC_COMPLETE_MSG, MAC_OP_WRITE, msg_ptr);

        // Write frame headers to transmit FIFO (0x21)
        HWWriteAESHeaderFifo(&buf->usb_status[2], 40 + 4);

        ptr = (char *)(buffer+64);
        
        // We currently copy the plain text data because the IV and Key
        // MUST go before the body to be sent over USB, we can fix this
        // by allocating enough space before the body in the mac_buff
        // structure.
        memcpy(ptr, data.init, AES_INIT_DATA_LEN);
        ptr += AES_INIT_DATA_LEN;
       
        memcpy(ptr, buf->body+EXTIV_SIZE, buf->length-DATA_SIZE-CCMP_SIZE);

        // Write IV and data to HW_DATA_FIFO (0x20) 
        // or 0x21 if it's not the last frame.
        HWWriteAESFifo(buffer+60, 
                       AES_INIT_DATA_LEN +
                       buf->length - DATA_SIZE-CCMP_SIZE + 4, 1); 
    }
        
    else
    {
        // Write frame to transmit FIFO, excluding FCS
        // This has register writes pre-pended
        HWWriteFifo(&buf->usb_status[2],
        sizeof(FrameHeader) + sizeof(PlcpHeader) + buf->length - FCS_SIZE + 4, last);
    }


}

// Offset into Interrupt Packet (In 32 bit words)
#define  RX_FRAME_OFFSET 3 

// Interrupt Status - Gen Status 1 
#define IRQ_GEN_STATUS1_START_BIT    16


//
// Handle an interrupt request
//
// data contains the address of Status0 of the interrupt packet
//
int MacHandleIR (MacBuffer *buf, int len)
{
    uint32    intrStatus;
    MacUsbMsg *msg_ptr=(MacUsbMsg *)NULL; 

    buf->length = len*4 - RX_FRAME_OFFSET*4;

    // Get the interrupt status (Gen Status 1)
    intrStatus = (buf->usb_status[0] >> IRQ_GEN_STATUS1_START_BIT);

    if (intrStatus == 0)
    {
        MacFreeBufferIR(buf);
        return 0;
    }

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

        if (msg_ptr != (MacUsbMsg *)NULL)
         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)
    {
        // Convert received signal strength to dbm
        int gain = lnaTable[buf->frameHdr.rx.rssiLNA] + 2*buf->frameHdr.rx.rssiVGA;
        if (gain > 96)
            buf->rx_signal = -98;
        else if (gain > 86)
            buf->rx_signal = gainTable[gain-87];
        else
            buf->rx_signal = 5-gain;
        
        buf->rssi = (buf->frameHdr.rx.rssiLNA << 5) | buf->frameHdr.rx.rssiVGA;
                
        // Add into average RSSI
        macStatus.rx_signal += buf->rx_signal - macStatus.rx_signal/RSSI_SCALE;

        // Process the frame
        HandleRxEnd (buf);
    }
    else
    {
        // Not a frame so free the macBuffer
        MacFreeBufferIR(buf);
    }

    return 0;
}

void MacClearTxEnd(void)
{
    //HW_INTR_STATUS = INTR_TXEND;
    MacUsbWriteReg(HW_INTR_STATUS, INTR_TXEND,
                   MAC_COMPLETE_MSG, MAC_OP_WRITE, 
                   (MacUsbMsg *)NULL);
}

//
// Get the complete frame that is being received
// @param buf Buffer to copy frame into, length field contains frame size
//
BOOL GetFrame (MacBuffer *buf)
{
    // All frames are alread in memory
    NA_UNUSED_PARAM(buf);

    return TRUE;
}

//
// Received frame is not needed, remove from FIFO
// @param length MAC frame size
// This can be called from an ISR
//
void DropFrame (int length)
{
    NA_UNUSED_PARAM(length);
}

void MacResetRxFifo(void)
{
    MacUsbMsg *msg_ptr=(MacUsbMsg *)NULL;

    msg_ptr = MacUsbInitMsg();
    if (msg_ptr != (MacUsbMsg *)NULL) 
    {
        MacUsbWriteReg(HW_GEN_CONTROL, GEN_RXFIFORST, 
                       MAC_CONTINUE_MSG, MAC_OP_OR, msg_ptr);
                       
        // HW_GEN_CONTROL &= ~GEN_RXFIFORST;
        MacUsbWriteReg(HW_GEN_CONTROL, ~GEN_RXFIFORST, 
                       MAC_COMPLETE_MSG, MAC_OP_AND, msg_ptr);
    }
}

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

int getHardwareStrapping(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 0-15 dbm.
//
void MacSetTxPower (int value)
{
    if (value < 0)
        value = 0;
    else if (value > 15)
        value = 15;

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

//
// Get the FPGA hardware and firmware (MAC assist/DSP) version numbers
//
int MacGetVersion (uint16 *hw, uint16 *fw)
{
    *hw = MacReadReg32 (HW_VERSION);
    *fw = MacReadReg32 (HW_MAC_STATUS) >> 16;
    return 0;
}

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

        /* disable continuous transmit mode */ 
        MacUsbWriteReg(HW_GEN_STATUS, 0x30000000, 
                       MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);

        /* disable mac interrupts except for rx interrupts */
        MacUsbWriteReg(HW_INTR_MASK, INTR_RXFIFO, 
                       MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);
    }
    else
    {

        /* enable common mac interrupts */
        MacUsbWriteReg(HW_INTR_MASK,  
                       INTR_RXFIFO|INTR_TXEND|INTR_TIMEOUT|INTR_ABORT, 
                       MAC_COMPLETE_MSG, MAC_OP_OR, (MacUsbMsg *)NULL);

        /* 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];
static unsigned char radio_txbuffer[1024];
static wln_bss bss_save = WLN_BSS_NONE;

//
// 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[4] = 0x00;
        fcc_txbuffer[5] = 0x10;                
        fcc_txbuffer[8] = 0x0a;
        break;

    case 1:
        /* 2mpbs */
        fcc_txbuffer[4] = 0x00;
        fcc_txbuffer[5] = 0x10; 
        fcc_txbuffer[8] = 0x14;
        break;

    case 2:
        /* 5.5mpbs */
        fcc_txbuffer[4] = 0x00;        
        fcc_txbuffer[5] = 0x10; 
        fcc_txbuffer[8] = 0x37;
        break;

    case 3:
        /* 11mpbs */
        fcc_txbuffer[4] = 0x00;
        fcc_txbuffer[5] = 0x10; 
        fcc_txbuffer[8] = 0x6e;
        break;

    /*OFDM */

    case 4:
        /* 6mpbs */
        fcc_txbuffer[4] = 0xEE;        
        fcc_txbuffer[5] = 0x10; 
        fcc_txbuffer[8] = 0x0B;
        break;

    case 5:
        /* 9mpbs */
        fcc_txbuffer[4] = 0xEE;        
        fcc_txbuffer[5] = 0x10; 
        fcc_txbuffer[8] = 0x0F;
        break;

    case 6:
        /* 12mpbs */
        fcc_txbuffer[4] = 0xEE;
        fcc_txbuffer[5] = 0x10; 
        fcc_txbuffer[8] = 0x0A;
        break;
        
    case 7:
        /* 18mpbs */
        fcc_txbuffer[4] = 0xEE;
        fcc_txbuffer[5] = 0x10; 
        fcc_txbuffer[8] = 0x0E;
        break;
        
    case 8:
        /* 24mpbs */
        fcc_txbuffer[4] = 0xEE;         
        fcc_txbuffer[5] = 0x10; 
        fcc_txbuffer[8] = 0x09;
        break;                

    case 9:
        /* 36mpbs */
        fcc_txbuffer[4] = 0xEE;        
        fcc_txbuffer[5] = 0x10; 
        fcc_txbuffer[8] = 0x0D;
        break;
        
    case 10:
        /* 48mpbs */
        fcc_txbuffer[4] = 0xEE;        
        fcc_txbuffer[5] = 0x10; 
        fcc_txbuffer[8] = 0x08;
        break;
        
    case 11:
        /* 54mpbs */
        fcc_txbuffer[4] = 0xEE;
        fcc_txbuffer[5] = 0x10;         
        fcc_txbuffer[8] = 0x0C;
        break;                

    default:
        fcc_txbuffer[4] = 0x00; 
    	fcc_txbuffer[5] = 0x10; 
        fcc_txbuffer[8] = 0x0a;
        break;
    }

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

    /* copy to transmit buffer */
    HWWriteFifo (&fcc_txbuffer, 16, TRUE);
}

//
// 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;
    uint32 data;

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

    case 1:
        /* zeros */
        data = 0x30300000;
        //data = 0x30330000;  /*with scrambling and spreading disabled */   
        break;

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

    case 3:
        /* Unmodulated signal, carrier only, continuous
           transmit.  Used for Japan emmissions testing. */
        data = 0x30700000;
        //data = 0x30730000; /*with scrambling and spreading disabled */
        break;

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

    default:
        data = 0x30100000;
        break;
    }

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

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

        MacUpdateFccBuffer(transmit_mode, transmit_rate);

        /* disable mac interrupts */
        MacUsbWriteReg(HW_INTR_MASK, 0,
                       MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);

       // Disable IBSS mode
        MacUsbWriteReg(HW_MAC_CONTROL, ~(CTRL_IBSS|CTRL_BEACONTX), 
                           MAC_COMPLETE_MSG, MAC_OP_AND, (MacUsbMsg *)NULL);

 
        if(3 == transmit_mode)
	  //enable test mode of operation
        MacUsbWriteReg(HW_GEN_CONTROL, GEN_TESTMODE, 
                       MAC_COMPLETE_MSG, MAC_OP_OR, (MacUsbMsg *)NULL);

        /* set to continuous transmit */
        MacUsbWriteReg(HW_GEN_STATUS, data, 
                       MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);

        /* start transmiting */
        MacUsbWriteReg(HW_MAC_CONTROL, CTRL_TXREQ, 
                       MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);

    }
    else
    {
         /* disable continuous transmit */
        switch(transmit_mode)
        {
        	case 0:
                        MacUsbWriteReg(HW_GEN_STATUS, 0x30000000, 
                                       MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);
        		break;

        	case 1:
                        MacUsbWriteReg(HW_GEN_STATUS, 0x30200000, 
                                     MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);
        		break;

        	case 2:
                        MacUsbWriteReg(HW_GEN_STATUS, 0x30400000, 
                                   MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);
        		break;
        		
        	case 3:
                        MacUsbWriteReg(HW_GEN_STATUS, 0x30000000, 
                                   MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);

                        MacUsbWriteReg(HW_GEN_CONTROL, ~GEN_TESTMODE, 
                                  MAC_COMPLETE_MSG, MAC_OP_AND, (MacUsbMsg *)NULL);
         		break;        		

        	case 4:
                         MacUsbWriteReg(HW_GEN_STATUS, 0x30000000, 
                                  MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);
        		break;
        		        		
        	default:
                         MacUsbWriteReg(HW_GEN_STATUS, 0x30000000, 
                                  MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);
        		break;	
        }
        
        macParams.bss_type = bss_save;
        staState = SS_WAIT;
        macStatus.state = WLN_ST_SCANNING;
      
        /* enable common mac interrupts */
        MacUsbWriteReg(HW_INTR_MASK,  
                       INTR_RXFIFO|INTR_TXEND|INTR_TIMEOUT|INTR_ABORT, 
                       MAC_COMPLETE_MSG, MAC_OP_OR, (MacUsbMsg *)NULL);
     }

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

void MacRadioPrepareTXBeacon(unsigned int tx_frame_period)
{
    if(tx_frame_period)
         /* 1-100 milliseconds */
        MacUsbWriteReg(HW_CFP_ATIM, (tx_frame_period << 16), 
                       MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);
    else
        /* 0 = 20 msecs default tx frame period */
        MacUsbWriteReg(HW_CFP_ATIM, (20 << 16), 
                       MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);
    
    /* enable beacon transmission */
    MacUsbWriteReg(HW_GEN_CONTROL, GEN_BEACEN, 
                           MAC_COMPLETE_MSG, MAC_OP_OR, (MacUsbMsg *)NULL);

}

/* Build a 1024/128 bytes frame. For WiWave, first word in the buffer is used for 
   a 4 bytes USB specific header. Shift everything by 4 bytes and adjust the 
   length upward */
void MacRadioTXRandomBuffer( unsigned int transmit_rate, unsigned int tx_frame_length)
{
    int i;

    /* TBD frame length of 128 bytes */
    //(void) tx_frame_length;

    switch (transmit_rate)
    {
    case 0:
        /* 1mpbs */
        
        /* modulation type = 0x00; no of ensuing 32bits words = 0x01 */
        radio_txbuffer[4] = 0x00;
        radio_txbuffer[5] = 0x01;        
        radio_txbuffer[6] = 0x00;
        radio_txbuffer[7] = 0x00;
        
        /*data rate identifier = 0x0a; length of 1,024 bytes frame in usecs = 0x2000 */
        radio_txbuffer[8] = 0x0a;        
        radio_txbuffer[9] = 0x04;

        if(!tx_frame_length)
	{
           radio_txbuffer[10] = 0x00;
           radio_txbuffer[11] = 0x20;
	}
	else
	{
           radio_txbuffer[10] = 0x00;
           radio_txbuffer[11] = 0x04;
        }
    
        break;

     case 1:
        /* 2mpbs */
        
        /* modulation type = 0x00; no of ensuing 32bits words = 0x01 */        
        radio_txbuffer[4] = 0x00;
        radio_txbuffer[5] = 0x01;        
        radio_txbuffer[6] = 0x00;
        radio_txbuffer[7] = 0x00;        
        
        /*data rate identifier = 0x14; length of 1,024 bytes frame in usecs = 0x1000 */        
        radio_txbuffer[8] = 0x14;
        radio_txbuffer[9] = 0x04;

        if(!tx_frame_length)
	{
          radio_txbuffer[10] = 0x00;
          radio_txbuffer[11] = 0x10;
	}
	else
	{
          radio_txbuffer[10] = 0x00;
          radio_txbuffer[11] = 0x02;
        }

        break;

    case 2:
        /* 5.5mpbs */
        
        /* modulation type = 0x00; no of ensuing 32bits words = 0x01 */
        radio_txbuffer[4] = 0x00;
        radio_txbuffer[5] = 0x01;        
        radio_txbuffer[6] = 0x00;
        radio_txbuffer[7] = 0x00;
        
        /*data rate identifier = 0x37; length of 1,024 bytes frame in usecs = 0x0661 */
        radio_txbuffer[8] = 0x37;
        radio_txbuffer[9] = 0x04;

        if(!tx_frame_length)
	{
          radio_txbuffer[10] = 0x61;
          radio_txbuffer[11] = 0x06;
	}
	else
	{
          radio_txbuffer[10] = 0xcc;
          radio_txbuffer[11] = 0x00;
	}

        break;

    case 3:
        /* 11mpbs */
        
        /* modulation type = 0x00; no of ensuing 32bits words = 0x01 */
        radio_txbuffer[4] = 0x00;
        radio_txbuffer[5] = 0x01;        
        radio_txbuffer[6] = 0x00;
        radio_txbuffer[7] = 0x00;
        
        /*data rate identifier = 0x6e; length of 1,024 bytes frame in usecs = 0x02e8 */        
        radio_txbuffer[8] = 0x6e;
        radio_txbuffer[9] = 0x04;

        if(!tx_frame_length)
	{
          radio_txbuffer[10] = 0xe8;
          radio_txbuffer[11] = 0x02;
	}
	else
	{
          radio_txbuffer[10] = 0x5d;
          radio_txbuffer[11] = 0x00;
	}

        break;

    /*OFDM */

    case 4:
        /* 6mpbs */
 
        /* modulation type = 0xEE; no of ensuing 32bits words = 0x01 */       
        radio_txbuffer[4] = 0xEE;        
        radio_txbuffer[5] = 0x01;
        radio_txbuffer[6] = 0x00;
        radio_txbuffer[7] = 0x00;         
 
        /*data rate identifier = 0x0B; length of 1,024 bytes frame in bytes = 0x80 */       
        radio_txbuffer[8] = 0x0B;

        if(!tx_frame_length)
          radio_txbuffer[9] = 0x80;   
	else
          radio_txbuffer[9] = 0x20;

        radio_txbuffer[10] = 0x00;
        radio_txbuffer[11] = 0x00;        

        break;

    case 5:
        /* 9mpbs */
        
       /* modulation type = 0xEE; no of ensuing 32bits words = 0x01 */       
        radio_txbuffer[4] = 0xEE;        
        radio_txbuffer[5] = 0x01;
        radio_txbuffer[6] = 0x00;
        radio_txbuffer[7] = 0x00;        
        
        /*data rate identifier = 0x0F; length of 1,024 bytes frame in bytes = 0x80 */        
        radio_txbuffer[8] = 0x0F;
        
        if(!tx_frame_length)
          radio_txbuffer[9] = 0x80;   
	else
          radio_txbuffer[9] = 0x20;

        radio_txbuffer[10] = 0x00;
        radio_txbuffer[11] = 0x00;        
        break;

    case 6:
        /* 12mpbs */
        
       /* modulation type = 0xEE; no of ensuing 32bits words = 0x01 */       
        radio_txbuffer[4] = 0xEE;        
        radio_txbuffer[5] = 0x01;
        radio_txbuffer[6] = 0x00;
        radio_txbuffer[7] = 0x00;        
        
        /*data rate identifier = 0x0A; length of 1,024 bytes frame in bytes = 0x80 */
        radio_txbuffer[8] = 0x0A;

        if(!tx_frame_length)
          radio_txbuffer[9] = 0x80;   
	else
          radio_txbuffer[9] = 0x20;

        radio_txbuffer[10] = 0x00;
        radio_txbuffer[11] = 0x00;        
        break;
        
    case 7:
        /* 18mpbs */
        
       /* modulation type = 0xEE; no of ensuing 32bits words = 0x01 */       
        radio_txbuffer[4] = 0xEE;        
        radio_txbuffer[5] = 0x01;
        radio_txbuffer[6] = 0x00;
        radio_txbuffer[7] = 0x00;
        
        /*data rate identifier = 0x0E; length of 1,024 bytes frame in bytes = 0x80 */
        radio_txbuffer[8] = 0x0E;

        if(!tx_frame_length)
          radio_txbuffer[9] = 0x80;   
	else
          radio_txbuffer[9] = 0x20;

        radio_txbuffer[10] = 0x00;
        radio_txbuffer[11] = 0x00;        
        break;
        
    case 8:
        /* 24mpbs */
        
       /* modulation type = 0xEE; no of ensuing 32bits words = 0x01 */       
        radio_txbuffer[4] = 0xEE;        
        radio_txbuffer[5] = 0x01;
        radio_txbuffer[6] = 0x00;
        radio_txbuffer[7] = 0x00;        

        /*data rate identifier = 0x09; length of 1,024 bytes frame in bytes = 0x80 */        
        radio_txbuffer[8] = 0x09;         

        if(!tx_frame_length)
          radio_txbuffer[9] = 0x80;   
	else
          radio_txbuffer[9] = 0x20;

        radio_txbuffer[10] = 0x00;
        radio_txbuffer[11] = 0x00;        
        break;                

    case 9:
        /* 36mpbs */
        
       /* modulation type = 0xEE; no of ensuing 32bits words = 0x01 */       
        radio_txbuffer[4] = 0xEE;        
        radio_txbuffer[5] = 0x01;
        radio_txbuffer[6] = 0x00;
        radio_txbuffer[7] = 0x00;        
        
        /*data rate identifier = 0x0D; length of 1,024 bytes frame in bytes = 0x80 */        
        radio_txbuffer[8] = 0x0d;        

        if(!tx_frame_length)
          radio_txbuffer[9] = 0x80;   
	else
          radio_txbuffer[9] = 0x20;

        radio_txbuffer[10] = 0x00;
        radio_txbuffer[11] = 0x00;        
        break;
        
    case 10:
        /* 48mpbs */
        
       /* modulation type = 0xEE; no of ensuing 32bits words = 0x01 */       
        radio_txbuffer[4] = 0xEE;        
        radio_txbuffer[5] = 0x01;
        radio_txbuffer[6] = 0x00;
        radio_txbuffer[7] = 0x00;        
        
        /*data rate identifier = 0x08; length of 1,024 bytes frame in bytes = 0x80 */
        radio_txbuffer[8] = 0x08;        

        if(!tx_frame_length)
          radio_txbuffer[9] = 0x80;   
	else
          radio_txbuffer[9] = 0x20;

        radio_txbuffer[10] = 0x00;
        radio_txbuffer[11] = 0x00;        
        break;
        
    case 11:
        /* 54mpbs */
        
       /* modulation type = 0xEE; no of ensuing 32bits words = 0x01 */       
        radio_txbuffer[4] = 0xEE;        
        radio_txbuffer[5] = 0x01;
        radio_txbuffer[6] = 0x00;
        radio_txbuffer[7] = 0x00;
        
        /*data rate identifier = 0x0C; length of 1,024 bytes frame in bytes = 0x80 */
        radio_txbuffer[8] = 0x0c;
       
        if(!tx_frame_length)
          radio_txbuffer[9] = 0x80;   
	else
          radio_txbuffer[9] = 0x20;

        radio_txbuffer[10] = 0x00;
        radio_txbuffer[11] = 0x00;        
        break;


    default:
    	
    	  /* modulation type = 0x00; no of ensuing 32bits words = 0x01 */        
        radio_txbuffer[4] = 0x00;
        radio_txbuffer[5] = 0x01;        
        radio_txbuffer[6] = 0x00;
        radio_txbuffer[7] = 0x00;
    	
       /*data rate identifier = 0x0a; length of 1,024 bytes frame in usecs = 0x2000 */    	
        radio_txbuffer[8] = 0x0a;
        radio_txbuffer[9] = 0x04;
        radio_txbuffer[10] = 0x00;
        radio_txbuffer[11] = 0x20;
       break;
    }

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

    /* copy 3 words of data to the transmit buffer, length in bytes! */
    HWWriteFifo (&radio_txbuffer, 8+4,TRUE);
}


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 */
        MacUsbWriteReg(HW_INTR_MASK, 0, 
                       MAC_COMPLETE_MSG, MAC_OP_WRITE, (MacUsbMsg *)NULL);
			  
        /* set ad-hoc mode of operation */
       MacUsbWriteReg(HW_MAC_CONTROL, CTRL_IBSS|CTRL_BEACONTX, 
                       MAC_COMPLETE_MSG, MAC_OP_OR, (MacUsbMsg *)NULL);
		  
    }
    else
    {
        MacUsbWriteReg(HW_MAC_CONTROL, ~(CTRL_IBSS|CTRL_BEACONTX), 
                       MAC_COMPLETE_MSG, MAC_OP_AND, (MacUsbMsg *)NULL);


        MacUsbWriteReg(HW_GEN_CONTROL, ~GEN_BEACEN, 
                           MAC_COMPLETE_MSG, MAC_OP_AND, (MacUsbMsg *)NULL);

        /* enable common mac interrupts */
        MacUsbWriteReg(HW_INTR_MASK,  
                       INTR_RXFIFO|INTR_TXEND|INTR_TIMEOUT|INTR_ABORT, 
                       MAC_COMPLETE_MSG, MAC_OP_OR, (MacUsbMsg *)NULL);

        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.  It is my
// hope that by disabling interrupts the FPGA will not do any automatic
// acking of received data.
//
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;

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

void MacWriteRF(uint8 addr, uint32 data)
{
   WriteRF (addr, data);
}

void MacReadRF(uint8 addr, uint32 *data)
{
  /* TBD */
   addr = addr;
  *data = *data;

#if 0
    uint8 data0;
    uint8 data1;
    uint8 data2;
    uint16 len = sizeof(uint8);

    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);
#endif

}

void MacWriteReg8(unsigned int offset, uint8 data)
{
    /* Not supported */
   //    REG8(offset) = data;

   (void) offset;
   (void) data;

}

uint8 MacReadReg8(unsigned int offset)
{
    /* Not supported */
   (void) offset;

#if 0
    uint8 data;

    data = REG8(offset);
    return data;
#endif
 
    return 0xFF;


}

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)
{

  MacUsbWriteReg((uint32)offset, (unsigned int)data, 
                   MAC_COMPLETE_MSG, 0, (MacUsbMsg *)NULL);
}


uint32 MacReadReg32(unsigned int offset)
{
    uint32 data = 0;
    
    if (MacUsbReadReg(offset, &data, sizeof data) != 0)
    {
        MAC_PRINTF(TRACE_CRITICAL, "MacUsbReadReg fails to read offset 0x%x\n", offset);
        return -1;
    }

    return data;
}



/*
 * Initialize a block of memory for a packet used to
 * send multiple messages in a single USB packet, in order to do this we need
 * to allocate a continous piece of memory.  This routine can not block and
 * may be called from an ISR context.
 *
 * Return NULL if no blocks are available.
 */
static MacUsbMsg *MacUsbInitMsg(void)
{
    MacUsbMsg    *msg_ptr;
    unsigned int ret;    

    /* 
     * Allocate a structure to keep track of the message ,this 
     * memory is freed when the message is sent
     */
    ret = tx_block_allocate(&usb_message_hdr_pool, (void **)&msg_ptr, TX_NO_WAIT);

    if (ret != TX_SUCCESS)
    {
       printf("MacUsbInitMsg no message headers\n");
       return (MacUsbMsg *)NULL;  
    } 
    memset(msg_ptr, 0, sizeof(MacUsbMsg)); 

    ret = tx_block_allocate(&usb_message_pool, (void **)&msg_ptr->start, TX_NO_WAIT);

    if (ret != TX_SUCCESS)
    {
       printf("MacUsbInitMsg no message headers\n");
       return (MacUsbMsg *)NULL;  
    } 

    return msg_ptr;
}

/*
 * Host to device write packets have the following format:
 * Bit 
 * 31:24    Packet ID
 * 23:22    (Operation, 0=MAC_OP_WRITE, 1=MAC_OP_AND, 2=MAC_OP_OR)
 * 21:15    Length MSB
 * 15:8     Length LSB
 * 7:0      ID^A5
 */
#define  WRITE_PACKET_ID  24
#define  WRITE_PACKET_OP  22 
#define  WRITE_PACKET_LEN 8 

/*
 * Routine used to write registers over USB, we try to fit as much into
 * one trasfer so we use a flag to indicate whether the message is
 * complete, in between or the end.
 */
int MacUsbWriteReg(uint32 reg, unsigned int data, 
               MacUsbMsgFlag flag, MacUsbOp op, MacUsbMsg *msg_ptr)
{
    int    bytesTransferred, errorCode, ret=0, len=2; 
    uint32 packet[2];
    
    if (flag == MAC_COMPLETE_MSG)
    {
        // We have a complete message
        if (msg_ptr == (MacUsbMsg *)NULL)
        {
            // This is not a multi-message packet
            packet[0] = (reg << WRITE_PACKET_ID)  |
                        (op  << WRITE_PACKET_OP)  |
                        (len << WRITE_PACKET_LEN) |
                        (reg^0x5A); 
                        
            packet[1] = data;
            usb_wifi_write((unsigned char *)&packet[0], 
                           sizeof packet, &bytesTransferred, 
                           &errorCode);
        }
        else
        {
            /* 
             * This is the end of multi-part message, add this message
             * to the end and send it out.
             */ 
             unsigned long *ptr_ulong;
             
             msg_ptr->len += len;
             
             ptr_ulong = msg_ptr->start + msg_ptr->offset;
                                         
             *ptr_ulong++ = (reg << WRITE_PACKET_ID)  |
                            (op  << WRITE_PACKET_OP)  |
                            (len << WRITE_PACKET_LEN) |
                            (reg^0x5A); 
             *ptr_ulong = (unsigned long)data;

            // The 'len' field is in 4 byte words. 
            usb_wifi_write((unsigned char *)msg_ptr->start, 
                           msg_ptr->len*4, &bytesTransferred, 
                           &errorCode);
                           
            ret = tx_block_release(msg_ptr->start);
            if (ret != TX_SUCCESS)
            {
                panic("tx_block_release failed1");
		        return -1;
            }

            ret = tx_block_release(msg_ptr);
            if (ret != TX_SUCCESS)
            {
                panic("tx_block_release failed");
		        return -1;
            }
         }
    
    }
    else if (((flag == MAC_START_MSG) || (flag == MAC_CONTINUE_MSG)) &&
             (msg_ptr != (MacUsbMsg *)NULL))
    {
        // Continiuation of an existing message, append to the end
        unsigned long *ptr_ulong;
             
        msg_ptr->len += len;
             
        ptr_ulong = msg_ptr->start + msg_ptr->offset; 
                                         
        msg_ptr->offset += len;
                                         
        *ptr_ulong++ = (reg << WRITE_PACKET_ID)  |
                       (op  << WRITE_PACKET_OP)  |
                       (len << WRITE_PACKET_LEN) |
                       (reg^0x5A); 
        *ptr_ulong = (unsigned long)data;
    }

    return ret;
}

// Add four bytes for the frame header word and four bytes for the packet header
#define MAC_DATA_OFFSET 8


/*
 * Routine used to write data over USB.
 */
static int MacUsbWriteData(uint8 reg, uint16 len, void *data)
{
    int           bytesTransferred, errorCode; 
    unsigned int *packet, ret=0;
    char         *buffer;

    // If no data, just return
    if (len == 0) 
    {
        return -1;
    }

    ret = tx_block_allocate(&usb_message_pool, (void **)&buffer, TX_NO_WAIT);
    if (ret != TX_SUCCESS)
    {
        panic("MacUsbMsg * tx_block_allocate failed");
        return -1;
    }

    memcpy(buffer+4, data, len);


    // len needs to be converted to a multiple of 32 bit words

    while (len % 4) 
    { 
       len++;
    }

    packet = (unsigned int *)buffer;

    len /= 4;  // convert to 32 bit words
    
    len += 1; // Add in the size of the packet header (one word)

    /* First send out the packet header */
    *packet++ = (reg << WRITE_PACKET_ID)  |
                (len << WRITE_PACKET_LEN) |
                (reg^0x5A); 
              
    ret = usb_wifi_write((unsigned char *)buffer, 
                         len*4, &bytesTransferred, 
                         &errorCode);
                   
    ret = tx_block_release(buffer);
    if (ret != TX_SUCCESS)
    {
       MAC_PRINTF(TRACE_CRITICAL, "tx_block_release failed [%d]\n", ret);
	   return -1;
    }

    return ret;
}

/*
 * Routine used to write data to a fifo over USB.
 * ***NOTE: The first four bytes of whats passed into this
 * routine is OVERWRITTEN with the packet header.
 * Pad the data pointer with four extra bytes.***
 *
 * Set block to one for a blocking write
 *
 */
static int MacUsbWriteFifoData(uint8 reg, uint16 len, void *data, int block)
{
    int bytesTransferred, errorCode, ret=0; 
    unsigned int *packet;


    // len needs to be converted to a multiple of 32 bit words
    while (len % 4) 
    { 
       len++;
    }

    packet = (unsigned int *)data;  

    len /= 4; // Convert to 32-bit words
    
    /* Fill in the packet header */
    *packet = (reg << WRITE_PACKET_ID)  |
             (len << WRITE_PACKET_LEN) |
             (reg^0x5A); 
    if (block)          
    {
        ret = usb_wifi_write((unsigned char *)packet, 
                             len*4, &bytesTransferred, 
                             &errorCode);
    }
    else
    {
        ret = usb_wifi_write_async((unsigned char *)packet, 
                                   len*4, &bytesTransferred, 
                                   &errorCode, NULL, NULL);
    }
                   
    return ret;
}

typedef struct mac_usb_read_request_buffer_t {
    unsigned long                        request[2];    /* read request */
    char                                 *data;         /* pointer to user's data buffer */
    int                                  data_offset;   /* number of bytes offset when response copied to the data buffer */
    int                                  length;        /* request length in byte */
    int                                  read_length;   /* actual read length from the response */
    int                                  in_used;
    TX_EVENT_FLAGS_GROUP                 event;         /* event flag */
    struct mac_usb_read_request_buffer_t *next;
} macUsbReadReqBuffer_t;

/* available buffer for read packet */
static macUsbReadReqBuffer_t * gMacUsbReadReqBuffer = NULL;
/* active read packet waiting for response */
static macUsbReadReqBuffer_t * gMacUsbReadReqFirst = NULL;
static TX_MUTEX *gMacUsbReadReqMutex = NULL;

#define MAC_USB_LOCK      tx_mutex_get(gMacUsbReadReqMutex, TX_WAIT_FOREVER)
#define MAC_USB_UNLOCK    tx_mutex_put(gMacUsbReadReqMutex)

/* number of buffer available */
#define MAC_USB_READ_REQ_BUFFER_MAX    1

/* event */
#define MAC_USB_READ_REQ_RESPONSE_DONE 0x01

/*
 * Initialize memory for read register.
 * It mallocates memory for read register buffer and mutex for accessing 
 * read register related functions.
 */
static int mac_usb_read_req_init(void)
{
    int ret;
    int i, size;
    
    macUsbReadReqBuffer_t * pReadReq;

    // Initialize mutex lock
    if (gMacUsbReadReqMutex == NULL)
    {
        gMacUsbReadReqMutex = (TX_MUTEX *)malloc(sizeof(TX_MUTEX));
        if (gMacUsbReadReqMutex == NULL)
        {
            MAC_PRINTF(TRACE_CRITICAL, "mac_usb_read_req_init: malloc mutex fails\n");
            ret = -1; 
            goto _ret;
        }
        memset(gMacUsbReadReqMutex, 0, sizeof(TX_MUTEX)); 
        ret = tx_mutex_create(gMacUsbReadReqMutex, "Mac USB Read Request", TX_INHERIT);
        if (ret != TX_SUCCESS)
        {
            MAC_PRINTF(TRACE_CRITICAL, "mac_usb_read_req_init: tx_mutex_create fails %d\n", ret);
            ret = -1; 
            goto _ret;
        }
    }
    
    // Initialize read req buffers
    if (gMacUsbReadReqBuffer == NULL)
    {
	    size = sizeof(macUsbReadReqBuffer_t) * MAC_USB_READ_REQ_BUFFER_MAX;
	
        gMacUsbReadReqBuffer = (macUsbReadReqBuffer_t *)malloc(size);
        if (gMacUsbReadReqBuffer == NULL)
        {
	        MAC_PRINTF(TRACE_CRITICAL, "mac_usb_read_req_init: malloc read request buffer fails\n");
            ret = -1;
            goto _ret;
        }

        for (i=0; i < MAC_USB_READ_REQ_BUFFER_MAX; i++)
        {
            pReadReq = &gMacUsbReadReqBuffer[i];
            pReadReq->in_used = 0;
            pReadReq->next = NULL;
	    
	        // Create an event. This event is used when response is received.
	        memset(&pReadReq->event, 0, sizeof(TX_EVENT_FLAGS_GROUP));
	        ret = tx_event_flags_create(&pReadReq->event, "USB Read Req Packet");
	        if (ret != TX_SUCCESS)
	        {
                MAC_PRINTF(TRACE_CRITICAL, "mac_usb_read_req_init: tx_event_flags_create fails %d\n", ret);
		        ret = -1;
		        goto _ret;
	        }
        }

    }
    
    ret = 0;
 
_ret:
    if (ret == -1)
    {
        if (gMacUsbReadReqMutex != NULL)
        {
            tx_mutex_delete(gMacUsbReadReqMutex);
            free(gMacUsbReadReqMutex);
            gMacUsbReadReqMutex = NULL;
        }
        if (gMacUsbReadReqBuffer != NULL)
        {
            free(gMacUsbReadReqBuffer);
	    gMacUsbReadReqBuffer = NULL;
	}
    }
    return ret;
}

                                                                      	    
/*
 * Format read register request and send it to the device.
 * This function returns packet buffer. 
 * Error if returns 0.
 */
static unsigned long mac_usb_read_req_packet_send(uint8 reg, void * data, uint16 read_len)
{
    unsigned long packet = 0;
    int sendLength, bytesTransferred, errorCode;
    int ret, i;
    macUsbReadReqBuffer_t * pReadReq, * pActiveReq;
    uint16 len;
	
    if (gMacUsbReadReqBuffer == NULL || data == NULL || read_len == 0) return 0;
    
    MAC_USB_LOCK;

    // Get read request buffer and put in the the available list.
    for (i=0; i < MAC_USB_READ_REQ_BUFFER_MAX; i++)
    {
        pReadReq = &gMacUsbReadReqBuffer[i];
        if (pReadReq->in_used == 0)
	    {

    	    pReadReq->in_used = 1;
	        // put in the active list. 
    	    // When response is recived, the response will go to the 1st entry.
	        if (gMacUsbReadReqFirst == NULL)
	        {
                    gMacUsbReadReqFirst = pReadReq; /* only one request */
	        }
            else
	        {
		        // put the request at the end of active list
                for (pActiveReq = gMacUsbReadReqFirst; pActiveReq != NULL; pActiveReq = pActiveReq->next)
		        {
                    if (pActiveReq->next == NULL)
		            {
                        pActiveReq->next = pReadReq;
                        break;
		            }
		        }
            }
	        pReadReq->next = NULL;

            /* convert len into number of 4-byte value.
             * read extra 4-byte value
             */
            len = read_len/4;
            if ((read_len % 4) != 0) len++;

	        // format the READ REQUEST PACKET
            pReadReq->request[0] = (0xFD << WRITE_PACKET_ID) |
                                   (2 << WRITE_PACKET_LEN) | 0xA7;
	    
            pReadReq->request[1] = (reg << WRITE_PACKET_ID) | (len << 16);

            pReadReq->data  = (char *)data;
            pReadReq->length  = read_len;

            sendLength = sizeof pReadReq->request;

	        /* send packet to the device */
            ret = usb_wifi_write((unsigned char *)&pReadReq->request[0], 
                                    sendLength, &bytesTransferred, 
                                    &errorCode);

            if (ret == 0)
	        {   /* send successfully */
                packet = (unsigned long)pReadReq;
	        }
            else
            {
		        /* removed the request from the active list */
		        for (pActiveReq = gMacUsbReadReqFirst; pActiveReq != NULL; pActiveReq = pActiveReq->next)
		        {
        	        if (gMacUsbReadReqFirst == pReadReq)
                    {
                        gMacUsbReadReqFirst = pReadReq;
                    }
                    else if (pActiveReq->next == pReadReq)
		            {
                        pActiveReq = pReadReq->next;
		            }
		        }
    	        pReadReq->in_used = 0;
            }
	        break;
        }
    }
    MAC_USB_UNLOCK;

    return packet;
}
		
/*
 * Wait for read packet response.
 * This function returns 0 if response is successfully received.
 * It returns 1 if it times out.
 * It returns -1 if error is encountered.
 */
static int mac_usb_read_req_receive(unsigned long packet, unsigned long wait_option)
{
    int ret = -1;
    unsigned long         actual_events;
    macUsbReadReqBuffer_t * pReadReq, * pActiveReq;

 
    pReadReq = (macUsbReadReqBuffer_t *)packet;
    if (pReadReq != NULL)
    {
        ret = tx_event_flags_get(&pReadReq->event, MAC_USB_READ_REQ_RESPONSE_DONE, TX_OR_CLEAR, &actual_events, wait_option);
	
        pReadReq->in_used = 0;
        pReadReq->read_length = 0;
        pReadReq->length = 0;
        pReadReq->data = NULL;
        pReadReq->request[0] = 0;
        pReadReq->request[1] = 0;

        MAC_USB_LOCK;

        // Remove request from active list in case of an error
        if (gMacUsbReadReqFirst == pReadReq)
            gMacUsbReadReqFirst = gMacUsbReadReqFirst->next;
        else
            for (pActiveReq = gMacUsbReadReqFirst; pActiveReq != NULL; pActiveReq = pActiveReq->next)
                if (pActiveReq->next == pReadReq)
                {
                    pActiveReq->next = pReadReq->next;
                    break;
                }
        
        MAC_USB_UNLOCK;

        if (ret == TX_SUCCESS && (actual_events & MAC_USB_READ_REQ_RESPONSE_DONE))
        {
	        ret = 0;
	    }
	    else if (ret == TX_NO_EVENTS)
	    {
            MAC_PRINTF(TRACE_DEBUG, "mac_usb_read_req_receive: no event\n");
            ret = 1;
	    }
	    else 
        {
	        MAC_PRINTF(TRACE_CRITICAL, "mac_usb_read_req_receive: tx_event_flags_get fails %d\n", ret);
	        ret = -1;
	        goto _ret;
        }
    }

_ret:
    return ret;
}    

/* 
 * This function should be called by WIFI driver when read response is received.
 */        
int MacUsbReadReqHandleResponse(unsigned long *read_value, unsigned int read_length)
{
    int ret = -1;
    int length;

    macUsbReadReqBuffer_t * pReadReq;
    

    if (gMacUsbReadReqBuffer == NULL)
    {
        goto _ret;
    }
    
    /* Get the 1st read request. */
    pReadReq = gMacUsbReadReqFirst; 
    if (pReadReq != NULL)
    {
        MAC_USB_LOCK;
	
        gMacUsbReadReqFirst = gMacUsbReadReqFirst->next;
        length = read_length * 4; /* number of 32bit */

        if (length > pReadReq->length)
            length = pReadReq->length;

        /* copy the response into user's buffer */    
        memcpy(pReadReq->data, read_value, length);

        pReadReq->read_length = read_length * 4;
	
        MAC_USB_UNLOCK;
	
        ret = tx_event_flags_set(&pReadReq->event, MAC_USB_READ_REQ_RESPONSE_DONE, TX_OR);

	    if (ret != TX_SUCCESS)
        {
            pReadReq->in_used = 0;
            MAC_PRINTF(TRACE_CRITICAL, "MacUsbReadReqHandleResponse: tx_event_flags_set fails %d\n", ret);
            ret = -1;
        }
        ret = 0;
    }

_ret:

    return ret;
}    
	
/*
 * Routine used to read data to a fifo over USB.
 */
int MacUsbReadReg(uint8 reg, void *data, uint16 len)
{
    unsigned long packet;
    int ret = -1; 


    packet = mac_usb_read_req_packet_send(reg, data, len);

    if (packet != 0)
    {
    	/* receive read request response */
        ret = mac_usb_read_req_receive(packet, NS_MILLISECONDS_TO_TICKS(MAC_USB_READ_TIMEOUT_MS));
    }

    return ret;
}

/**
 * This function writes given data to EEPROM
 *
 * @external
 * @include "mac_api.h"
 * @category 
 * @since 7.2
 *
 * @param location  Location address of the EEPROM to be written
 * @param data      Pointer to data to be written to the EEPROM
 * @param length    Length of the data in bytes
 *
 * @return  -1      Error
 * @return  0       Data was successfully written to the EEPROM
 * 
**/
int MacUsbWriteEEPROM(unsigned int location, char *data, uint16 length)
{
#define _MAC_4_BYTE_WRITE   4

    int     ret = 0, i, j;
    unsigned int startAddr, endAddr;
    unsigned int rValue, dataValue;
    unsigned int *dataPtr;
    char        *destPtr, *srcPtr;

    uint8   sRemainBytes, eRemainBytes;
    uint16  rLength;

    if (data == NULL || length == 0)
    {
        MAC_PRINTF(TRACE_WARNING, "MacUsbWriteEEPROM: invalid given parameters\n");
        ret = -1;
        goto _ret;
    }

    /* if the eeprom location is not in 4-byte boundry,
     * we need to calculate number of bytes that we
     * need to skip.
     */
    sRemainBytes = (location % _MAC_4_BYTE_WRITE);
    eRemainBytes = _MAC_4_BYTE_WRITE;

    /* get the eeprom location */        
    startAddr = location - sRemainBytes;
    endAddr = location + length;

    for (i=0; startAddr < endAddr; startAddr += _MAC_4_BYTE_WRITE)
    {
        /* get user's data */
        dataPtr = (unsigned int *)&dataValue;

        destPtr = (char *)&dataValue;
        srcPtr = (char *)(data + i);

        for (j=0; j < (eRemainBytes - sRemainBytes); j++)
        {
            *(destPtr+j) = *(srcPtr+j);
        }

        /* Write EEPROM Control for EEPROM location */
        if (MacUsbWriteReg(HW_EEPROM_CONTROL, startAddr, MAC_COMPLETE_MSG, MAC_OP_WRITE, NULL) != 0)
        {
            MAC_PRINTF(TRACE_CRITICAL, "MacUsbWriteEEPROM: MacUsbWriteReg fails to write EEPROM CONTROL\n");
            ret = -1;
            goto _ret;
        }

        tx_thread_sleep(NS_MILLISECONDS_TO_TICKS(MAC_USB_EEPROM_ACCESS_MS));

        if ((endAddr - startAddr) < _MAC_4_BYTE_WRITE)
        {
            sRemainBytes = 0;
            eRemainBytes = (uint8)(endAddr - startAddr);
        }

        if ((eRemainBytes - sRemainBytes) < _MAC_4_BYTE_WRITE)
        {
            rLength = sizeof rValue;
            if (MacUsbReadReg(HW_EEPROM_DATA, (void *)&rValue, rLength) != 0)
            {
                MAC_PRINTF(TRACE_CRITICAL, "MacUsbWriteEEPROM: MacUsbWriteReg fails to Read EEPROM Data\n");
                ret = -1;
                goto _ret;
            }

            /* skip the bytes */
            destPtr = (char *)&rValue;
            srcPtr = (char *)dataPtr;

            /* copy the remain bytes */
            for (j=sRemainBytes; j<eRemainBytes; j++)
            {
                *(destPtr+j) = *srcPtr;
                srcPtr++;
            }
            dataPtr = &rValue;

            i += (eRemainBytes - sRemainBytes);

            sRemainBytes = 0;
            eRemainBytes = _MAC_4_BYTE_WRITE;
        }
        else
        {
            i += _MAC_4_BYTE_WRITE;
        }

        /* Write EEPROM Data for actual data */
        if (MacUsbWriteReg(HW_EEPROM_DATA, *dataPtr, MAC_COMPLETE_MSG, MAC_OP_WRITE, NULL) != 0)
        {
            MAC_PRINTF(TRACE_CRITICAL, "MacUsbWriteEEPROM: MacUsbWriteReg fails to write EEPROM Data\n");
            ret = -1;
            goto _ret;
        }

        tx_thread_sleep(NS_MILLISECONDS_TO_TICKS(MAC_USB_EEPROM_ACCESS_MS));

    }

_ret:
    return ret;
}


/**
 * This function reads data from EEPROM
 *
 * @external
 * @include "mac_api.h"
 * @category 
 * @since 7.2
 *
 * @param location  Location address of the EEPROM to be read
 * @param data      Pointer to memory where data will be written to
 * @param length    Size of the data in bytes
 *
 * @return  -1      Error
 * @return  0       Data was successfully obtained from the EEPROM
**/
int MacUsbReadEEPROM(unsigned int location, char *data, uint16 length)
{
    int ret = 0;
    unsigned int eepromAddr;

    if (data == NULL || length == 0)
    {
        MAC_PRINTF(TRACE_WARNING, "MacUsbWriteEEPROM: invalid given parameters\n");
        ret = -1;
        goto _ret;
    }

    /* Write EEPROM Control for EEPROM location */
    eepromAddr = location;

    if (MacUsbWriteReg(HW_EEPROM_CONTROL, eepromAddr, MAC_COMPLETE_MSG, MAC_OP_WRITE, NULL) != 0)
    {
        MAC_PRINTF(TRACE_CRITICAL, "MacUsbReadEEPROM fails to write EEPROM Control\n");
        ret = -1;
        goto _ret;
    }

    tx_thread_sleep(NS_MILLISECONDS_TO_TICKS(MAC_USB_EEPROM_ACCESS_MS));

    /* Read EEPROM Data */
    if (MacUsbReadReg(HW_EEPROM_DATA, (void *)data, length) != 0)
    {
        MAC_PRINTF(TRACE_CRITICAL, "MacUsbReadEEPROM fails to read EEPROM Data\n");
        ret = -1;
        goto _ret;
    }

_ret:
    return ret;
}

// Load a key to the WiWave 
void SetAESKey(int id, AES_KEY *key)
{
    unsigned long val;
//    unsigned int  len = sizeof(AES_KEY);
#ifdef NETOS_GNU_TOOLS
    static uint8 buf[512]  __attribute__ ((aligned(32)));
#else
#pragma alignvar (32)
    static uint8 buf[512];
#endif

    if (hwAES)
    {
        // Enable HW AES decryption
        // HW_MAC_CONTROL &= ~CTRL_AES_DISABLE;
        MacUsbWriteReg(HW_MAC_CONTROL, ~CTRL_AES_DISABLE,
                           MAC_COMPLETE_MSG, MAC_OP_AND, (MacUsbMsg *)NULL);

        // Select mode 1, load AES key
        val = AES_MODE_BIT | AES_KEY_LOAD_BIT | id;

        MacUsbWriteReg(HW_AES_CONTROL, val, 
                      MAC_COMPLETE_MSG, MAC_OP_WRITE,
                      (MacUsbMsg *)NULL);

        // Write plaintext key, IV and data to HW_AES_FIFO_END (0x30) 
        memcpy(&buf[16], key, 4*44+4);
        HWWriteAESFifo(&buf[16], 4*44+4, 1); 
    }
}
#endif 
