/*******************************************************************************
 * Agere Systems Inc.
 * Wireless device driver for Linux (wlags49).
 *
 * Copyright (c) 1998-2003 Agere Systems Inc.
 * All rights reserved.
 *   http://www.agere.com
 *
 * Initially developed by TriplePoint, Inc.
 *   http://www.triplepoint.com
 *
 *------------------------------------------------------------------------------
 *
 *   This file defines misc utility functions.
 *
 *------------------------------------------------------------------------------
 *
 * SOFTWARE LICENSE
 *
 * This software is provided subject to the following terms and conditions,
 * which you should read carefully before using the software.  Using this
 * software indicates your acceptance of these terms and conditions.  If you do
 * not agree with these terms and conditions, do not use the software.
 *
 * Copyright © 2003 Agere Systems Inc.
 * All rights reserved.
 *
 * Redistribution and use in source or binary forms, with or without
 * modifications, are permitted provided that the following conditions are met:
 *
 * . Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following Disclaimer as comments in the code as
 *    well as in the documentation and/or other materials provided with the
 *    distribution.
 *
 * . Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following Disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * . Neither the name of Agere Systems Inc. nor the names of the contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * Disclaimer
 *
 * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
 * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
 * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 ******************************************************************************/

/*******************************************************************************
 *  include files
 ******************************************************************************/
#include <wl_version.h>

#include <linux/kernel.h>
// #include <linux/sched.h>
// #include <linux/ptrace.h>
#include <linux/ctype.h>
// #include <linux/string.h>
// #include <linux/timer.h>
// #include <linux/interrupt.h>
// #include <linux/in.h>
// #include <linux/delay.h>
// #include <asm/io.h>
// #include <asm/system.h>
// #include <asm/bitops.h>

#include <linux/netdevice.h>
#include <linux/etherdevice.h>
// #include <linux/skbuff.h>
// #include <linux/if_arp.h>
// #include <linux/ioport.h>

#include <debug.h>
#include <hcf.h>
// #include <hcfdef.h>

#include <wl_if.h>
#include <wl_internal.h>
#include <wl_util.h>
#include <wl_wext.h>
#include <wl_main.h>



/*******************************************************************************
 * global variables
 ******************************************************************************/

/* A matrix which maps channels to frequencies */
#define MAX_CHAN_FREQ_MAP_ENTRIES   50
static const long chan_freq_list[][MAX_CHAN_FREQ_MAP_ENTRIES] =
{
    {1,2412},
    {2,2417},
    {3,2422},
    {4,2427},
    {5,2432},
    {6,2437},
    {7,2442},
    {8,2447},
    {9,2452},
    {10,2457},
    {11,2462},
    {12,2467},
    {13,2472},
    {14,2484},
    {36,5180},
    {40,5200},
    {44,5220},
    {48,5240},
    {52,5260},
    {56,5280},
    {60,5300},
    {64,5320},
    {149,5745},
    {153,5765},
    {157,5785},
    {161,5805}
};

#if DBG
extern dbg_info_t *DbgInfo;
#endif  /* DBG */




/*******************************************************************************
 *	dbm()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Return an energy value in dBm.
 *
 *  PARAMETERS:
 *
 *      value - the energy value to be converted
 *
 *  RETURNS:
 *
 *      the value in dBm
 *
 ******************************************************************************/
int dbm( int value )
{
    /* Truncate the value to be between min and max. */
    if( value < HCF_MIN_SIGNAL_LEVEL )
        value = HCF_MIN_SIGNAL_LEVEL;

    if( value > HCF_MAX_SIGNAL_LEVEL )
        value = HCF_MAX_SIGNAL_LEVEL;

    /* Return the energy value in dBm. */
    return ( value - HCF_0DBM_OFFSET );
} // dbm
/*============================================================================*/




/*******************************************************************************
 *	percent()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Return a value as a percentage of min to max.
 *
 *  PARAMETERS:
 *
 *      value   - the value in question
 *      min     - the minimum range value
 *      max     - the maximum range value
 *
 *  RETURNS:
 *
 *      the percentage value
 *
 ******************************************************************************/
int percent( int value, int min, int max )
{
    /* Truncate the value to be between min and max. */
    if( value < min )
        value = min;

    if( value > max )
        value = max;

    /* Return the value as a percentage of min to max. */
    return ((( value - min ) * 100 ) / ( max - min ));
} // percent
/*============================================================================*/




/*******************************************************************************
 *	is_valid_key_string()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Checks to determine if the WEP key string is valid
 *
 *  PARAMETERS:
 *
 *      s - the string in question
 *
 *  RETURNS:
 *
 *      non-zero if the string contains a valid key
 *
 ******************************************************************************/
int is_valid_key_string( char *s )
{
    int l;
    int i;
    /*------------------------------------------------------------------------*/


    l = strlen( s );

    /* 0x followed by 5 or 13 hexadecimal digit pairs is valid */
    if( s[0] == '0' && ( s[1] == 'x' || s[1] == 'X' )) {
        if( l == 12 || l == 28 ) {
            for( i = 2; i < l; i++ ) {
                if( !isxdigit( s[i] ))
                    return 0;
            }

            return 1;
        } else {
            return 0;
        }
    }

    /* string with 0, 5, or 13 characters is valid */
    else
    {
        return( l == 0 || l == 5 || l == 13 );
    }
} // is_valid_key_string
/*============================================================================*/




/*******************************************************************************
 *	hexdigit2int()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Converts a hexadecimal digit character to an integer
 *
 *  PARAMETERS:
 *
 *      c   - the hexadecimal digit character
 *
 *  RETURNS:
 *
 *      the converted integer
 *
 ******************************************************************************/
int hexdigit2int( char c )
{
   if( c >= '0' && c <= '9' )
       return c - '0';

   if( c >= 'A' && c <= 'F' )
       return c - 'A' + 10;

   if( c >= 'a' && c <= 'f' )
       return c - 'a' + 10;

   return 0;
} // hexdigit2int
/*============================================================================*/




/*******************************************************************************
 *	key_string2key()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Converts a key_string to a key, Assumes the key_string is validated with
 *  is_valid_key_string().
 *
 *  PARAMETERS:
 *
 *      ks  - the valid key string
 *      key - a pointer to a KEY_STRUCT where the converted key information will
 *            be stored.
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void key_string2key( char *ks, KEY_STRCT *key )
{
    int l,i,n;
    char *p;
    /*------------------------------------------------------------------------*/


    l = strlen( ks );

    /* 0x followed by hexadecimal digit pairs */
    if( ks[0] == '0' && ( ks[1] == 'x' || ks[1] == 'X' )) {
        n = 0;
        p = (char *)key->key;

        for( i = 2; i < l; i+=2 ) {
           *p++ = ( hexdigit2int( ks[i] ) << 4 ) + hexdigit2int (ks[i+1] );
           n++;
        }

        /* Note that endian translation of the length field is not needed here
          because it's performed in wl_put_ltv() */
        key->len = n;
    }
    /* character string */
    else
    {
        strcpy( (char *)key->key, ks );
        key->len = l;
    }

    return;
} // key_string2key
/*============================================================================*/




#if DBG
/*******************************************************************************
 *	DbgHwAddr()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Convert a hardware ethernet address to a character string
 *
 *  PARAMETERS:
 *
 *      hwAddr  - an ethernet address
 *
 *  RETURNS:
 *
 *      a pointer to a string representing the ethernet address
 *
 ******************************************************************************/
const char *DbgHwAddr(unsigned char *hwAddr)
{
    static char     buffer[18];
    /*------------------------------------------------------------------------*/


    sprintf( buffer, "%02X:%02X:%02X:%02X:%02X:%02X",
             hwAddr[0], hwAddr[1], hwAddr[2], hwAddr[3], hwAddr[4], hwAddr[5] );

    return buffer;
} // DbgHwAddr
/*============================================================================*/

#endif /* DBG */




/*******************************************************************************
 *	wl_has_wep()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Checks to see if the device supports WEP
 *
 *  PARAMETERS:
 *
 *      ifbp    - the IFB pointer of the device in question
 *
 *  RETURNS:
 *
 *      1 if WEP is known enabled, else 0
 *
 ******************************************************************************/
int wl_has_wep (IFBP ifbp)
{
    CFG_PRIVACY_OPT_IMPLEMENTED_STRCT ltv;
	int rc, privacy;
    /*------------------------------------------------------------------------*/


	/* This function allows us to distiguish bronze cards from other types, to
       know if WEP exists. Does not distinguish (because there's no way to)
       between silver and gold cards. */
    ltv.len = 2;
    ltv.typ = CFG_PRIVACY_OPT_IMPLEMENTED;

	rc = hcf_get_info( ifbp, (LTVP) &ltv );

	privacy = CNV_LITTLE_TO_INT( ltv.privacy_opt_implemented );

	//return rc ? 0 : privacy;
    return 1;
} // wl_has_wep
/*============================================================================*/




/*******************************************************************************
 *	wl_hcf_error()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Report the type of HCF error message
 *
 *  PARAMETERS:
 *
 *      none
 *
 *  RETURNS:
 *
 *      A descriptive string indicating the error, quiet otherwise.
 *
 ******************************************************************************/
void wl_hcf_error( struct net_device *dev, int hcfStatus )
{
    char     buffer[64], *pMsg;
    /*------------------------------------------------------------------------*/


    if( hcfStatus != HCF_SUCCESS ) {
        switch( hcfStatus ) {

        case HCF_ERR_TIME_OUT:

            pMsg = "Expected adapter event did not occur in expected time";
            break;


        case HCF_ERR_NO_NIC:

            pMsg = "Card not found (ejected unexpectedly)";
            break;


        case HCF_ERR_LEN:

            pMsg = "Command buffer size insufficient";
            break;


        case HCF_ERR_INCOMP_PRI:

            pMsg = "Primary functions are not compatible";
            break;


        case HCF_ERR_INCOMP_FW:

            pMsg = "Primary functions are compatible, "
                "station/ap functions are not";
            break;


        case HCF_ERR_BUSY:

            pMsg = "Inquire cmd while another Inquire in progress";
            break;


        //case HCF_ERR_SEQ_BUG:

        //    pMsg = "Unexpected command completed";
        //    break;


        case HCF_ERR_DEFUNCT_AUX:

            pMsg = "Timeout on ack for enable/disable of AUX registers";
            break;


        case HCF_ERR_DEFUNCT_TIMER:
            pMsg = "Timeout on timer calibration during initialization process";
            break;


        case HCF_ERR_DEFUNCT_TIME_OUT:
            pMsg = "Timeout on Busy bit drop during BAP setup";
            break;


        case HCF_ERR_DEFUNCT_CMD_SEQ:
            pMsg = "Hermes and HCF are out of sync";
            break;


        default:

            sprintf( buffer, "Error code %d", hcfStatus );
            pMsg = buffer;
            break;
        }

        printk( KERN_INFO "%s: Wireless, HCF failure: \"%s\"\n",
                dev->name, pMsg );
    }
} // wl_hcf_error
/*============================================================================*/




/*******************************************************************************
 *	wl_endian_translate_event()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Determines what type of data is in the mailbox and performs the proper
 *  endian translation.
 *
 *  PARAMETERS:
 *
 *      pLtv - an LTV pointer
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_endian_translate_event( ltv_t *pLtv )
{
    DBG_FUNC( "wl_endian_translate_event" );
    DBG_ENTER( DbgInfo );


    switch( pLtv->typ ) {
    case CFG_TALLIES:
        break;


    case CFG_SCAN:
        {
            int numAPs;
            SCAN_RS_STRCT *pAps = (SCAN_RS_STRCT*)&pLtv->u.u8[0];

            numAPs = (hcf_16)(( (size_t)( pLtv->len - 1 ) * 2 ) /
                                (sizeof( SCAN_RS_STRCT )));

            while( numAPs >= 1 ) {
                numAPs--;

                pAps[numAPs].channel_id           =
                    CNV_LITTLE_TO_INT( pAps[numAPs].channel_id );

                pAps[numAPs].noise_level          =
                    CNV_LITTLE_TO_INT( pAps[numAPs].noise_level );

                pAps[numAPs].signal_level         =
                    CNV_LITTLE_TO_INT( pAps[numAPs].signal_level );

                pAps[numAPs].beacon_interval_time =
                    CNV_LITTLE_TO_INT( pAps[numAPs].beacon_interval_time );

                pAps[numAPs].capability           =
                    CNV_LITTLE_TO_INT( pAps[numAPs].capability );

                pAps[numAPs].ssid_len             =
                    CNV_LITTLE_TO_INT( pAps[numAPs].ssid_len );

                pAps[numAPs].ssid_val[pAps[numAPs].ssid_len] = 0;

            }
        }
        break;


    case CFG_ACS_SCAN:
        {
            PROBE_RESP *probe_resp = (PROBE_RESP *)pLtv;

            probe_resp->frameControl   = CNV_LITTLE_TO_INT( probe_resp->frameControl );
            probe_resp->durID          = CNV_LITTLE_TO_INT( probe_resp->durID );
            probe_resp->sequence       = CNV_LITTLE_TO_INT( probe_resp->sequence );
            probe_resp->dataLength     = CNV_LITTLE_TO_INT( probe_resp->dataLength );

#ifndef WARP
            probe_resp->lenType        = CNV_LITTLE_TO_INT( probe_resp->lenType );
#endif // WARP

            probe_resp->beaconInterval = CNV_LITTLE_TO_INT( probe_resp->beaconInterval );
            probe_resp->capability     = CNV_LITTLE_TO_INT( probe_resp->capability );
            probe_resp->flags          = CNV_LITTLE_TO_INT( probe_resp->flags );
        }
        break;


    case CFG_LINK_STAT:
#define ls ((LINK_STATUS_STRCT *)pLtv)
            ls->linkStatus = CNV_LITTLE_TO_INT( ls->linkStatus );
        break;
#undef ls

    case CFG_ASSOC_STAT:
        {
            ASSOC_STATUS_STRCT *pAs = (ASSOC_STATUS_STRCT *)pLtv;

            pAs->assocStatus = CNV_LITTLE_TO_INT( pAs->assocStatus );
        }
        break;


    case CFG_SECURITY_STAT:
        {
            SECURITY_STATUS_STRCT *pSs = (SECURITY_STATUS_STRCT *)pLtv;

            pSs->securityStatus = CNV_LITTLE_TO_INT( pSs->securityStatus );
            pSs->reason         = CNV_LITTLE_TO_INT( pSs->reason );
        }
        break;


    case CFG_WMP:
        break;


    case CFG_NULL:
        break;


    default:
        break;
    }

    DBG_LEAVE( DbgInfo );
    return;
} // wl_endian_translate_event
/*============================================================================*/


/*******************************************************************************
 *	msf_assert()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Print statement used to display asserts from within the HCF. Only called
 *  when asserts in the HCF are turned on. See hcfcfg.h for more information.
 *
 *  PARAMETERS:
 *
 *      file_namep  - the filename in which the assert occurred.
 *      line_number - the line number on which the assert occurred.
 *      trace       - a comment associated with the assert.
 *      qual        - return code or other value related to the assert
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void msf_assert( unsigned int line_number, hcf_16 trace, hcf_32 qual )
{
    DBG_PRINT( "HCF ASSERT: Line %d, VAL: 0x%.8x\n", line_number, /*;?*/(u32)qual );
} // msf_assert
/*============================================================================*/




/*******************************************************************************
 *	wl_parse_ds_ie()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      This function parses the Direct Sequence Parameter Set IE, used to
 *      determine channel/frequency information.
 *
 *  PARAMETERS:
 *
 *      probe_rsp - a pointer to a PROBE_RESP structure containing the probe
 *                  response.
 *
 *  RETURNS:
 *
 *      The channel on which the BSS represented by this probe response is
 *      transmitting.
 *
 ******************************************************************************/
hcf_8 wl_parse_ds_ie( PROBE_RESP *probe_rsp )
{
    int     i;
    int     ie_length = 0;
    hcf_8   *buf;
    hcf_8   buf_size;
    /*------------------------------------------------------------------------*/


    if( probe_rsp == NULL ) {
        return 0;
    }

    buf      = probe_rsp->rawData;
    buf_size = sizeof( probe_rsp->rawData );


    for( i = 0; i < buf_size; i++ ) {
        if( buf[i] == DS_INFO_ELEM ) {
            /* Increment by 1 to get the length, and test it; in a DS element,
               length should always be 1 */
            i++;
            ie_length = buf[i];

            if( buf[i] == 1 ) {
                /* Get the channel information */
                i++;
                return buf[i];
            }
        }
    }

    /* If we get here, we didn't find a DS-IE, which is strange */
    return 0;
} // wl_parse_ds_ie


/*******************************************************************************
 *	wl_parse_wpa_ie()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      This function parses the Probe Response for a valid WPA-IE.
 *
 *  PARAMETERS:
 *
 *      probe_rsp - a pointer to a PROBE_RESP structure containing the probe
 *                  response
 *      length    - a pointer to an hcf_16 in which the size of the WPA-IE will
 *                  be stored (if found).
 *
 *  RETURNS:
 *
 *      A pointer to the location in the probe response buffer where a valid
 *      WPA-IE lives. The length of this IE is written back to the 'length'
 *      argument passed to the function.
 *
 ******************************************************************************/
hcf_8 * wl_parse_wpa_ie( PROBE_RESP *probe_rsp, hcf_16 *length )
{
    int     i;
    int     ie_length = 0;
    hcf_8   *buf;
    hcf_8   buf_size;
    hcf_8   wpa_oui[] = WPA_OUI_TYPE;
    /*------------------------------------------------------------------------*/


    if( probe_rsp == NULL || length == NULL ) {
        return NULL;
    }

    buf      = probe_rsp->rawData;
    buf_size = sizeof( probe_rsp->rawData );
    *length  = 0;


    for( i = 0; i < buf_size; i++ ) {
        if( buf[i] == GENERIC_INFO_ELEM ) {
            /* Increment by one to get the IE length */
            i++;
            ie_length = probe_rsp->rawData[i];

            /* Increment by one to point to the IE payload */
            i++;

            /* Does the IE contain a WPA OUI? If not, it's a proprietary IE */
            if( memcmp( &buf[i], &wpa_oui, WPA_SELECTOR_LEN ) == 0 ) {
                /* Pass back length and return a pointer to the WPA-IE */
                /* NOTE: Length contained in the WPA-IE is only the length of
                   the payload. The entire WPA-IE, including the IE identifier
                   and the length, is 2 bytes larger */
                *length = ie_length + 2;

                /* Back up the pointer 2 bytes to include the IE identifier and
                   the length in the buffer returned */
                i -= 2;
                return &buf[i];
            }

            /* Increment past this non-WPA IE and continue looking */
            i += ( ie_length - 1 );
        }
    }

    /* If we're here, we didn't find a WPA-IE in the buffer */
    return NULL;
} // wl_parse_wpa_ie


/*******************************************************************************
 *	wl_print_wpa_ie()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Function used to take a WPA Information Element (WPA-IE) buffer and
 *      display it in a readable format.
 *
 *  PARAMETERS:
 *
 *      buffer - the byte buffer containing the WPA-IE
 *      length - the length of the above buffer
 *
 *  RETURNS:
 *
 *      A pointer to the formatted WPA-IE string. Note that the format used is
 *      byte-by-byte printing as %02x hex values with no spaces. This is
 *      required for proper operation with some WPA supplicants.
 *
 ******************************************************************************/
hcf_8 * wl_print_wpa_ie( hcf_8 *buffer, int length )
{
    int count;
    int rows;
    int remainder;
    int rowsize = 4;
    hcf_8 row_buf[64];
    static hcf_8 output[512];
    /*------------------------------------------------------------------------*/


    memset( output, 0, sizeof( output ));
    memset( row_buf, 0, sizeof( row_buf ));


    /* Determine how many rows will be needed, and the remainder */
    rows = length / rowsize;
    remainder = length % rowsize;


    /* Format the rows */
    for( count = 0; count < rows; count++ ) {
        sprintf( row_buf, "%02x%02x%02x%02x",
                 buffer[count*rowsize], buffer[count*rowsize+1],
                 buffer[count*rowsize+2], buffer[count*rowsize+3]);
        strcat( output, row_buf );
    }

    memset( row_buf, 0, sizeof( row_buf ));


    /* Format the remainder */
    for( count = 0; count < remainder; count++ ) {
        sprintf( row_buf, "%02x", buffer[(rows*rowsize)+count]);
        strcat( output, row_buf );
    }

    return output;
} // wl_print_wpa_ie
/*============================================================================*/




/*******************************************************************************
 *	wl_is_a_valid_chan()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Checks if a given channel is valid
 *
 *  PARAMETERS:
 *
 *      channel - the channel
 *
 *  RETURNS:
 *
 *      1 if TRUE
 *      0 if FALSE
 *
 ******************************************************************************/
int wl_is_a_valid_chan( int channel )
{
    int i;
    /*------------------------------------------------------------------------*/


    /* Strip out the high bit set by the FW for 802.11a channels */
    if( channel & 0x100 ) {
        channel = channel & 0x0FF;
    }

    /* Iterate through the matrix and retrieve the frequency */
    for( i = 0; i < MAX_CHAN_FREQ_MAP_ENTRIES; i++ ) {
        if( chan_freq_list[i][0] == channel ) {
            return 1;
        }
    }

    return 0;
} // wl_is_a_valid_chan
/*============================================================================*/




/*******************************************************************************
 *	wl_get_chan_from_freq()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Checks if a given frequency is valid
 *
 *  PARAMETERS:
 *
 *      freq - the frequency
 *
 *  RETURNS:
 *
 *      1 if TRUE
 *      0 if FALSE
 *
 ******************************************************************************/
int wl_is_a_valid_freq( long frequency )
{
    int i;
    /*------------------------------------------------------------------------*/


    /* Iterate through the matrix and retrieve the channel */
    for( i = 0; i < MAX_CHAN_FREQ_MAP_ENTRIES; i++ ) {
        if( chan_freq_list[i][1] == frequency ) {
            return 1;
        }
    }

    return 0;
} // wl_is_a_valid_freq
/*============================================================================*/




/*******************************************************************************
 *	wl_get_freq_from_chan()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Function used to look up the frequency for a given channel on which the
 *      adapter is Tx/Rx.
 *
 *  PARAMETERS:
 *
 *      channel - the channel
 *
 *  RETURNS:
 *
 *      The corresponding frequency
 *
 ******************************************************************************/
long wl_get_freq_from_chan( int channel )
{
    int i;
    /*------------------------------------------------------------------------*/


    /* Strip out the high bit set by the FW for 802.11a channels */
    if( channel & 0x100 ) {
        channel = channel & 0x0FF;
    }

    /* Iterate through the matrix and retrieve the frequency */
    for( i = 0; i < MAX_CHAN_FREQ_MAP_ENTRIES; i++ ) {
        if( chan_freq_list[i][0] == channel ) {
            return chan_freq_list[i][1];
        }
    }

    return 0;
} // wl_get_freq_from_chan
/*============================================================================*/




/*******************************************************************************
 *	wl_get_chan_from_freq()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Function used to look up the channel for a given frequency on which the
 *      adapter is Tx/Rx.
 *
 *  PARAMETERS:
 *
 *      frequency - the frequency
 *
 *  RETURNS:
 *
 *      The corresponding channel
 *
 ******************************************************************************/
int wl_get_chan_from_freq( long frequency )
{
    int i;
    /*------------------------------------------------------------------------*/


    /* Iterate through the matrix and retrieve the channel */
    for( i = 0; i < MAX_CHAN_FREQ_MAP_ENTRIES; i++ ) {
        if( chan_freq_list[i][1] == frequency ) {
            return chan_freq_list[i][0];
        }
    }

    return 0;
} // wl_get_chan_from_freq
/*============================================================================*/




/*******************************************************************************
 *	wl_process_link_status()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Process the link status message signaled by the device.
 *
 *  PARAMETERS:
 *
 *      lp - a pointer to the device's private structure
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_process_link_status( struct wl_private *lp )
{
    hcf_16 link_stat;
    /*------------------------------------------------------------------------*/

    DBG_FUNC( "wl_process_link_status" );
    DBG_ENTER( DbgInfo );

    if( lp != NULL ) {
        //link_stat = lp->hcfCtx.IFB_DSLinkStat & CFG_LINK_STAT_FW;
        link_stat = lp->hcfCtx.IFB_LinkStat & CFG_LINK_STAT_FW;
        switch( link_stat ) {
        case 1:
            DBG_TRACE( DbgInfo, "Link Status : Connected\n" );
            wl_wext_event_ap( lp->dev );
            break;
        case 2:
            DBG_TRACE( DbgInfo, "Link Status : Disconnected\n"  );
            break;
        case 3:
            DBG_TRACE( DbgInfo, "Link Status : Access Point Change\n" );
            break;
        case 4:
            DBG_TRACE( DbgInfo, "Link Status : Access Point Out of Range\n" );
            break;
        case 5:
            DBG_TRACE( DbgInfo, "Link Status : Access Point In Range\n" );
            break;
        default:
            DBG_TRACE( DbgInfo, "Link Status : UNKNOWN (0x%04x)\n", link_stat );
            break;
        }
    }
    DBG_LEAVE( DbgInfo );
    return;
} // wl_process_link_status
/*============================================================================*/




/*******************************************************************************
 *	wl_process_probe_response()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Process the probe responses retunred by the device as a result of an
 *      active scan.
 *
 *  PARAMETERS:
 *
 *      lp - a pointer to the device's private structure
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_process_probe_response( struct wl_private *lp )
{
    PROBE_RESP  *probe_rsp;
    hcf_8       *wpa_ie = NULL;
    hcf_16      wpa_ie_len = 0;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wl_process_probe_response" );
    DBG_ENTER( DbgInfo );


    if( lp != NULL ) {
        probe_rsp = (PROBE_RESP *)&lp->ProbeResp;

        wl_endian_translate_event( (ltv_t *)probe_rsp );

        DBG_TRACE( DbgInfo, "(%s) =========================\n", lp->dev->name );
        DBG_TRACE( DbgInfo, "(%s) length      : 0x%04x.\n",  lp->dev->name,
                probe_rsp->length );

        if( probe_rsp->length > 1 ) {
            DBG_TRACE( DbgInfo, "(%s) infoType    : 0x%04x.\n", lp->dev->name,
                    probe_rsp->infoType );

            DBG_TRACE( DbgInfo, "(%s) signal      : 0x%02x.\n", lp->dev->name,
                    probe_rsp->signal );

            DBG_TRACE( DbgInfo, "(%s) silence     : 0x%02x.\n", lp->dev->name,
                    probe_rsp->silence );

            DBG_TRACE( DbgInfo, "(%s) rxFlow      : 0x%02x.\n", lp->dev->name,
                    probe_rsp->rxFlow );

            DBG_TRACE( DbgInfo, "(%s) rate        : 0x%02x.\n", lp->dev->name,
                    probe_rsp->rate );

            DBG_TRACE( DbgInfo, "(%s) frame cntl  : 0x%04x.\n", lp->dev->name,
                    probe_rsp->frameControl );

            DBG_TRACE( DbgInfo, "(%s) durID       : 0x%04x.\n", lp->dev->name,
                    probe_rsp->durID );

            DBG_TRACE( DbgInfo, "(%s) address1    : %s\n", lp->dev->name,
                    DbgHwAddr( probe_rsp->address1 ));

            DBG_TRACE( DbgInfo, "(%s) address2    : %s\n", lp->dev->name,
                    DbgHwAddr( probe_rsp->address2 ));

            DBG_TRACE( DbgInfo, "(%s) BSSID       : %s\n", lp->dev->name,
                    DbgHwAddr( probe_rsp->BSSID ));

            DBG_TRACE( DbgInfo, "(%s) sequence    : 0x%04x.\n", lp->dev->name,
                    probe_rsp->sequence );

            DBG_TRACE( DbgInfo, "(%s) address4    : %s\n", lp->dev->name,
                    DbgHwAddr( probe_rsp->address4 ));

            DBG_TRACE( DbgInfo, "(%s) datalength  : 0x%04x.\n", lp->dev->name,
                    probe_rsp->dataLength );

            DBG_TRACE( DbgInfo, "(%s) DA          : %s\n", lp->dev->name,
                    DbgHwAddr( probe_rsp->DA ));

            DBG_TRACE( DbgInfo, "(%s) SA          : %s\n", lp->dev->name,
                    DbgHwAddr( probe_rsp->SA ));

#ifdef WARP

            DBG_TRACE( DbgInfo, "(%s) channel     : %d\n", lp->dev->name,
                    probe_rsp->channel );

            DBG_TRACE( DbgInfo, "(%s) band        : %d\n", lp->dev->name,
                    probe_rsp->band );
#else
            DBG_TRACE( DbgInfo, "(%s) lenType     : 0x%04x.\n", lp->dev->name,
                    probe_rsp->lenType );
#endif  // WARP

            DBG_TRACE( DbgInfo, "(%s) timeStamp   : %d.%d.%d.%d.%d.%d.%d.%d\n",
                    lp->dev->name,
                    probe_rsp->timeStamp[0],
                    probe_rsp->timeStamp[1],
                    probe_rsp->timeStamp[2],
                    probe_rsp->timeStamp[3],
                    probe_rsp->timeStamp[4],
                    probe_rsp->timeStamp[5],
                    probe_rsp->timeStamp[6],
                    probe_rsp->timeStamp[7]);

            DBG_TRACE( DbgInfo, "(%s) beaconInt   : 0x%04x.\n", lp->dev->name,
                    probe_rsp->beaconInterval );

            DBG_TRACE( DbgInfo, "(%s) capability  : 0x%04x.\n", lp->dev->name,
                    probe_rsp->capability );

            DBG_TRACE( DbgInfo, "(%s) SSID len    : 0x%04x.\n", lp->dev->name,
                    probe_rsp->rawData[1] );


            if( probe_rsp->rawData[1] > 0 ) {
                char ssid[HCF_MAX_NAME_LEN];

                memset( ssid, 0, sizeof( ssid ));
                strncpy( ssid, &probe_rsp->rawData[2],
                            probe_rsp->rawData[1] );

                DBG_TRACE( DbgInfo, "(%s) SSID        : %s\n",
                            lp->dev->name, ssid );
            }


            /* Parse out the WPA-IE, if one exists */
            wpa_ie = wl_parse_wpa_ie( probe_rsp, &wpa_ie_len );
            if( wpa_ie != NULL ) {
                DBG_TRACE( DbgInfo, "(%s) WPA-IE      : %s\n",
                lp->dev->name, wl_print_wpa_ie( wpa_ie, wpa_ie_len ));
            }

            DBG_TRACE( DbgInfo, "(%s) flags       : 0x%04x.\n",
                        lp->dev->name, probe_rsp->flags );
        }

        DBG_TRACE( DbgInfo, "\n" );


        /* If probe response length is 1, then the scan is complete */
        if( probe_rsp->length == 1 ) {
            DBG_TRACE( DbgInfo, "SCAN COMPLETE\n" );
            lp->probe_results.num_aps = lp->probe_num_aps;
            lp->probe_results.scan_complete = TRUE;

            /* Reset the counter for the next scan request */
            lp->probe_num_aps = 0;

            /* Send a wireless extensions event that the scan completed */
            wl_wext_event_scan_complete( lp->dev );
        } else {
            /* Only copy to the table if the entry is unique; APs sometimes
                respond more than once to a probe */
            if( lp->probe_num_aps == 0 ) {
                /* Copy the info to the ScanResult structure in the private
                adapter struct */
                memcpy( &( lp->probe_results.ProbeTable[lp->probe_num_aps] ),
                        probe_rsp, sizeof( PROBE_RESP ));

                /* Increment the number of APs detected */
                lp->probe_num_aps++;
            } else {
                int count;
                int unique = 1;

                for( count = 0; count < lp->probe_num_aps; count++ ) {
                    if( memcmp( &( probe_rsp->BSSID ),
                        lp->probe_results.ProbeTable[count].BSSID,
                        ETH_ALEN ) == 0 ) {
                        unique = 0;
                    }
                }

                if( unique ) {
                    /* Copy the info to the ScanResult structure in the
                    private adapter struct. Only copy if there's room in the
                    table */
                    if( lp->probe_num_aps < MAX_NAPS )
                    {
                        memcpy( &( lp->probe_results.ProbeTable[lp->probe_num_aps] ),
                                probe_rsp, sizeof( PROBE_RESP ));
                    }
                    else
                    {
                        DBG_WARNING( DbgInfo, "Num of scan results exceeds storage, truncating\n" );
                    }

                    /* Increment the number of APs detected. Note I do this
                        here even when I don't copy the probe response to the
                        buffer in order to detect the overflow condition */
                    lp->probe_num_aps++;
                }
            }
        }
    }

    DBG_LEAVE( DbgInfo );
    return;
} // wl_process_probe_response
/*============================================================================*/




/*******************************************************************************
 *	wl_process_updated_record()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Process the updated information record message signaled by the device.
 *
 *  PARAMETERS:
 *
 *      lp - a pointer to the device's private structure
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_process_updated_record( struct wl_private *lp )
{
    DBG_FUNC( "wl_process_updated_record" );
    DBG_ENTER( DbgInfo );


    if( lp != NULL ) {
        lp->updatedRecord.u.u16[0] = CNV_LITTLE_TO_INT( lp->updatedRecord.u.u16[0] );

        switch( lp->updatedRecord.u.u16[0] ) {
        case CFG_CUR_COUNTRY_INFO:
            DBG_TRACE( DbgInfo, "Updated Record: CFG_CUR_COUNTRY_INFO\n" );
            wl_connect( lp );
            break;

        case CFG_PORT_STAT:
            DBG_TRACE( DbgInfo, "Updated Record: WAIT_FOR_CONNECT (0xFD40)\n" );
            //wl_connect( lp );
            break;

        default:
            DBG_TRACE( DbgInfo, "UNKNOWN: 0x%04x\n",
                       lp->updatedRecord.u.u16[0] );
        }
    }

    DBG_LEAVE( DbgInfo );
    return;
} // wl_process_updated_record
/*============================================================================*/




/*******************************************************************************
 *	wl_process_assoc_status()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Process the association status event signaled by the device.
 *
 *  PARAMETERS:
 *
 *      lp - a pointer to the device's private structure
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_process_assoc_status( struct wl_private *lp )
{
    ASSOC_STATUS_STRCT *assoc_stat;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wl_process_assoc_status" );
    DBG_ENTER( DbgInfo );


    if( lp != NULL ) {
        assoc_stat = (ASSOC_STATUS_STRCT *)&lp->assoc_stat;

        wl_endian_translate_event( (ltv_t *)assoc_stat );

        switch( assoc_stat->assocStatus ) {
        case 1:
            DBG_TRACE( DbgInfo, "Association Status : STA Associated\n" );
            break;

        case 2:
            DBG_TRACE( DbgInfo, "Association Status : STA Reassociated\n" );
            break;

        case 3:
            DBG_TRACE( DbgInfo, "Association Status : STA Disassociated\n" );
            break;

        default:
            DBG_TRACE( DbgInfo, "Association Status : UNKNOWN (0x%04x)\n",
                        assoc_stat->assocStatus );
            break;
        }

        DBG_TRACE( DbgInfo, "STA Address        : %s\n",
                    DbgHwAddr( assoc_stat->staAddr ));

        if(( assoc_stat->assocStatus == 2 )  && ( assoc_stat->len == 8 )) {
            DBG_TRACE( DbgInfo, "Old AP Address     : %s\n",
                        DbgHwAddr( assoc_stat->oldApAddr ));
        }
    }

    DBG_LEAVE( DbgInfo );
    return;
} // wl_process_assoc_status
/*============================================================================*/




/*******************************************************************************
 *	wl_process_security_status()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Process the security status message signaled by the device.
 *
 *  PARAMETERS:
 *
 *      lp - a pointer to the device's private structure
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_process_security_status( struct wl_private *lp )
{
    SECURITY_STATUS_STRCT *sec_stat;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wl_process_security_status" );
    DBG_ENTER( DbgInfo );


    if( lp != NULL ) {
        sec_stat = (SECURITY_STATUS_STRCT *)&lp->sec_stat;

        wl_endian_translate_event( (ltv_t *)sec_stat );

        switch( sec_stat->securityStatus ) {
        case 1:
            DBG_TRACE( DbgInfo, "Security Status : Dissassociate [AP]\n" );
            break;

        case 2:
            DBG_TRACE( DbgInfo, "Security Status : Deauthenticate [AP]\n" );
            break;

        case 3:
            DBG_TRACE( DbgInfo, "Security Status : Authenticate Fail [STA] or [AP]\n" );
            break;

        case 4:
            DBG_TRACE( DbgInfo, "Security Status : MIC Fail\n" );
            break;

        case 5:
            DBG_TRACE( DbgInfo, "Security Status : Associate Fail\n" );
            break;

        default:
            DBG_TRACE( DbgInfo, "Security Status : UNKNOWN (0x%04x)\n",
                        sec_stat->securityStatus );
            break;
        }

        DBG_TRACE( DbgInfo, "STA Address     : %s\n",
                   DbgHwAddr( sec_stat->staAddr ));
        DBG_TRACE( DbgInfo, "Reason          : 0x%04x \n", sec_stat->reason );

    }

    DBG_LEAVE( DbgInfo );
    return;
} // wl_process_security_status
/*============================================================================*/

int wl_get_tallies(struct wl_private *lp,
		   CFG_HERMES_TALLIES_STRCT *tallies)
{
    int ret = 0;
    int status;
    CFG_HERMES_TALLIES_STRCT *pTallies;

    DBG_FUNC( "wl_get_tallies" );
    DBG_ENTER(DbgInfo);

    /* Get the current tallies from the adapter */
    lp->ltvRecord.len = 1 + HCF_TOT_TAL_CNT * sizeof(hcf_16);
    lp->ltvRecord.typ = CFG_TALLIES;

    status = hcf_get_info(&(lp->hcfCtx), (LTVP)&(lp->ltvRecord));

    if( status == HCF_SUCCESS ) {
	pTallies = (CFG_HERMES_TALLIES_STRCT *)&(lp->ltvRecord.u.u32);
	memcpy(tallies, pTallies, sizeof(*tallies));
    	DBG_TRACE( DbgInfo, "Get tallies okay, dixe: %d\n", sizeof(*tallies) );
    } else {
    	DBG_TRACE( DbgInfo, "Get tallies failed\n" );
	ret = -EFAULT;
    }

    DBG_LEAVE( DbgInfo );

    return ret;
}

