/*******************************************************************************
 * 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
 *
 *------------------------------------------------------------------------------
 *
 * 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/03/31 14:47:45 $
 * $Revision: 1.45 $
 * $Source: /cvsroot/wifi/wl_lkm/wireless/wl_wext.c,v $
 *
 ******************************************************************************/




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

#include <linux/if_arp.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <asm/uaccess.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>
#include <wireless/wl_main.h>
#include <wireless/wl_wext.h>




/* If WIRELESS_EXT is not defined (as a result of HAS_WIRELESS_EXTENSIONS
   #including linux/wireless.h), then these functions do not need to be included
   in the build. */
#ifdef WIRELESS_EXT




/*******************************************************************************
 * global definitions
 ******************************************************************************/
// Frequency of channel 1..14 in MHz
static const long frequency_list[] = 
{ 
    2412, 2417, 2422, 2427, 2432, 2437, 2442,
    2447, 2452, 2457, 2462, 2467, 2472, 2484
};


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




/*******************************************************************************
 *	wl_wireless_ioctl()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Handle the Wireless extension IOCTLs and return TRUE if it was a 
 *  wireless IOCTL.
 *      
 *
 *  PARAMETERS:
 *
 *      dev     - a pointer to the net_device structure representing the 
 *                device this IOCTL is for.
 *      rq      - the IOCTL request buffer.
 *      cmd     - the IOCTL command.
 *      pRet    - an integer in which to store the return value of the wireless
 *                request.
 *      
 *
 *  RETURNS:
 *
 *      TRUE if the IOCTL is a wireless IOCTL
 *      FALSE if not
 *
 ******************************************************************************/
bool_t wl_wireless_ioctl( struct net_device *dev, struct ifreq *rq, int cmd,
                              int *pRet )
{
    struct wl_private  *lp;
    int                     ret;
    bool_t                  wIoctl;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wl_wireless_ioctl" );
    DBG_ENTER( DbgInfo );
    DBG_PARAM( DbgInfo, "dev", "%s (0x%p)", dev->name, dev );
    DBG_PARAM( DbgInfo, "rq", "0x%p", rq );
    DBG_PARAM( DbgInfo, "cmd", "0x%04x", cmd );
    DBG_PARAM( DbgInfo, "pRet", "%d (0x%p)", *pRet, pRet );


    DBG_ASSERT( dev != NULL );


    wIoctl  = FALSE;
    ret     = *pRet;

    lp = (struct wl_private *)dev->priv;

    if(( cmd >= SIOCIWFIRST ) && ( cmd <= SIOCIWLAST ))
    {
        struct iwreq *wrq = (struct iwreq *) rq;

        wIoctl = TRUE;

        switch( cmd )
        {

        case SIOCGIWNAME:       // Get Protocol name

            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWNAME\n" );
            wireless_get_protocol( wrq );

            break;


        case SIOCSIWFREQ:       // Set channel/frequency

            DBG_TRACE( DbgInfo, "IOCTL: SIOCSIWFREQ\n" );
            ret = wireless_set_frequency( wrq, lp );

            break;

        
        case SIOCGIWFREQ:       // Get channel/frequency

            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWFREQ\n" );
            wireless_get_frequency( wrq, lp );

            break;


        case SIOCGIWRANGE:      // Get range of parameters

            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWRANGE\n" );
            ret = wireless_get_range( wrq, lp );

            break;


	    case SIOCSIWSPY:        // Set the spy list
            
            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWNAME\n" );
            ret = wireless_set_spy_addrs( wrq, lp );

            break;


	    case SIOCGIWSPY:        // Get the spy list
            
            DBG_TRACE( DbgInfo, "IOCTL: SIOCSIWSPY\n" );
            ret = wireless_get_spy_addrs( wrq, lp );

            break;


#if WIRELESS_EXT > 4

        case SIOCSIWAP:
           
            DBG_TRACE( DbgInfo, "IOCTL: SIOCSIWAP NOT SUPPORTED!!!\n" );
            ret = -EOPNOTSUPP;

            break;


#if (HCF_TYPE) & HCF_TYPE_STA
        case SIOCGIWAP:         // Get the MAC Address of current AP

            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWAP\n" );
            ret = wireless_get_bssid( wrq, lp );

            break;

#endif //HCF_STA


        case SIOCGIWAPLIST:

            /* NOTE: SIOCGIWAPLIST has been deprecated by SIOCSIWSCAN/SIOCGIWSCAN.
               This function implements SIOCGIWAPLIST only to provide backwards 
               compatibility. For all systems using WIRELESS_EXT v14 and higher,
               use SIOCSIWSCAN! */
            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWAPLIST\n" );
            if( WIRELESS_EXT >= 14 )
            {
                DBG_WARNING( DbgInfo, "This Wireless IOCTL has been deprecated by SIOCSIWSCAN!!!\n" );
                DBG_WARNING( DbgInfo, "Please use the SIOCSIWSCAN/SIOCGIWSCAN IOCTLs instead!!!\n" );
            }

            ret = wireless_get_ap_list( wrq, lp );

            break;


	    case SIOCSIWSENS:       // Set the desired AP density

            DBG_TRACE( DbgInfo, "IOCTL: SIOCSIWSENS\n" );
            ret = wireless_set_sensitivity( wrq, lp );
            
            break;


	    case SIOCGIWSENS:       // Get the current AP density
            
            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWSENS\n" );
            wireless_get_sensitivity( wrq, lp );
            
            break;

#endif // WIRELESS_EXT > 4


#if WIRELESS_EXT > 5

	    case SIOCSIWESSID:      // Set the desired network name (ESSID)
            
            DBG_TRACE( DbgInfo, "IOCTL: SIOCSIWESSID\n" );
            ret = wireless_set_essid( wrq, lp );

            break;


	    case SIOCGIWESSID:      // Get the current network name (ESSID)
            
            DBG_TRACE(DbgInfo, "IOCTL: SIOCGIWESSID\n");  
            ret = wireless_get_essid( wrq, lp );
            
            break;

#endif // WIRELESS_EXT > 5


#if WIRELESS_EXT > 7

	    case SIOCSIWNICKN:      // Set desired station nickname
           
            DBG_TRACE( DbgInfo, "IOCTL: SIOCSIWNICKN\n" );
            ret = wireless_set_nickname( wrq, lp );
            
            break;
	

	    case SIOCGIWNICKN:      // Get current station nickname
            
            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWNICKN\n" );
            ret = wireless_get_nickname( wrq, lp );
            
            break;


        case SIOCSIWRTS:        // Set the desired RTS threshold
           
            DBG_TRACE( DbgInfo, "IOCTL: SIOCSIWRTS\n" );
            ret = wireless_set_rts_threshold( wrq, lp );
            
            break;
	

	    case SIOCGIWRTS:        // Get the current RTS threshold
            
            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWRTS\n" );
            wireless_get_rts_threshold( wrq, lp );
            
            break;


        case SIOCSIWFRAG:       // Set the fragmentation threshold
            
            DBG_TRACE( DbgInfo, "IOCTL: SIOCSIWFRAG NOT SUPPORTED\n" );
            ret = -EOPNOTSUPP;
            
            break;


        case SIOCGIWFRAG:       // Get the fragmentation threshold
            
            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWFRAG NOT SUPPORTED\n" );
            ret = -EOPNOTSUPP;
            
            break;


        case SIOCSIWRATE:       // Set default bit rate (bps)
            
            DBG_TRACE( DbgInfo, "IOCTL: SIOCSIWRATE\n" );
            ret = wireless_set_rate( wrq, lp );
           
            break;


        case SIOCGIWRATE:       // Get default bit rate (bps)
            
            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWRATE\n" );
            ret = wireless_get_rate( wrq, lp );
            
            break;

#endif // WIRELESS_EXT > 7

#if WIRELESS_EXT > 8

        case SIOCSIWENCODE:     // Set the encryption keys
            
            DBG_TRACE( DbgInfo, "IOCTL: SIOCSIWENCODE\n" );
            ret = wireless_set_encode( wrq, lp );
            
            break;


	    case SIOCGIWENCODE:     // Get the encryption keys
           
            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWENCODE\n" );
            ret = wireless_get_encode( wrq, lp );
            
            break;


	    case SIOCSIWMODE:       // Set the port type
            
            DBG_TRACE( DbgInfo, "IOCTL: SIOCSIWMODE\n" );
            ret = wireless_set_porttype( wrq, lp );
            
            break;


	    case SIOCGIWMODE:       // Get the port type
           
            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWMODE\n" );
	        ret = wireless_get_porttype( wrq, lp );
            
            break;


        case SIOCSIWNWID:
            
            DBG_TRACE( DbgInfo, "IOCTL: SIOCSIWNWID\n" );
            DBG_TRACE( DbgInfo, "NOT SUPPORTED!!!\n" );
            ret = -EOPNOTSUPP;
            
            break;


        case SIOCGIWNWID:

            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWNWID\n" );
            DBG_TRACE( DbgInfo, "NOT SUPPORTED!!!\n" );
            ret = -EOPNOTSUPP;

            break;


        case SIOCSIWPOWER:

            DBG_TRACE( DbgInfo, "IOCTL: SIOCSIWPOWER\n" );
            ret = wireless_set_power( wrq, lp );

            break;


        case SIOCGIWPOWER:

            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWPOWER\n" );
            wireless_get_power( wrq, lp );

            break;

#endif // WIRELESS_EXT > 8


#if WIRELESS_EXT > 9

        case SIOCSIWTXPOW:

            DBG_TRACE( DbgInfo, "IOCTL: SIOCSIWTXPOW NOT SUPPORTED!!!\n" );
            ret = -EOPNOTSUPP;

            break;


        case SIOCGIWTXPOW:

            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWTXPOW\n" );
            wireless_get_tx_power( wrq, lp );

            break;

#endif  // WIRELESS_EXT > 9


#if WIRELESS_EXT > 10

        case SIOCSIWRETRY:

            DBG_TRACE( DbgInfo, "IOCTL: SIOCSIWRETRY NOT SUPPORTED!!!\n" );
            ret = -EOPNOTSUPP;

            break;


        case SIOCGIWRETRY:

            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWRETRY NOT SUPPORTED!!!\n" );
            ret = -EOPNOTSUPP;

            break;


#endif  // WIRELESS_EXT > 10


#if WIRELESS_EXT > 11

        case SIOCSIWSTATS:

            DBG_TRACE( DbgInfo, "IOCTL: SIOCSIWSTATS NOT SUPPORTED!!!\n" );
            ret = -EOPNOTSUPP;

            break;


        case SIOCGIWSTATS:

            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWSTATS\n" );
            wl_wireless_stats( dev );

            break;


#endif  // WIRELESS_EXT > 11

	    case SIOCGIWPRIV:       // Get private ioctl interface info

            DBG_TRACE(DbgInfo, "IOCTL: SIOCGIWPRIV NOT SUPPORTED!!!\n");
            ret = -EOPNOTSUPP;

            break;


#if WIRELESS_EXT > 12

        case SIOCSIWCOMMIT:

            DBG_TRACE( DbgInfo, "IOCTL: SIOCSIWCOMMIT\n" );
            wl_apply( lp );

            break;

#endif  // WIRELESS_EXT > 12


#if WIRELESS_EXT > 13

        case SIOCSIWSCAN:

            DBG_TRACE( DbgInfo, "IOCTL: SIOCSIWSCAN\n" );
            ret = wireless_set_scan( wrq, lp );

            break;


        case SIOCGIWSCAN:

            DBG_TRACE( DbgInfo, "IOCTL: SIOCGIWSCAN\n" );
            ret = wireless_get_scan( wrq, lp );

            break;

#endif  // WIRELESS_EXT > 13


#if WIRELESS_EXT > 14
        /* No IOCTLs added in v15 */
#endif

        default:
            DBG_TRACE( DbgInfo, "IOCTL CODE NOT SUPPORTED: 0x%X\n", cmd );
            ret = -EOPNOTSUPP;
        }
    }

    *pRet = ret;

    DBG_LEAVE( DbgInfo );

    return wIoctl;
}
/*============================================================================*/




/*******************************************************************************
 *	wireless_get_protocol()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Returns a vendor-defined string that should identify the wireless
 *  protocol used.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wireless_get_protocol( struct iwreq *wrq )
{
    DBG_FUNC( "wireless_get_protocol" );
    DBG_ENTER( DbgInfo );


    /* Originally, the driver was placing the string "Wireless" here. However, 
       the wireless extensions (/linux/wireless.h) indicate this string should
       describe the wireless protocol. */

    strcpy( wrq->u.name, "IEEE 802.11b" );

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




/*******************************************************************************
 *	wireless_set_frequency()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Sets the frequency (channel) on which the card should Tx/Rx.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_set_frequency( struct iwreq *wrq, struct wl_private *lp )
{

    int channel = 0;
    int ret     = 0;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_set_frequency" );
    DBG_ENTER( DbgInfo );


    if( !capable( CAP_NET_ADMIN ))
    {
        ret = -EPERM;
        DBG_LEAVE( DbgInfo );
        return ret;
    }

    /* If frequency specified, look up channel */
    if( wrq->u.freq.e == 1 )
    {
        int f = wrq->u.freq.m / 100000;
        int i;


        for( i = 0; i < 14; i++ )
        {
            if( f == frequency_list[i] )
            {
                channel = i+1;
                break;
            }
        }
    }

    /* Channel specified */
    if( wrq->u.freq.e == 0 )
    {
        channel = wrq->u.freq.m;
    }

    /* Validate the new value against the valid adapter channels. */
    if(( channel >= 0 ) && ( channel <= 14 ))
    {

#if (HCF_TYPE) & HCF_TYPE_HII

        /* Define a method for validating the channel selection for Hermes-II,
           as CFG_CHANNEL_LIST is only supported for Hermes-I */
#else
        __u16   *pChanList;
        int     status  = -1;


        lp->ltvRecord.len = 1 + ( sizeof( *pChanList ) / sizeof( hcf_16 ));
        lp->ltvRecord.typ = CFG_CHANNEL_LIST;

        status = hcf_get_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
        if( status == HCF_SUCCESS )
        {
            pChanList = (__u16 *)&( lp->ltvRecord.u.u32 );

 
            /* Check if the channel is acceptable. */
            if( !(( CNV_LITTLE_TO_INT( *pChanList )) & ( 0x01L << ( channel - 1 ))))
            {
                ret = -EINVAL;
                DBG_LEAVE( DbgInfo );
                return ret;
            }
        }
        else
        {
            ret = -EIO;
            DBG_LEAVE( DbgInfo );
            return ret;
        }

#endif /* (HCF_TYPE) & HCF_TYPE_HII */    
    }
    else
    {
        ret = -EINVAL;
        DBG_LEAVE( DbgInfo );
        return ret;
    }
 
    lp->Channel = channel;

    /* Commit the adapter parameters */
    wl_apply( lp );

    /* Send an event that channel/freq has been set */ 
    wl_wext_event_freq( lp->dev );

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




/*******************************************************************************
 *	wireless_get_frequency()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Gets the frequency (channel) on which the card is Tx/Rx.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wireless_get_frequency( struct iwreq *wrq, struct wl_private *lp )
{
    int status = -1;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_get_frequency" );
    DBG_ENTER( DbgInfo );


    memset( &( wrq->u.freq ), 0, sizeof( wrq->u.freq ));

    lp->ltvRecord.len = 2;
    lp->ltvRecord.typ = CFG_CURRENT_CHANNEL;
    
    status = hcf_get_info( &(lp->hcfCtx), (LTVP)&( lp->ltvRecord ));
    if( status == HCF_SUCCESS )
    {
        int channel = CNV_LITTLE_TO_INT( lp->ltvRecord.u.u16[0] );


#ifdef USE_FREQUENCY

        if( channel >= 1 && channel <= RADIO_CHANNELS )
        {
             wrq->u.freq.m = frequency_list[channel-1] * 100000;
             wrq->u.freq.e = 1;
        }
#else

        wrq->u.freq.m = channel;
        wrq->u.freq.e = 0;

#endif /* USE_FREQUENCY */
    }

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




/*******************************************************************************
 *	wireless_get_range()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      This function is used to provide misc info and statistics about the
 *  wireless device.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_get_range( struct iwreq *wrq, struct wl_private *lp )
{
    int ret = 0;
    int status = -1;
    int count;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_get_range" );
    DBG_ENTER( DbgInfo );


    if( wrq->u.data.pointer != NULL )
    {
        struct iw_range range;
        __u16           *pTxRate;


        /* Verify the user buffer */
        ret = verify_area( VERIFY_WRITE, wrq->u.data.pointer, sizeof( range ));

        if( ret != 0 )
        {
            DBG_LEAVE( DbgInfo );
            return ret;
        }

        /* Set range information */
        memset( &range, 0, sizeof( range ));

        /* Get the current transmit rate from the adapter */
        lp->ltvRecord.len = 1 + (sizeof(*pTxRate) / sizeof(hcf_16));
        lp->ltvRecord.typ = CFG_CURRENT_TX_RATE;
        
        status = hcf_get_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
        if( status == HCF_SUCCESS )
        {
            pTxRate = (__u16 *)&( lp->ltvRecord.u.u32 );

            range.throughput = CNV_LITTLE_TO_INT( *pTxRate ) * MEGABIT;
        }


        // NWID - NOT SUPPORTED


        /* Channel/Frequency Info */
        range.num_channels = RADIO_CHANNELS;

        
        /* Signal Level Thresholds */
        range.sensitivity = RADIO_SENSITIVITY_LEVELS;


        /* Link quality */
#ifdef USE_DBM

        range.max_qual.qual     = (u_char)HCF_MAX_COMM_QUALITY;
        
        /* If the value returned in /proc/net/wireless is greater than the maximum range,
           iwconfig assumes that the value is in dBm. Because an unsigned char is used,
           it requires a bit of contorsion... */

        range.max_qual.level    = (u_char)( dbm( HCF_MIN_SIGNAL_LEVEL ) - 1 );
        range.max_qual.noise    = (u_char)( dbm( HCF_MIN_NOISE_LEVEL ) - 1 );
#else

        range.max_qual.qual     = 100;
        range.max_qual.level    = 100;
        range.max_qual.noise    = 100;

#endif /* USE_DBM */


        /* Set available rates */
        range.num_bitrates = 0;

        lp->ltvRecord.len = 6;
        lp->ltvRecord.typ = CFG_SUPPORTED_DATA_RATES;
		
        status = hcf_get_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
        if( status == HCF_SUCCESS )
        {
			for( count = 0; count < MAX_RATES; count++ )
            {
                if( lp->ltvRecord.u.u8[count+2] != 0 )
                {
                    range.bitrate[count] = lp->ltvRecord.u.u8[count+2] * MEGABIT / 2;
                    range.num_bitrates++;
                }
			}
        }

        /* RTS Threshold info */
        range.min_rts   = MIN_RTS_BYTES;
        range.max_rts   = MAX_RTS_BYTES;

        // Frag Threshold info - NOT SUPPORTED

        // Power Management info - NOT SUPPORTED

        /* Encryption */

#if WIRELESS_EXT > 8

		/* Is WEP supported? */
		
        if( wl_has_wep( &( lp->hcfCtx )))
		{
		    /* WEP: RC4 40 bits */
		    range.encoding_size[0]      = MIN_KEY_SIZE;
            
		    /* RC4 ~128 bits */
		    range.encoding_size[1]      = MAX_KEY_SIZE;
		    range.num_encoding_sizes    = 2;
		    range.max_encoding_tokens   = MAX_KEYS;
		}
		else
		{
		    range.num_encoding_sizes    = 0;
		    range.max_encoding_tokens   = 0;
		}

#endif /* WIRELESS_EXT > 8 */

        /* Tx Power Info */
        range.txpower_capa  = IW_TXPOW_MWATT;
        range.num_txpower   = 1;
        range.txpower[0]    = RADIO_TX_POWER_MWATT;

#if WIRELESS_EXT > 10

        /* Wireless Extension Info */
        range.we_version_compiled   = WIRELESS_EXT;
        range.we_version_source     = WIRELESS_SUPPORT; 

        // Retry Limits and Lifetime - NOT SUPPORTED

#endif


#if WIRELESS_EXT > 11

        wl_wireless_stats( lp->dev );
        range.avg_qual = lp->wstats.qual;

#endif

        /* Copy the data into the user's buffer */
        wrq->u.data.length = sizeof( range );

        copy_to_user( wrq->u.data.pointer, &range, sizeof( range ));
    }

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




/*******************************************************************************
 *	wireless_set_spy_addrs()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Adds an IP or MAC address to the list of addresses to spy on.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_set_spy_addrs( struct iwreq *wrq, struct wl_private *lp )
{
    /*
        SIOCSIWSPY sets mac interface addresses to listen to. When
        packets arrive, we have to test the source mac address
        against this list and update the information.
    */


    int ret = 0;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_set_spy_addrs" );
    DBG_ENTER( DbgInfo );

    
    if( wrq->u.data.length > IW_MAX_SPY )
    {
        ret = -E2BIG;

        DBG_LEAVE( DbgInfo );
        return ret;
    }

    lp->spy_number = wrq->u.data.length;
    
    if( lp->spy_number > 0 )
    {
        struct sockaddr address[IW_MAX_SPY];
        int i;
        

        ret = verify_area( VERIFY_READ, wrq->u.data.pointer, 
                           sizeof( struct sockaddr ) * lp->spy_number );
        if( ret )
        {
            DBG_LEAVE( DbgInfo );
            return ret;
        }
        
        copy_from_user( address, wrq->u.data.pointer,
                        sizeof( struct sockaddr ) * lp->spy_number );

        for( i=0; i < lp->spy_number; i++ )
        {
            memcpy( lp->spy_address[i], address[i].sa_data, ETH_ALEN );
        }

        memset( lp->spy_stat, 0, sizeof( struct iw_quality ) * IW_MAX_SPY );

#ifdef DEBUGSPY

        DEBUG_NOTICE( DbgInfo, "%s: New spy list:\n", dev_info );
        for( i=0; i < wrq->u.data.length; i++ )
        {
            DEBUG_NOTICE( DbgInfo, "%s: %d - %02x:%02x:%02x:%02x:%02x:%02x\n",
                          dev_info, i+1,
                          lp->spy_address[i][0], lp->spy_address[i][1],
                          lp->spy_address[i][2], lp->spy_address[i][3],
                          lp->spy_address[i][4], lp->spy_address[i][5] );
        }

#endif // DEBUGSPY

    }

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




/*******************************************************************************
 *	wireless_get_spy_addrs()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Gets the list of IP or MAC addresses that the card is spying on.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_get_spy_addrs( struct iwreq *wrq, struct wl_private *lp )
{
    /*
        SIOCGIWSPY mac interface addresses being listened to. With the
        information it's possible to compare the quality of links to different
        HW addresses.
            
    */

    int ret = 0;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_get_spy_addrs" );
    DBG_ENTER( DbgInfo );


    wrq->u.data.length = lp->spy_number;

    if(( lp->spy_number > 0 ) && ( wrq->u.data.pointer ))
    {
        struct sockaddr address[IW_MAX_SPY];
        int i;

    
        ret = verify_area( VERIFY_WRITE, wrq->u.data.pointer,
                           ( sizeof( struct iw_quality ) + 
                                sizeof( struct sockaddr )) * IW_MAX_SPY );
        if( ret )
        {
            DBG_LEAVE( DbgInfo );
            return ret;
        }

        for( i=0 ; i < lp->spy_number; i++ )
        {
            memcpy( address[i].sa_data, lp->spy_address[i], ETH_ALEN );
            address[i].sa_family = AF_UNIX;
        }
        
        copy_to_user( wrq->u.data.pointer, address,
                      sizeof( struct sockaddr ) * lp->spy_number );

        copy_to_user( wrq->u.data.pointer + (sizeof(struct sockaddr)*lp->spy_number ),
                      lp->spy_stat, sizeof( struct iw_quality ) * lp->spy_number );
        
        for( i=0; i < lp->spy_number; i++ )
        {
            lp->spy_stat[i].updated = 0;
        }
    }

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




/*******************************************************************************
 *	wireless_get_bssid()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Gets the BSSID the wireless device is currently associated with.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_get_bssid( struct iwreq *wrq, struct wl_private *lp )
{
    int ret = 0;

#if (HCF_TYPE) & HCF_TYPE_STA
    int status = -1;
#endif  /* (HCF_TYPE) & HCF_TYPE_STA */
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_get_bssid" );
    DBG_ENTER( DbgInfo );


    wrq->u.data.length = ETH_ALEN;

    memset( wrq->u.ap_addr.sa_data, 0, wrq->u.data.length );

    wrq->u.ap_addr.sa_family = ARPHRD_ETHER;

    
    /* Assume AP mode here, which means the BSSID is our own MAC address. In
       STA mode, this address will be overwritten with the actual BSSID using
       the code below. */
    memcpy( wrq->u.ap_addr.sa_data, lp->dev->dev_addr, ETH_ALEN );


#if (HCF_TYPE) & HCF_TYPE_STA

    if( lp->DownloadFirmware != WVLAN_DRV_MODE_AP )
    {
        /* Get Current BSSID */
        lp->ltvRecord.typ = CFG_CURRENT_BSSID;
        lp->ltvRecord.len = 4;

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

        if( status == HCF_SUCCESS )
        {
            /* Copy info into sockaddr struct */
            wrq->u.data.length = ETH_ALEN;

            memcpy( wrq->u.ap_addr.sa_data, lp->ltvRecord.u.u8, wrq->u.data.length );
            
            wrq->u.ap_addr.sa_family = ARPHRD_ETHER;
        }
        else
        {
            ret = -EIO;
        }
    }

#endif  /* (HCF_TYPE) & HCF_TYPE_STA */
 
    DBG_LEAVE( DbgInfo );
    return ret;
}
/*============================================================================*/




/*******************************************************************************
 *	wireless_get_ap_list()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Gets the results of a network scan.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 *  NOTE: SIOCGIWAPLIST has been deprecated by SIOCSIWSCAN. This function
 *       implements SIOCGIWAPLIST only to provide backwards compatibility. For
 *       all systems using WIRELESS_EXT v14 and higher, use SIOCSIWSCAN!
 *
 ******************************************************************************/
int wireless_get_ap_list( struct iwreq *wrq, struct wl_private *lp )
{
    int                 ret = 0;
    int                 status = -1;
    int                 num_aps = -1;
    int                 sec_count = 0;
    hcf_32              count;
    struct sockaddr     *hwa = NULL;
    struct iw_quality   *qual = NULL;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_get_ap_list" );
    DBG_ENTER( DbgInfo );


    /* Set the completion state to FALSE */
    lp->scan_results.scan_complete = FALSE;
    lp->probe_results.scan_complete = FALSE;


    /* Channels to scan */
    lp->ltvRecord.len       = 2;
    lp->ltvRecord.typ       = CFG_SCAN_CHANNELS_2GHZ;
    lp->ltvRecord.u.u16[0]  = CNV_INT_TO_LITTLE( 0x7FFF );

    status = hcf_put_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
    DBG_TRACE( DbgInfo, "CFG_SCAN_CHANNELS_2GHZ result: 0x%x\n", status );


    /* Set the SCAN_SSID to "ANY". Using this RID for scan prevents the need to
       disassociate from the network we are currently on */
    lp->ltvRecord.len       = 2;
    lp->ltvRecord.typ       = CFG_SCAN_SSID;
    lp->ltvRecord.u.u16[0]  = CNV_INT_TO_LITTLE( 0 );
    
    status = hcf_put_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
    DBG_TRACE( DbgInfo, "CFG_SCAN_SSID to 'any' status: 0x%x\n", status );


#if (HCF_TYPE) & HCF_TYPE_HII
    /* Initiate the scan */
    status = hcf_action( &( lp->hcfCtx ), HCF_ACT_ACS_SCAN );
#else
    status = hcf_action( &( lp->hcfCtx ), HCF_ACT_SCAN );
#endif  /* (HCF_TYPE) & HCF_TYPE_HII */


    if( status == HCF_SUCCESS )
    {
        DBG_TRACE( DbgInfo, "SUCCESSFULLY INITIATED SCAN...\n" );

        do
        {
            DBG_TRACE( DbgInfo, "Waiting for scan results...\n" );

            /* Abort the scan if we've waited for more than MAX_SCAN_TIME_SEC */
            if( sec_count > MAX_SCAN_TIME_SEC )
            {
                DBG_ERROR( DbgInfo, "Scan did not complete in allotted time\n" );
                ret = -EIO;
                break;
            }

            /* Wait for 1 sec in 10ms intervals, scheduling the kernel to do
               other things in the meantime, This prevents system lockups by
               giving some time back to the kernel */
            for( count = 0; count < 100; count ++ )
            {
                mdelay( 10 );
                schedule( );
            }

            /* We've waited for another second, so increase the second count */
            sec_count++;
        }
#if (HCF_TYPE) & HCF_TYPE_HII
        while( lp->probe_results.scan_complete == FALSE );
#else
        while( lp->scan_results.scan_complete == FALSE );
#endif  /* (HCF_TYPE) & HCF_TYPE_HII */


#if (HCF_TYPE) & HCF_TYPE_HII
        if( lp->probe_results.scan_complete == FALSE )
#else
        if( lp->scan_results.scan_complete == FALSE )
#endif  /* (HCF_TYPE) & HCF_TYPE_HII */
        {
            DBG_ERROR( DbgInfo, "Unknown Error, timeout waiting for scan results? \n" );
            ret = -EIO;
        }
        else
        {
#if (HCF_TYPE) & HCF_TYPE_HII

            num_aps             = lp->probe_results.num_aps;
            wrq->u.data.length  = lp->probe_results.num_aps; 

            hwa = (struct sockaddr *)wrq->u.data.pointer;
            qual = (struct iw_quality *)wrq->u.data.pointer + 
                                        ( sizeof( struct sockaddr ) * num_aps );

            /* This flag is used to tell the user if we provide quality
               information. Since we provide signal/noise levels but no
               quality info on a scan, this is set to 0. Setting to 1 and 
               providing a quality of 0 produces weird results. If we ever
               provide quality (or can calculate it), this can be changed */
            wrq->u.data.flags = 0;

            for( count = 0; count < num_aps; count++ )
            {
                DBG_PRINT( "BSSID: %s\n", DbgHwAddr( lp->probe_results.ProbeTable[count].BSSID ));
                memcpy( hwa[count].sa_data, 
                        lp->probe_results.ProbeTable[count].BSSID, ETH_ALEN );
            }

            /* Once the data is copied to the wireless struct, invalidate the
               scan result to initiate a rescan on the next request */
            lp->probe_results.scan_complete = FALSE;

#else
            /* We got an indication that the scan has completed, so fill out the
               wireless struct */
            num_aps             = lp->scan_results.num_aps;
            wrq->u.data.length  = lp->scan_results.num_aps;

            hwa = (struct sockaddr *)wrq->u.data.pointer;
            qual = (struct iw_quality *)wrq->u.data.pointer + 
                                        ( sizeof( struct sockaddr ) * num_aps );

            /* This flag is used to tell the user if we provide quality
               information. Since we provide signal/noise levels but no
               quality info on a scan, this is set to 0. Setting to 1 and 
               providing a quality of 0 produces weird results. If we ever
               provide quality (or can calculate it), this can be changed */
            wrq->u.data.flags = 0;

            for( count = 0; count < num_aps; count++ )
            {
                memcpy( hwa[count].sa_data, lp->scan_results.APTable[count].bssid, ETH_ALEN );
            }

            /* Once the data is copied to the wireless struct, invalidate the
               scan result to initiate a rescan on the next request */
            lp->scan_results.scan_complete = FALSE;

#endif  /* (HCF_TYPE) & HCF_TYPE_HII */

            /* Send the wireless event that the scan has completed, just in case
               it's needed */
            wl_wext_event_scan_complete( lp->dev );
        }
    }


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




/*******************************************************************************
 *	wireless_set_sensitivity()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Sets the sensitivity (distance between APs) of the wireless card.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_set_sensitivity( struct iwreq *wrq, struct wl_private *lp )
{
    int ret = 0;
    int dens = wrq->u.sens.value;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_set_sensitivity" );
    DBG_ENTER( DbgInfo );


    if(( dens < 1 ) || ( dens > 3 ))
    {
        ret = -EINVAL;

        DBG_LEAVE( DbgInfo );
        return ret;
    }
	
    lp->DistanceBetweenAPs = dens;
	wl_apply( lp );

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




/*******************************************************************************
 *	wireless_get_sensitivity()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Gets the sensitivity (distance between APs) of the wireless card.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
void wireless_get_sensitivity( struct iwreq *wrq, struct wl_private *lp )
{
    DBG_FUNC( "wireless_get_sensitivity" );
    DBG_ENTER( DbgInfo );


    wrq->u.sens.value = lp->DistanceBetweenAPs;
	wrq->u.sens.fixed = 0;	/* auto */


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




/*******************************************************************************
 *	wireless_set_essid()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Sets the ESSID (network name) that the wireless device should associate
 *  with.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_set_essid( struct iwreq *wrq, struct wl_private *lp )
{
    int ret = 0;

    DBG_FUNC( "wireless_set_essid" );
    DBG_ENTER( DbgInfo );

    if( wrq->u.data.pointer == 0 )
    {
        /* Should we return some other value here? */
        DBG_LEAVE( DbgInfo );
        return ret;
    }

	/* Validate the new value */
	if( wrq->u.data.length > HCF_MAX_NAME_LEN + 1 )
	{
	    ret = -EINVAL;
        DBG_LEAVE( DbgInfo );
		return ret;
	}

	ret = verify_area( VERIFY_READ, wrq->u.data.pointer, wrq->u.data.length );
	if( ret != 0 )
	{
        DBG_LEAVE( DbgInfo );
		return ret;
    }

	memset( lp->NetworkName, 0, sizeof( lp->NetworkName ));
	
    /* wrq->u.data.flags is zero to ask for "any" */
	if( wrq->u.data.flags == 0 )
    {
        /* Need this because in STAP build PARM_DEFAULT_SSID is "LinuxAP" */
        if( lp->DownloadFirmware == WVLAN_DRV_MODE_STA )
        {
            strcpy( lp->NetworkName, "ANY" );
        }
        else
        {
            //strcpy( lp->NetworkName, "ANY" );
            strcpy( lp->NetworkName, PARM_DEFAULT_SSID );
        }
    }
	else
    {
        copy_from_user( lp->NetworkName, wrq->u.data.pointer, 
                        //( wrq->u.data.length -1 ));
                        ( wrq->u.data.length ));
    }
	    
    DBG_TRACE( DbgInfo, "set NetworkName: %s\n", lp->NetworkName );
    
    /* Commit the adapter parameters */
	wl_apply( lp );

    /* Send an event that ESSID has been set */
    wl_wext_event_essid( lp->dev );

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




/*******************************************************************************
 *	wireless_get_essid()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Gets the ESSID (network name) that the wireless device is associated
 *  with.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_get_essid( struct iwreq *wrq, struct wl_private *lp )
{
    int         ret = 0;
    int         status = -1;
    wvName_t    *pName;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_get_essid" );
    DBG_ENTER( DbgInfo );


	if( wrq->u.data.pointer == 0 )
    {
        DBG_LEAVE( DbgInfo );
        return ret;
    }

	ret = verify_area( VERIFY_WRITE, wrq->u.data.pointer, HCF_MAX_NAME_LEN );
	if( ret != 0 )
	{
        DBG_LEAVE( DbgInfo );
		return ret;
	}

	/* Get the desired network name */
	lp->ltvRecord.len = 1 + ( sizeof( *pName ) / sizeof( hcf_16 ));


#if (HCF_TYPE) & HCF_TYPE_STA

	lp->ltvRecord.typ = CFG_DESIRED_SSID;

#endif


#if (HCF_TYPE) & HCF_TYPE_AP

	if( lp->DownloadFirmware == WVLAN_DRV_MODE_AP )
	{
        lp->ltvRecord.typ = CFG_CNF_OWN_SSID;
    }

#endif // HCF_AP

	
    status = hcf_get_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
	if( status == HCF_SUCCESS )
	{
		pName = (wvName_t *)&( lp->ltvRecord.u.u32 );

        /* Endian translate the string length */
        pName->length = CNV_LITTLE_TO_INT( pName->length ); 

		/* Copy the information into the user buffer */
        wrq->u.data.length = pName->length;


        /* NOTE: Null terminating is necessary for proper display of the SSID in
           the wireless tools */
        wrq->u.data.length = pName->length + 1;
		if( pName->length < HCF_MAX_NAME_LEN )
        {
		    pName->name[pName->length] = '\0';
        }

		wrq->u.data.flags = 1;


#if (HCF_TYPE) & HCF_TYPE_STA
#ifdef RETURN_CURRENT_NETWORKNAME

		/* if desired is null ("any"), return current or "any" */
		if( pName->name[0] == '\0' )
        {
	        /* Get the current network name */
	        lp->ltvRecord.len = 1 + ( sizeof(*pName ) / sizeof( hcf_16 ));
	        lp->ltvRecord.typ = CFG_CURRENT_SSID;

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

	        if( status == HCF_SUCCESS )
	        {
		        pName = (wvName_t *)&( lp->ltvRecord.u.u32 );

                /* Endian translate the string length */
                pName->length = CNV_LITTLE_TO_INT( pName->length ); 

		        /* Copy the information into the user buffer */
		        wrq->u.data.length = pName->length + 1;
		        if( pName->length < HCF_MAX_NAME_LEN )
                {
		            pName->name[pName->length] = '\0';
                }

		        wrq->u.data.flags = 1;
	        }
		}

#endif // RETURN_CURRENT_NETWORKNAME
#endif // HCF_STA

        wrq->u.data.length--;

		copy_to_user( wrq->u.data.pointer, pName->name, pName->length + 1 );
    }
	else
	{
		ret = -EIO;
	}

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




/*******************************************************************************
 *	wireless_set_encode()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *     Sets the encryption keys and status (enable or disable).
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_set_encode( struct iwreq *wrq, struct wl_private *lp )
{
    int     ret = 0;

#if WIRELESS_EXT > 8
    hcf_8   encryption_state;
#endif // WIRELESS_EXT > 8
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_set_encode" );
    DBG_ENTER( DbgInfo );


    /* Is encryption supported? */
	if( !wl_has_wep( &( lp->hcfCtx )))
	{
        DBG_WARNING( DbgInfo, "WEP not supported on this device\n" );
        ret = -EOPNOTSUPP;
        DBG_LEAVE( DbgInfo );
        return ret;
    }
    
    DBG_NOTICE( DbgInfo, "pointer: %p, length: %d, flags: %#x\n", 
                wrq->u.encoding.pointer, wrq->u.encoding.length,
                wrq->u.encoding.flags);

    /* Save state of Encryption switch */
    encryption_state = lp->EnableEncryption;

    /* Basic checking: do we have a key to set? */
	if( wrq->u.encoding.pointer !=  0 )
	{
        int index   = ( wrq->u.encoding.flags & IW_ENCODE_INDEX ) - 1;
        int tk      = lp->TransmitKeyID - 1;		// current key

	    
        /* Check the size of the key */
        switch( wrq->u.encoding.length )
        {
        case 0:
            break;

        case MIN_KEY_SIZE:
        case MAX_KEY_SIZE:

            /* Check the index */
		    if(( index < 0 ) || ( index >= MAX_KEYS ))
            {
                index = tk;
            }

            /* Cleanup */
            memset( lp->DefaultKeys.key[index].key, 0, MAX_KEY_SIZE );
    		
            /* Copy the key in the driver */
            if( copy_from_user( lp->DefaultKeys.key[index].key,
                                wrq->u.encoding.pointer,
                                wrq->u.encoding.length ))
            {
                lp->DefaultKeys.key[index].len = 0;

                DBG_WARNING( DbgInfo, "WEP Key copy failed\n" );
                ret = -EFAULT;
                DBG_LEAVE( DbgInfo );
                return ret;
            }
            else
            {
                /* Set the length */
                lp->DefaultKeys.key[index].len = wrq->u.encoding.length;
		    }

            DBG_NOTICE( DbgInfo, "encoding.length: %d\n", wrq->u.encoding.length );
            DBG_NOTICE( DbgInfo, "set key: %s(%d) [%d]\n", lp->DefaultKeys.key[index].key,
                        lp->DefaultKeys.key[index].len, index );

		    /* Enable WEP (if possible) */
            if(( index == tk ) && ( lp->DefaultKeys.key[tk].len > 0 ))
            {
                lp->EnableEncryption = 1;
            }

            break;

        default:
            DBG_WARNING( DbgInfo, "Invalid Key length\n" );
            ret = -EINVAL;
            DBG_LEAVE( DbgInfo );
            return ret;
        }
    }
	else
	{
        int index = ( wrq->u.encoding.flags & IW_ENCODE_INDEX ) - 1;


        /* Do we want to just set the current transmit key? */
        if(( index >= 0 ) && ( index < MAX_KEYS ))
        {
            DBG_NOTICE( DbgInfo, "index: %d; len: %d\n", index,
                        lp->DefaultKeys.key[index].len );

            if( lp->DefaultKeys.key[index].len > 0 )
            {
                lp->TransmitKeyID       = index + 1;
                lp->EnableEncryption    = 1;
            }
		    else
            {
                DBG_WARNING( DbgInfo, "Problem setting the current TxKey\n" );
                DBG_LEAVE( DbgInfo );
			    ret = -EINVAL;
            }
        }
    }
    
    /* Read the flags */ 
    if( wrq->u.encoding.flags & IW_ENCODE_DISABLED )
    {
        lp->EnableEncryption = 0;	// disable encryption
    }
    else
    {
        lp->EnableEncryption = 1;
    }

	if( wrq->u.encoding.flags & IW_ENCODE_RESTRICTED )
    {
        DBG_WARNING( DbgInfo, "IW_ENCODE_RESTRICTED invalid\n" );
		ret = -EINVAL;		// Invalid
    }
    
    DBG_TRACE( DbgInfo, "encryption_state :       %d\n", encryption_state );
    DBG_TRACE( DbgInfo, "lp->EnableEncryption :   %d\n", lp->EnableEncryption );
    DBG_TRACE( DbgInfo, "wrq->u.encoding.length : %d\n", 
               wrq->u.encoding.length );
    DBG_TRACE( DbgInfo, "wrq->u.encoding.flags  : 0x%x\n", 
               wrq->u.encoding.flags );

    /* Write the changes to the card */
    if( ret == 0 )
    {
        DBG_NOTICE( DbgInfo, "encrypt: %d, ID: %d\n", lp->EnableEncryption,
                    lp->TransmitKeyID );

        if( lp->EnableEncryption == encryption_state )
        {
            if( wrq->u.encoding.length != 0 )
            {
                /* Dynamic WEP key update */
                wl_set_wep_keys( lp );
            }
        }
        else
        {
            /* To switch encryption on/off, soft reset is required */
	        wl_apply( lp );
	    }
	}
    
    /* Send an event that Encryption has been set */
    wl_wext_event_encode( lp->dev );

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




/*******************************************************************************
 *	wireless_get_encode()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *     Gets the encryption keys and status.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_get_encode( struct iwreq *wrq, struct wl_private *lp )
{
    int ret = 0;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_get_encode" );
    DBG_ENTER( DbgInfo );
    DBG_NOTICE(DbgInfo, "GIWENCODE: encrypt: %d, ID: %d\n", lp->EnableEncryption, lp->TransmitKeyID);
    

    /* Is it supported? */
    if( !wl_has_wep( &( lp->hcfCtx )))
    {
        ret = -EOPNOTSUPP;
        DBG_LEAVE( DbgInfo );
        return ret;
    }
	
    /* Only super-user can see WEP key */
    if( !capable( CAP_NET_ADMIN ))
    {
        ret = -EPERM;
        DBG_LEAVE( DbgInfo );
        return ret;
	}
	
    /* Basic checking */
    if( wrq->u.encoding.pointer != 0 )
    {
        int index = ( wrq->u.encoding.flags & IW_ENCODE_INDEX ) - 1;

		
		/* Set the flags */
        wrq->u.encoding.flags = 0;
        
        if( lp->EnableEncryption == 0 )
        {
            wrq->u.encoding.flags |= IW_ENCODE_DISABLED;
        }
		 
        /* Which key do we want */
        if(( index < 0 ) || ( index >= MAX_KEYS ))
        {
            index = lp->TransmitKeyID - 1;
        }
        
        wrq->u.encoding.flags |= index + 1;
        
        /* Copy the key to the user buffer */
        wrq->u.encoding.length = lp->DefaultKeys.key[index].len;

        if( copy_to_user( wrq->u.encoding.pointer, 
                          lp->DefaultKeys.key[index].key,
                          lp->DefaultKeys.key[index].len ))
        {
            ret = -EFAULT;
        }
    }

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




/*******************************************************************************
 *	wireless_set_nickname()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *     Sets the nickname, or station name, of the wireless device.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_set_nickname( struct iwreq *wrq, struct wl_private *lp )
{
    int ret = 0;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_set_nickname" );
    DBG_ENTER( DbgInfo );


    if( !capable(CAP_NET_ADMIN ))
    {
        ret = -EPERM;
        DBG_LEAVE( DbgInfo );
        return ret;
    }
    
    /* Validate the new value */
    if( wrq->u.data.length > HCF_MAX_NAME_LEN )
    {
        ret = -EINVAL;
        DBG_LEAVE( DbgInfo );
        return ret;
    }
    
    ret = verify_area( VERIFY_READ, wrq->u.data.pointer, wrq->u.data.length );

    if( ret != 0 )
    {
        DBG_LEAVE( DbgInfo );
        return ret;
    }
    
    memset( lp->StationName, 0, sizeof( lp->StationName ));

    copy_from_user( lp->StationName, wrq->u.data.pointer, wrq->u.data.length );

    /* Commit the adapter parameters */
    wl_apply( lp );

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




/*******************************************************************************
 *	wireless_get_nickname()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *     Gets the nickname, or station name, of the wireless device.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_get_nickname( struct iwreq *wrq, struct wl_private *lp )
{
    int         ret = 0;
    int         status = -1;
    wvName_t    *pName;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_get_nickname" );
    DBG_ENTER( DbgInfo );


    ret = verify_area( VERIFY_WRITE, wrq->u.data.pointer, HCF_MAX_NAME_LEN );

    if( ret != 0 )
    {
        DBG_LEAVE( DbgInfo );
        return ret;
    }
    
    /* Get the current station name */
    lp->ltvRecord.len = 1 + ( sizeof( *pName ) / sizeof( hcf_16 ));
    lp->ltvRecord.typ = CFG_CNF_OWN_NAME;

    status = hcf_get_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
    
    if( status == HCF_SUCCESS )
    {
        pName = (wvName_t *)&( lp->ltvRecord.u.u32 );

        /* Endian translate the length */
        pName->length = CNV_LITTLE_TO_INT( pName->length );
        
        /* Copy the information into the user buffer */
        wrq->u.data.length = pName->length + 1;

        copy_to_user( wrq->u.data.pointer, pName->name, pName->length + 1 );
    }
    else
    {
        ret = -EIO;
    }

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




/*******************************************************************************
 *	wireless_set_porttype()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *     Sets the port type of the wireless device.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_set_porttype( struct iwreq *wrq, struct wl_private *lp )
{
    int ret = 0;
    hcf_16  portType;
    hcf_16 *pData;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_set_porttype" );
    DBG_ENTER( DbgInfo );

    
    if( !capable( CAP_NET_ADMIN ))
    {
        ret = -EPERM;
        DBG_LEAVE( DbgInfo );
        return ret;
    }
    
    /* Validate the new value */
    pData       = (hcf_16 *)&( wrq->u.data.pointer );
    portType    = *pData;
    
    switch( portType )
    {
    case IW_MODE_ADHOC:

        /* When user requests ad-hoc, set IBSS mode! */
        portType             = 1;

        lp->CreateIBSS       = 1;
        lp->DownloadFirmware = 1;

        break;


    case IW_MODE_AUTO:
    case IW_MODE_INFRA:

        /* Both automatic and infrastructure set port to BSS/STA mode */
        portType             = 1;

        lp->CreateIBSS       = 0;
        lp->DownloadFirmware = 1;

        break;


#if (HCF_TYPE) & HCF_TYPE_AP   

    case IW_MODE_MASTER:

        /* Set BSS/AP mode */
        portType             = 1;

        lp->CreateIBSS       = 0;
        lp->DownloadFirmware = 2;
        
        break;

#endif /* (HCF_TYPE) & HCF_TYPE_AP */


    default:

        portType = 0;
        ret = -EINVAL;
    }
    
    if( portType != 0 )
    {
        lp->PortType = portType;
     
        /* Commit the adapter parameters */
        wl_go( lp );

        /* Send an event that mode has been set */ 
        wl_wext_event_mode( lp->dev );
    }

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




/*******************************************************************************
 *	wireless_get_porttype()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *     Gets the port type of the wireless device.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_get_porttype( struct iwreq *wrq, struct wl_private *lp )
{
    int     ret = 0;
    int     status = -1;
    hcf_16  *pPortType;
    hcf_16  *pData;
    /*------------------------------------------------------------------------*/

    
    DBG_FUNC( "wireless_get_porttype" );
    DBG_ENTER( DbgInfo );


    /* Get the current port type */
    lp->ltvRecord.len = 1 + ( sizeof( *pPortType ) / sizeof( hcf_16 ));
    lp->ltvRecord.typ = CFG_CNF_PORT_TYPE;
    
    status = hcf_get_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
    
    if( status == HCF_SUCCESS )
    {
        pPortType = (hcf_16 *)&( lp->ltvRecord.u.u32 );
        pData = (hcf_16 *)&( wrq->u.data.pointer );

        *pPortType = CNV_LITTLE_TO_INT( *pPortType );
		
        switch( *pPortType )
        {
        case 1:

#if 0
#if (HCF_TYPE) & HCF_TYPE_AP

            if( lp->DownloadFirmware == WVLAN_DRV_MODE_AP )
            {
                *pData = IW_MODE_MASTER;
            }
            else
            {
                *pData = IW_MODE_INFRA;
            }

#else

            *pData = IW_MODE_INFRA;

#endif  /* (HCF_TYPE) & HCF_TYPE_AP */
#endif

            if( lp->DownloadFirmware == WVLAN_DRV_MODE_AP )
            {
                *pData =  IW_MODE_MASTER;
            }
            else
            {
                if( lp->CreateIBSS )
                {
                    *pData = IW_MODE_ADHOC;
                }
                else
                {
                    *pData = IW_MODE_INFRA;
                }
            }

            break;


		case 3:
            *pData = IW_MODE_ADHOC;
            break;

        }
    }
    
    else
	{
        ret = -EIO;
    }

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




/*******************************************************************************
 *	wireless_set_power()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *     Sets the power management settings of the wireless device.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_set_power( struct iwreq *wrq, struct wl_private *lp )
{
    int ret = 0;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_set_power" );
    DBG_ENTER( DbgInfo );


    if( !capable( CAP_NET_ADMIN ))
    {
        ret = -EPERM;

        DBG_LEAVE( DbgInfo );
        return ret;
    }

    /* Set the power management state based on the 'disabled' value */
    if( wrq->u.power.disabled )
    {
        lp->PMEnabled = 0;
    }
    else
    {
        lp->PMEnabled = 1;
    }

    /* Commit the adapter parameters */
    wl_apply( lp );

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




/*******************************************************************************
 *	wireless_get_power()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *     Gets the power management settings of the wireless device.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
void wireless_get_power( struct iwreq *wrq, struct wl_private *lp )
{
    DBG_FUNC( "wireless_get_power" );
    DBG_ENTER( DbgInfo );


    if( lp->PMEnabled )
    {
        wrq->u.power.disabled = 0;
    }
    else
    {
        wrq->u.power.disabled = 1;
    }

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




/*******************************************************************************
 *	wireless_get_tx_power()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *     Gets the transmit power of the wireless device's radio.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
void wireless_get_tx_power( struct iwreq *wrq, struct wl_private *lp )
{
    DBG_FUNC( "wireless_get_tx_power" );
    DBG_ENTER( DbgInfo );


    wrq->u.txpower.value = RADIO_TX_POWER_MWATT;
    wrq->u.txpower.flags = IW_TXPOW_MWATT;

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




/*******************************************************************************
 *	wireless_set_rts_threshold()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *     Sets the RTS threshold for the wireless card.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_set_rts_threshold( struct iwreq *wrq, struct wl_private *lp )
{
    int ret = 0;
    int rthr = wrq->u.rts.value;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_set_rts_threshold" );
    DBG_ENTER( DbgInfo );
    

    if( wrq->u.rts.fixed == 0 )
    {
        ret = -EINVAL;
        DBG_LEAVE( DbgInfo );
        return ret;
    }

#if WIRELESS_EXT > 8
    if( wrq->u.rts.disabled )
    {
        rthr = 2347;
    }
#endif /* WIRELESS_EXT > 8 */
    
    if(( rthr < 256 ) || ( rthr > 2347 ))
    {
        ret = -EINVAL;
        DBG_LEAVE( DbgInfo );
        return ret;
    }
    
    lp->RTSThreshold = rthr;

    wl_apply( lp );

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




/*******************************************************************************
 *	wireless_get_rts_threshold()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *     Gets the RTS threshold for the wireless card.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
void wireless_get_rts_threshold( struct iwreq *wrq, struct wl_private *lp )
{
    DBG_FUNC( "wireless_get_rts_threshold" );
    DBG_ENTER( DbgInfo );


    wrq->u.rts.value = lp->RTSThreshold;

#if WIRELESS_EXT > 8

    wrq->u.rts.disabled = ( wrq->u.rts.value == 2347 );

#endif /* WIRELESS_EXT > 8 */
    
    wrq->u.rts.fixed = 1;

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





/*******************************************************************************
 *	wireless_set_rate()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Set the default data rate setting used by the wireless device.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_set_rate( struct iwreq *wrq, struct wl_private *lp )
{
    int ret = 0;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_set_rate" );
    DBG_ENTER( DbgInfo );


    /* Convert the bitrate to a required value. Rates are stored as:
            1 - Low Speed                   (1  Mb/sec)
            2 - Standard Speed              (2  Mb/sec)
            3 - Auto Rate Select (High)     (11 Mb/sec)
            4 - Medium Speed                (5  Mb/sec)
            5 - High Speed                  (11 Mb/sec)
            6 - Auto Rate Select (Standard) (2  Mb/sec)
            7 - Auto Rate Select (Medium)   (5  Mb/sec)
    */


    if( wrq->u.bitrate.value > 0 && wrq->u.bitrate.value <= 1 * MEGABIT )
    {
        lp->TxRateControl = 1;
    }
    else if( wrq->u.bitrate.value > 1 * MEGABIT && wrq->u.bitrate.value <= 2 * MEGABIT )
    {
        if( wrq->u.bitrate.fixed == 1 )
        {
            lp->TxRateControl = 2;
        }
        else
        {
            lp->TxRateControl = 6;
        }
    }
    else if( wrq->u.bitrate.value > 2 * MEGABIT && wrq->u.bitrate.value <= 5 * MEGABIT )
    {
        if( wrq->u.bitrate.fixed == 1 )
        {
            lp->TxRateControl = 4;
        }
        else
        {
            lp->TxRateControl = 7;
        }
    }
    else if( wrq->u.bitrate.value > 5 * MEGABIT && wrq->u.bitrate.value <= 11 * MEGABIT )
    {
        if( wrq->u.bitrate.fixed == 1 )
        {
            lp->TxRateControl = 5;
        }
        else
        {
            lp->TxRateControl = 3;
        }
    }
    else if( wrq->u.bitrate.fixed == 0 )
    {
        /* In this case, the user has not specified a bitrate, only the "auto"
           moniker. So, set the rate to 11Mb auto */
        lp->TxRateControl = 3;
    }
    else
    {
        wrq->u.bitrate.value = 0;
        ret = -EINVAL; 
    }


    /* Commit the adapter parameters */
    wl_apply( lp );

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




/*******************************************************************************
 *	wireless_get_rate()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Get the default data rate setting used by the wireless device.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_get_rate( struct iwreq *wrq, struct wl_private *lp )
{
    int     ret = 0;
    int     status = -1;
    __u16   *pTxRate;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_get_rate" );
    DBG_ENTER( DbgInfo );


    /* Get the current transmit rate from the adapter */
    lp->ltvRecord.len = 1 + ( sizeof( *pTxRate ) / sizeof( hcf_16 ));
    lp->ltvRecord.typ = CFG_CURRENT_TX_RATE;
        
    status = hcf_get_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
    if( status == HCF_SUCCESS )
    {
        pTxRate = (__u16 *)&( lp->ltvRecord.u.u32 );

        *pTxRate = CNV_LITTLE_TO_INT( *pTxRate );

        wrq->u.bitrate.value = *pTxRate * MEGABIT;
    }
    else
    {
        wrq->u.bitrate.value = 0;
        ret = -EINVAL; 
    }

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




/*******************************************************************************
 *	wireless_get_private_interface()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Returns the Linux Wireless Extensions' compatible private interface of 
 *  the driver.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_get_private_interface( struct iwreq *wrq, struct wl_private *lp )
{
    int ret = 0;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_get_private_interface" );
    DBG_ENTER( DbgInfo );


    if( wrq->u.data.pointer != NULL )
    {
		struct iw_priv_args priv[] =
        {
            { SIOCSIWNETNAME, IW_PRIV_TYPE_CHAR | HCF_MAX_NAME_LEN, 0, "snetwork_name" },
            { SIOCGIWNETNAME, 0, IW_PRIV_TYPE_CHAR | HCF_MAX_NAME_LEN, "gnetwork_name" },
            { SIOCSIWSTANAME, IW_PRIV_TYPE_CHAR | HCF_MAX_NAME_LEN, 0, "sstation_name" },
            { SIOCGIWSTANAME, 0, IW_PRIV_TYPE_CHAR | HCF_MAX_NAME_LEN, "gstation_name" },
            { SIOCSIWPORTTYPE, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "sport_type" },
            { SIOCGIWPORTTYPE, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "gport_type" },
        };
        
        /* Verify the user buffer */
        ret = verify_area( VERIFY_WRITE, wrq->u.data.pointer, sizeof( priv ));

        if( ret != 0 )
        {
            DBG_LEAVE( DbgInfo );
            return ret;
        }
        
        /* Copy the data into the user's buffer */
        wrq->u.data.length = NELEM( priv );
        copy_to_user( wrq->u.data.pointer, &priv, sizeof( priv ));
    }

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



#if WIRELESS_EXT > 13

/*******************************************************************************
 *	wireless_set_scan()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Instructs the driver to initiate a network scan.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_set_scan( struct iwreq *wrq, struct wl_private *lp )
{
    int                 ret = 0;
    int                 status = -1;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_set_scan" );
    DBG_ENTER( DbgInfo );

    /* Set the completion state to FALSE */
    lp->probe_results.scan_complete = FALSE;


    /* Channels to scan */
    lp->ltvRecord.len       = 2;
    lp->ltvRecord.typ       = CFG_SCAN_CHANNELS_2GHZ; // need a define in MDD.h
    lp->ltvRecord.u.u16[0]  = CNV_INT_TO_LITTLE( 0x7FFF );

    status = hcf_put_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
    DBG_TRACE( DbgInfo, "CFG_SCAN_CHANNELS_2GHZ result: 0x%x\n", status );


    /* Set the SCAN_SSID to "ANY". Using this RID for scan prevents the need to
       disassociate from the network we are currently on */
    lp->ltvRecord.len       = 2;
    lp->ltvRecord.typ       = CFG_SCAN_SSID;
    lp->ltvRecord.u.u16[0]  = CNV_INT_TO_LITTLE( 0 );
    
    status = hcf_put_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
    DBG_TRACE( DbgInfo, "CFG_SCAN_SSID to 'any' status: 0x%x\n", status );

    /* Initiate the scan */
    /* NOTE: Using HCF_ACT_SCAN has been removed, as using HCF_ACT_ACS_SCAN to
       retrieve probe responses must always be used to support WPA */
    status = hcf_action( &( lp->hcfCtx ), HCF_ACT_ACS_SCAN );

    if( status == HCF_SUCCESS )
    {
        DBG_TRACE( DbgInfo, "SUCCESSFULLY INITIATED SCAN...\n" );
    }

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




/*******************************************************************************
 *	wireless_get_scan()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Instructs the driver to gather and return the results of a network scan.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
int wireless_get_scan( struct iwreq *wrq, struct wl_private *lp )
{
    int                 ret = 0;
    int                 count;
    hcf_8               *buf;
    hcf_8               *buf_end;
    struct iw_event     iwe;
    PROBE_RESP          *probe_resp;
    hcf_8               msg[512];
    hcf_8               *wpa_ie;
    hcf_16              wpa_ie_len;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wireless_get_scan" );
    DBG_ENTER( DbgInfo );


    /* If the scan is not done, tell the calling process to try again later */
    if( !lp->probe_results.scan_complete )
    {
        DBG_LEAVE( DbgInfo );
        return -EAGAIN;
    }

    DBG_TRACE( DbgInfo, "SCAN COMPLETE, Num of APs: %d\n", 
               lp->probe_results.num_aps );

    buf     = wrq->u.data.pointer;
    buf_end = buf + IW_SCAN_MAX_DATA;

    for( count = 0; count < lp->probe_results.num_aps; count++ )
    {
        /* Reference the probe response from the table */
        probe_resp = (PROBE_RESP *)&lp->probe_results.ProbeTable[count];


        /* First entry MUST be the MAC address */
        memset( &iwe, 0, sizeof( iwe ));

        iwe.cmd                 = SIOCGIWAP;
        iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
        memcpy( iwe.u.ap_addr.sa_data, probe_resp->BSSID, ETH_ALEN );
        iwe.len                 = IW_EV_ADDR_LEN;

        buf = iwe_stream_add_event( buf, buf_end, &iwe, IW_EV_ADDR_LEN );


        /* Use the mode to indicate if it's a station or AP */
        /* Won't always be an AP if in IBSS mode */
        memset( &iwe, 0, sizeof( iwe ));

        iwe.cmd = SIOCGIWMODE;

        if( probe_resp->capability & CAPABILITY_IBSS )
        {
            iwe.u.mode = IW_MODE_INFRA;
        }
        else
        {
            iwe.u.mode = IW_MODE_MASTER;
        }

        iwe.len = IW_EV_UINT_LEN;

        buf = iwe_stream_add_event( buf, buf_end, &iwe, IW_EV_UINT_LEN );


        /* Any quality information */
        memset( &iwe, 0, sizeof( iwe ));

        iwe.cmd             = IWEVQUAL;
        iwe.u.qual.level    = dbm( probe_resp->signal );
        iwe.u.qual.noise    = dbm( probe_resp->silence );
        iwe.u.qual.qual     = iwe.u.qual.level - iwe.u.qual.noise;
        iwe.u.qual.updated  = lp->probe_results.scan_complete;
        iwe.len             = IW_EV_QUAL_LEN;

        buf = iwe_stream_add_event( buf, buf_end, &iwe, IW_EV_QUAL_LEN );


        /* ESSID information */
        if( probe_resp->rawData[1] > 0 )
        {
            memset( &iwe, 0, sizeof( iwe ));

            iwe.cmd = SIOCGIWESSID;
            iwe.u.data.length = probe_resp->rawData[1];
            iwe.u.data.flags = 1;

            buf = iwe_stream_add_point( buf, buf_end, &iwe, &probe_resp->rawData[2] );
        }


        /* Encryption Information */
        memset( &iwe, 0, sizeof( iwe ));
        
        iwe.cmd             = SIOCGIWENCODE;
        iwe.u.data.length   = 0;

        /* Check the capabilities field of the Probe Response to see if
           'privacy' is supported on the AP in question */
        if( probe_resp->capability & CAPABILITY_PRIVACY )
        {
            iwe.u.data.flags |= IW_ENCODE_ENABLED;
        }
        else
        {
            iwe.u.data.flags |= IW_ENCODE_DISABLED;
        }

        buf = iwe_stream_add_point( buf, buf_end, &iwe, NULL );

        
        /* Frequency Info */
        memset( &iwe, 0, sizeof( iwe ));

        iwe.cmd = SIOCGIWFREQ;
        iwe.len = IW_EV_FREQ_LEN;
        iwe.u.freq.m = wl_parse_ds_ie( probe_resp );
        iwe.u.freq.e = 0;

        buf = iwe_stream_add_event( buf, buf_end, &iwe, IW_EV_FREQ_LEN );


#if WIRELESS_EXT > 14 
        /* Custom info (Beacon Interval) */
        memset( &iwe, 0, sizeof( iwe ));
        memset( msg, 0, sizeof( msg ));

        iwe.cmd = IWEVCUSTOM;
        sprintf( msg, "beacon_interval=%d", probe_resp->beaconInterval );
        iwe.u.data.length = strlen( msg );

        buf = iwe_stream_add_point( buf, buf_end, &iwe, msg );
        

        /* Custom info (WPA-IE) */
        wpa_ie = NULL;
        wpa_ie_len = 0;

        wpa_ie = wl_parse_wpa_ie( probe_resp, &wpa_ie_len );
        if( wpa_ie != NULL )
        {
            memset( &iwe, 0, sizeof( iwe ));
            memset( msg, 0, sizeof( msg ));

            iwe.cmd = IWEVCUSTOM;
            sprintf( msg, "wpa_ie=%s", wl_print_wpa_ie( wpa_ie, wpa_ie_len ));
            iwe.u.data.length = strlen( msg );

            buf = iwe_stream_add_point( buf, buf_end, &iwe, msg );
        }

        /* Add other custom info in formatted string format as needed... */
#endif
    }

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

#endif  // WIRELESS_EXT > 13




/*******************************************************************************
 *	wl_wireless_stats()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Return the current device wireless statistics.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
struct iw_statistics * wl_wireless_stats( struct net_device *dev )
{
    struct iw_statistics    *pStats;
    struct wl_private       *lp;
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wl_wireless_stats" );
    DBG_ENTER(DbgInfo);
    DBG_PARAM(DbgInfo, "dev", "%s (0x%p)", dev->name, dev);

    pStats = NULL;

    if(( lp = ( struct wl_private * ) dev->priv ) != NULL )
    {
        /* Initialize the statistics */
        pStats                  = &( lp->wstats );
        pStats->qual.updated    = 0x00;

        if( !( lp->flags & WVLAN2_UIL_BUSY ))
        {
            CFG_COMMS_QUALITY_STRCT    *pQual;
            CFG_HERMES_TALLIES_STRCT   *pTallies;
            int                         status;

            /* Update driver status */
            pStats->status = 0;

            /* Get the current link quality information */
            lp->ltvRecord.len = 1 + ( sizeof( *pQual ) / sizeof( hcf_16 ));
            lp->ltvRecord.typ = CFG_COMMS_QUALITY;

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

            if( status == HCF_SUCCESS )
            {
                pQual = (CFG_COMMS_QUALITY_STRCT *)&( lp->ltvRecord );

#ifdef USE_DBM
                pStats->qual.qual  = (u_char) CNV_LITTLE_TO_INT( pQual->coms_qual );
                pStats->qual.level = (u_char) dbm( CNV_LITTLE_TO_INT( pQual->signal_lvl ));
                pStats->qual.noise = (u_char) dbm( CNV_LITTLE_TO_INT( pQual->noise_lvl ));
#else
                pStats->qual.qual = percent( CNV_LITTLE_TO_INT( pQual->coms_qual ),
                                             HCF_MIN_COMM_QUALITY,
                                             HCF_MAX_COMM_QUALITY );

                pStats->qual.level = percent( CNV_LITTLE_TO_INT( pQual->signal_lvl ),
                                              HCF_MIN_SIGNAL_LEVEL,
                                              HCF_MAX_SIGNAL_LEVEL );

                pStats->qual.noise = percent( CNV_LITTLE_TO_INT( pQual->noise_lvl ),
                                              HCF_MIN_NOISE_LEVEL,
                                              HCF_MAX_NOISE_LEVEL );
#endif /* USE_DBM */

                pStats->qual.updated |= 0x07;
            }
            else
            {
                memset( &( pStats->qual ), 0, sizeof( pStats->qual ));
            }

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

                /* No endian translation is needed here, as CFG_TALLIES is an
                   MSF RID; all processing is done on the host, not the card! */
                pStats->discard.nwid = 0L;
                pStats->discard.code = pTallies->RxWEPUndecryptable;
                pStats->discard.misc = pTallies->TxDiscards + 
                                       pTallies->RxFCSErrors + 
                                       pTallies->RxDiscards_NoBuffer +
                                       pTallies->TxDiscardsWrongSA;
            }
            else
            {
                memset( &( pStats->discard ), 0, sizeof( pStats->discard ));
            }
        }
    }

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




/*******************************************************************************
 *	wl_get_wireless_stats()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Return the current device wireless statistics. This function calls
 *      wl_wireless_stats, but acquires spinlocks first as it can be called
 *      directly by the network layer.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
struct iw_statistics * wl_get_wireless_stats( struct net_device *dev )
{
    unsigned long           flags;
    struct wl_private       *lp;
    struct iw_statistics    *pStats = NULL;  
    /*------------------------------------------------------------------------*/


    DBG_FUNC( "wl_get_wireless_stats" );
    DBG_ENTER(DbgInfo);


    if(( lp = ( struct wl_private * ) dev->priv ) != NULL )
    {
        spin_lock_irqsave( &( lp->slock ), flags );
        hcf_action( &( lp->hcfCtx ), HCF_ACT_INT_OFF );

#ifdef USE_RTS
        if( lp->useRTS == 1 )
        {
            DBG_TRACE( DbgInfo, "Skipping wireless stats, in RTS mode\n" );
            spin_unlock_irqrestore( &( lp->slock ), flags );
            
            DBG_LEAVE( DbgInfo );
            return NULL;
        }
#endif

        pStats = wl_wireless_stats( dev );

        hcf_action( &( lp->hcfCtx ), HCF_ACT_INT_ON );
        spin_unlock_irqrestore( &( lp->slock ), flags );
    }

    DBG_LEAVE( DbgInfo );
    return pStats;
}

/*******************************************************************************
 *	wl_spy_gather()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      Gather wireless spy statistics.
 *
 *  PARAMETERS:
 *
 *      wrq - the wireless request buffer
 *      lp  - the device's private adapter structure
 *
 *  RETURNS:
 *
 *      0 on success
 *      errno value otherwise
 *
 ******************************************************************************/
inline void wl_spy_gather( struct net_device *dev, u_char *mac )
{
    int                     i; 
    int                     status;
    u_char                  stats[2];
    DESC_STRCT              desc[1];
    struct wl_private   *lp = (struct wl_private *)dev->priv;
    /*------------------------------------------------------------------------*/


    /* Gather wireless spy statistics: for each packet, compare the source 
       address with out list, and if match, get the stats. */
    for( i=0; i < lp->spy_number; i++ )
    {
        if( !memcmp( mac, lp->spy_address[i], ETH_ALEN ))
      	{
            memset( &stats, 0, sizeof( stats ));
            memset( &desc, 0, sizeof( DESC_STRCT ));

            desc[0].buf_addr        = stats;
            desc[0].BUF_SIZE        = sizeof( stats );
            desc[0].next_desc_addr  = 0;				// terminate list

            status = hcf_rcv_msg( &( lp->hcfCtx ), &desc[0], 0 );

            if( status == HCF_SUCCESS )
            {
                lp->spy_stat[i].level = (u_char) dbm(stats[1]);
                lp->spy_stat[i].noise = (u_char) dbm(stats[0]);
                lp->spy_stat[i].qual  = lp->spy_stat[i].level - 
                                        lp->spy_stat[i].noise;
                lp->spy_stat[i].updated = 7;
            }

            break;
        }
    }
}
/*============================================================================*/




/*******************************************************************************
 *	wl_wext_event_freq()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      This function is used to send an event that the channel/freq
 *      configuration for a specific device has changed.
 *      
 *
 *  PARAMETERS:
 *
 *      dev - the network device for which this event is to be issued
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_wext_event_freq( struct net_device *dev )
{
#if WIRELESS_EXT > 13
    union iwreq_data wrqu;
    struct wl_private *lp = NULL;
    /*------------------------------------------------------------------------*/


    lp = (struct wl_private *)dev->priv;
    if( lp == NULL )
    {
        return;
    }
    
	memset( &wrqu, 0, sizeof( wrqu ));

    wrqu.freq.m = lp->Channel;
    wrqu.freq.e = 0;

	wireless_send_event( dev, SIOCSIWFREQ, &wrqu, NULL );
#endif /* WIRELESS_EXT > 13 */

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




/*******************************************************************************
 *	wl_wext_event_mode()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      This function is used to send an event that the mode of operation
 *      for a specific device has changed.
 *      
 *
 *  PARAMETERS:
 *
 *      dev - the network device for which this event is to be issued
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_wext_event_mode( struct net_device *dev )
{
    #if WIRELESS_EXT > 13
    union iwreq_data wrqu;
    struct wl_private *lp = NULL;
    /*------------------------------------------------------------------------*/


    lp = (struct wl_private *)dev->priv;
    if( lp == NULL )
    {
        return;
    }
    
	memset( &wrqu, 0, sizeof( wrqu ));

    if( lp->DownloadFirmware == WVLAN_DRV_MODE_STA )
    {
        wrqu.mode = IW_MODE_INFRA;
    }
    else
    {
        wrqu.mode = IW_MODE_MASTER;
    }

	wireless_send_event( dev, SIOCSIWMODE, &wrqu, NULL );
#endif /* WIRELESS_EXT > 13 */

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




/*******************************************************************************
 *	wl_wext_event_essid()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      This function is used to send an event that the ESSID configuration for 
 *      a specific device has changed.
 *      
 *
 *  PARAMETERS:
 *
 *      dev - the network device for which this event is to be issued
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_wext_event_essid( struct net_device *dev )
{
#if WIRELESS_EXT > 13
    union iwreq_data wrqu;
    struct wl_private *lp = NULL;
    /*------------------------------------------------------------------------*/

    
    lp = (struct wl_private *)dev->priv;
    if( lp == NULL )
    {
        DBG_PRINT( "lp is NULL!\n" );
        return;
    }
    
	memset( &wrqu, 0, sizeof( wrqu ));

    /* Fill out the buffer. Note that the buffer doesn't actually contain the
       ESSID, but a pointer to the contents. In addition, the 'extra' field of
       the call to wireless_send_event() must also point to where the ESSID 
       lives */
    wrqu.essid.length  = strlen( lp->NetworkName );
    wrqu.essid.pointer = (caddr_t)lp->NetworkName;
    wrqu.essid.flags   = 1;

	wireless_send_event( dev, SIOCSIWESSID, &wrqu, lp->NetworkName );
#endif /* WIRELESS_EXT > 13 */

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




/*******************************************************************************
 *	wl_wext_event_encode()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      This function is used to send an event that the encryption configuration
 *      for a specific device has changed.
 *      
 *
 *  PARAMETERS:
 *
 *      dev - the network device for which this event is to be issued
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_wext_event_encode( struct net_device *dev )
{
#if WIRELESS_EXT > 13
    union iwreq_data wrqu;
    struct wl_private *lp = NULL;
    int index = 0;
    /*------------------------------------------------------------------------*/

    
    lp = (struct wl_private *)dev->priv;
    if( lp == NULL )
    {
        DBG_PRINT( "lp is NULL!\n" );
        return;
    }
    
	memset( &wrqu, 0, sizeof( wrqu ));

    if( lp->EnableEncryption == 0 )
    {
        wrqu.encoding.flags = IW_ENCODE_DISABLED;
    }
    else
    {
        wrqu.encoding.flags |= lp->TransmitKeyID;

        index = lp->TransmitKeyID - 1;

        /* Only set IW_ENCODE_RESTRICTED/OPEN flag using lp->ExcludeUnencrypted
           if we're in AP mode */
#if (HCF_TYPE) & HCF_TYPE_AP

        if( lp->DownloadFirmware == WVLAN_DRV_MODE_AP )
        {
            if( lp->ExcludeUnencrypted )
            {
                wrqu.encoding.flags |= IW_ENCODE_RESTRICTED;
            }
            else
            {
                wrqu.encoding.flags |= IW_ENCODE_OPEN;
            }
        }

#endif  // HCF_TYPE_AP

        /* Only provide the key if permissions allow */
        if( capable( CAP_NET_ADMIN ))
        {
            wrqu.encoding.pointer = (caddr_t)lp->DefaultKeys.key[index].key;
            wrqu.encoding.length  = lp->DefaultKeys.key[index].len;
        }
        else
        {
            wrqu.encoding.flags |= IW_ENCODE_NOKEY;
        }
    }

	wireless_send_event( dev, SIOCSIWENCODE, &wrqu,
                         lp->DefaultKeys.key[index].key );
#endif /* WIRELESS_EXT > 13 */

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




/*******************************************************************************
 *	wl_wext_event_ap()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      This function is used to send an event that the device has been
 *      associated to a new AP.
 *      
 *
 *  PARAMETERS:
 *
 *      dev - the network device for which this event is to be issued
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_wext_event_ap( struct net_device *dev )
{
#if WIRELESS_EXT > 13
	union iwreq_data wrqu;
    struct wl_private *lp = NULL;
    int status;
    /*------------------------------------------------------------------------*/


    lp = (struct wl_private *)dev->priv;
    if( lp == NULL )
    {
        DBG_PRINT( "lp is NULL!\n" );
        return;
    }

    /* Retrieve the WPA-IEs used by the firmware and sned an event. We must send 
       this event BEFORE sending the association event, as there are timing
       issues with the hostap supplicant. The supplicant will attempt to process
       an EAPOL-Key frame from an AP before receiving this information, which
       is required properly process the said frame. */
    wl_wext_event_assoc_ie( dev );

    /* Get the BSSID */
    lp->ltvRecord.typ = CFG_CURRENT_BSSID;
    lp->ltvRecord.len = 4;

    status = hcf_get_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
    if( status == HCF_SUCCESS )
    {
        memset( &wrqu, 0, sizeof( wrqu ));

        memcpy( wrqu.addr.sa_data, lp->ltvRecord.u.u8, ETH_ALEN );
	    wrqu.addr.sa_family = ARPHRD_ETHER;
	    wireless_send_event( dev, SIOCGIWAP, &wrqu, NULL );
    }

#endif /* WIRELESS_EXT > 13 */

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




/*******************************************************************************
 *	wl_wext_event_scan_complete()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      This function is used to send an event that a request for a network scan
 *      has completed.
 *      
 *
 *  PARAMETERS:
 *
 *      dev - the network device for which this event is to be issued
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_wext_event_scan_complete( struct net_device *dev )
{
#if WIRELESS_EXT > 13
    union iwreq_data wrqu;
    /*------------------------------------------------------------------------*/


	memset( &wrqu, 0, sizeof( wrqu ));

    wrqu.addr.sa_family = ARPHRD_ETHER;
	wireless_send_event( dev, SIOCGIWSCAN, &wrqu, NULL );
#endif /* WIRELESS_EXT > 13 */

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




/*******************************************************************************
 *	wl_wext_event_new_sta()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      This function is used to send an event that an AP has registered a new
 *      station.
 *      
 *
 *  PARAMETERS:
 *
 *      dev - the network device for which this event is to be issued
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_wext_event_new_sta( struct net_device *dev )
{
#if WIRELESS_EXT > 14
	union iwreq_data wrqu;
    /*------------------------------------------------------------------------*/


	memset( &wrqu, 0, sizeof( wrqu ));

    /* Send the station's mac address here */
    memcpy( wrqu.addr.sa_data, dev->dev_addr, ETH_ALEN );
	wrqu.addr.sa_family = ARPHRD_ETHER;
	wireless_send_event( dev, IWEVREGISTERED, &wrqu, NULL );
#endif /* WIRELESS_EXT > 14 */

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




/*******************************************************************************
 *	wl_wext_event_expired_sta()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      This function is used to send an event that an AP has deregistered a
 *      station.
 *      
 *
 *  PARAMETERS:
 *
 *      dev - the network device for which this event is to be issued
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_wext_event_expired_sta( struct net_device *dev )
{
#if WIRELESS_EXT > 14
	union iwreq_data wrqu;
    /*------------------------------------------------------------------------*/


	memset( &wrqu, 0, sizeof( wrqu ));

	memcpy( wrqu.addr.sa_data, dev->dev_addr, ETH_ALEN );
	wrqu.addr.sa_family = ARPHRD_ETHER;
	wireless_send_event( dev, IWEVEXPIRED, &wrqu, NULL );
#endif /* WIRELESS_EXT > 14 */

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




/*******************************************************************************
 *	wl_wext_event_mic_failed()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      This function is used to send an event that MIC calculations failed.
 *      
 *
 *  PARAMETERS:
 *
 *      dev - the network device for which this event is to be issued
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_wext_event_mic_failed( struct net_device *dev )
{
#if WIRELESS_EXT > 14
    char               msg[512];
	union iwreq_data   wrqu;
    struct wl_private *lp;
    int                key_idx;
    char              *addr1;
    char              *addr2;
    WVLAN_RX_WMP_HDR  *hdr; 
    /*------------------------------------------------------------------------*/


    lp = (struct wl_private *)dev->priv;
    if( lp == NULL )
    {
        DBG_PRINT( "lp is NULL!\n" );
        return;
    }


    key_idx = lp->lookAheadBuf[HFS_STAT+1] >> 3;
    key_idx &= 0x03;

    /* Cast the lookahead buffer into a RFS format */
    hdr = (WVLAN_RX_WMP_HDR *)&lp->lookAheadBuf[HFS_STAT];

    /* Cast the addresses to byte buffers, as in the above RFS they are word
       length */
    addr1 = (char *)hdr->address1;
    addr2 = (char *)hdr->address2;

    DBG_PRINT( "MIC FAIL - KEY USED : %d, STATUS : 0x%04x\n", key_idx,
               hdr->status );

	memset( &wrqu, 0, sizeof( wrqu ));
    memset( msg, 0, sizeof( msg ));


    /* Becuase MIC failures are not part of the Wireless Extensions yet, they
       must be passed as a string using an IWEVCUSTOM event. In order for the 
       event to be effective, the string format must be known by both the
       driver and the supplicant. The following is the string format used by the
       hostap project's WPA supplicant, and will be used here until the Wireless
       Extensions interface adds this support:
       
       MLME-MICHAELMICFAILURE.indication(keyid=# broadcast/unicast addr=addr2)
   */

    /* NOTE: Format of MAC address (using colons to seperate bytes) may cause
             a problem in future versions of the supplicant, if they ever
             actually parse these parameters */
    sprintf( msg, "MLME-MICHAELMICFAILURE.indication(keyid=%d %scast addr="
             "%s)", key_idx, addr1[0] & 0x01 ? "broad" : "uni",
             DbgHwAddr( addr2 ));
    wrqu.data.length = strlen( msg );
	wireless_send_event( dev, IWEVCUSTOM, &wrqu, msg );
#endif /* WIRELESS_EXT > 14 */

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




/*******************************************************************************
 *	wl_wext_event_assoc_ie()
 *******************************************************************************
 *
 *  DESCRIPTION:
 *
 *      This function is used to send an event containing the WPA-IE generated
 *      by the firmware in an association request.
 *      
 *
 *  PARAMETERS:
 *
 *      dev - the network device for which this event is to be issued
 *
 *  RETURNS:
 *
 *      N/A
 *
 ******************************************************************************/
void wl_wext_event_assoc_ie( struct net_device *dev )
{
#if WIRELESS_EXT > 14
    char               msg[512];
	union iwreq_data   wrqu;
    struct wl_private *lp;
    int status;
    PROBE_RESP         data;
    hcf_16             length;
    hcf_8              *wpa_ie;
    /*------------------------------------------------------------------------*/


    lp = (struct wl_private *)dev->priv;
    if( lp == NULL )
    {
        DBG_PRINT( "lp is NULL!\n" );
        return;
    }

	memset( &wrqu, 0, sizeof( wrqu ));
    memset( msg, 0, sizeof( msg ));

    /* Retrieve the Association Request IE */
    lp->ltvRecord.len = 45;
    lp->ltvRecord.typ = CFG_CUR_ASSOC_REQ_INFO;

    status = hcf_get_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
    if( status == HCF_SUCCESS )
    {
        length = 0;
        memcpy( &data.rawData, &( lp->ltvRecord.u.u8[1] ), 88 );
        wpa_ie = wl_parse_wpa_ie( &data, &length );

        /* Becuase this event (Association WPA-IE) is not part of the Wireless
        Extensions yet, it must be passed as a string using an IWEVCUSTOM event.
        In order for the event to be effective, the string format must be known
        by both the driver and the supplicant. The following is the string format
        used by the hostap project's WPA supplicant, and will be used here until 
        the Wireless Extensions interface adds this support:
           
        ASSOCINFO(ReqIEs=WPA-IE RespIEs=WPA-IE)
        */

        if( length != 0 )
        {
            sprintf( msg, "ASSOCINFO(ReqIEs=%s)", wl_print_wpa_ie( wpa_ie, length ));
	        wrqu.data.length = strlen( msg );
            wireless_send_event( dev, IWEVCUSTOM, &wrqu, msg );
        }
    }
#endif /* WIRELESS_EXT > 14 */

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

#endif // WIRELESS_EXT
