/*******************************************************************************
 * 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.
 *
 ******************************************************************************/




/*******************************************************************************
 * VERSION CONTROL INFORMATION
 *******************************************************************************
 *
 * $Author: vjs $
 * $Date: 2004/02/16 17:58:12 $
 * $Revision: 1.22 $
 * $Source: /cvsroot/wifi/wl_lkm/wireless/wl_util.c,v $
 *
 ******************************************************************************/




/*******************************************************************************
 *  include files
 ******************************************************************************/
#include <wireless/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 <hcf/debug.h>
#include <hcf.h>
#include <hcfdef.h>

#include <wireless/wl_if.h>
#include <wireless/wl_internal.h>
#include <wireless/wl_util.h>




/*******************************************************************************
 * global variables
 ******************************************************************************/
#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 );
}
/*============================================================================*/




/*******************************************************************************
 *	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 ));
}
/*============================================================================*/




/*******************************************************************************
 *	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 );
    }
}
/*============================================================================*/




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




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




#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;
}
/*============================================================================*/

#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;
}
/*============================================================================*/




/*******************************************************************************
 *	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 );
    }
}
/*============================================================================*/




/*******************************************************************************
 *	wvlan_endian_translate_mailbox()
 *******************************************************************************
 *
 *  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 wvlan_endian_translate_mailbox( ltv_t *pLtv )
{
    DBG_FUNC( "wvlanEndianTranslateMailbox" );
    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:
        break;


    case CFG_LINK_STAT:
        {
            LINK_STATUS_STRCT *pLs = (LINK_STATUS_STRCT *)pLtv;
    
            pLs->linkStatus = CNV_LITTLE_TO_INT( pLs->linkStatus );
        }
        break;


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




/*******************************************************************************
 *	dsf_assert()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Print statement used to display asserts from within the DHF. Only called
 *  when asserts in the DHF are turned on. See dhfcfg.h for more information.
 *
 *  PARAMETERS:
 *
 *      file_name   - the filename in which the assert occurred.
 *      line_number - the line number on which the assert occurred.
 *      szCmt       - a comment associated with the assert.
 *      nVal        - return code or other value related to the assert
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void dsf_assert(char *file_name, unsigned int line_number, char *szCmt, int nVal)
{
    DBG_PRINT( "DSF ASSERT: File %s, Line %d - %s, VAL: 0x%x\n", file_name, line_number, szCmt, nVal );
}
/*============================================================================*/




/*******************************************************************************
 *	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(wci_bufp file_namep, unsigned int line_number, hcf_16 trace, int qual )
void msf_assert( unsigned int line_number, hcf_16 trace, hcf_32 qual )
{
    DBG_PRINT( "HCF ASSERT: Line %d, VAL: 0x%x\n", line_number, (u32)qual );
}
/*============================================================================*/




/*******************************************************************************
 *	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_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_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;
}
/*============================================================================*/
