/*******************************************************************************
 * 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: nico $
 * $Date: 2004/08/03 11:39:39 $
 * $Revision: 1.6 $
 * $Source: /usr/local/cvs/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
 ******************************************************************************/
#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 1 //;? (HCF_TYPE) & HCF_TYPE_STA
//;?seems reasonable that even an AP-only driver could afford this small additional footprint
					//;?should we return an error status in AP mode
		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;
} // wl_wireless_ioctl
/*============================================================================*/




/*******************************************************************************
 *	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_get_protocol
/*============================================================================*/




/*******************************************************************************
 *	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;
		channel = wl_get_chan_from_freq( f );
	}


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

 
	/* If the channel is an 802.11a channel, set Bit 8 */
	if( channel > 14 ) {
		channel = channel | 0x100;
	}


	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_set_frequency
/*============================================================================*/




/*******************************************************************************
 *	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 ret = -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_CUR_CHANNEL;
	
	ret = hcf_get_info( &(lp->hcfCtx), (LTVP)&( lp->ltvRecord ));
	if( ret == HCF_SUCCESS ) {
		hcf_16 channel = CNV_LITTLE_TO_INT( lp->ltvRecord.u.u16[0] );

#ifdef USE_FREQUENCY

		wrq->u.freq.m = wl_get_freq_from_chan( channel ) * 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_frequency
/*============================================================================*/




/*******************************************************************************
 *	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;
		lp->ltvRecord.typ = CFG_CUR_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_get_range
/*============================================================================*/




/*******************************************************************************
 *	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_set_spy_addrs
/*============================================================================*/




/*******************************************************************************
 *	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_spy_addrs
/*============================================================================*/




/*******************************************************************************
 *	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 1 //;? (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 1 //;? (HCF_TYPE) & HCF_TYPE_STA
					//;?should we return an error status in AP mode

	if ( CNV_INT_TO_LITTLE( lp->hcfCtx.IFB_FWIdentity.comp_id ) == COMP_ID_FW_STA  ) {
		/* Get Current BSSID */
		//lp->ltvRecord.typ = CFG_CURRENT_BSSID;
		lp->ltvRecord.typ = CFG_CUR_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_bssid
/*============================================================================*/




/*******************************************************************************
 *	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;
	int                 num_aps = -1;
	int                 sec_count = 0;
	hcf_32              count;
	struct sockaddr     *hwa = NULL;
	struct iw_quality   *qual = NULL;
#ifdef WARP
	ScanResult			*p = &lp->scan_results;
#else
	ProbeResult         *p = &lp->probe_results;
#endif  // WARP
	/*------------------------------------------------------------------------*/

	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 );
	ret = hcf_put_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
	DBG_TRACE( DbgInfo, "CFG_SCAN_CHANNELS_2GHZ result: 0x%x\n", ret );

	/* 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 );
	ret = hcf_put_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
	DBG_TRACE( DbgInfo, "CFG_SCAN_SSID to 'any' ret: 0x%x\n", ret );

	/* Initiate the scan */
#ifdef WARP 
	ret = hcf_action( &( lp->hcfCtx ), MDD_ACT_SCAN );
#else
	ret = hcf_action( &( lp->hcfCtx ), HCF_ACT_ACS_SCAN );
#endif  // WARP
	if( ret == HCF_SUCCESS ) {
		DBG_TRACE( DbgInfo, "SUCCESSFULLY INITIATED SCAN...\n" );
		while( (*p).scan_complete == FALSE && ret == HCF_SUCCESS ) {
			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 ) {
				ret = -EIO;
			} else {
				/* 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( );
				}
			}
		}
		if ( ret != HCF_SUCCESS ) {
			DBG_ERROR( DbgInfo, "timeout waiting for scan results\n" );
		} else {
			num_aps             = (*p)/*lp->probe_results*/.num_aps;
			wrq->u.data.length  = (*p)/*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++ ) {
#ifdef WARP
				memcpy( hwa[count].sa_data, 
						(*p)/*lp->scan_results*/.APTable[count].bssid, ETH_ALEN );
#else  //;?why use BSSID and bssid as names in seemingly very comparable situations
				DBG_PRINT( "BSSID: %s\n", DbgHwAddr( (*p)/*lp->probe_results*/.ProbeTable[count].BSSID ));
				memcpy( hwa[count].sa_data, 
						(*p)/*lp->probe_results*/.ProbeTable[count].BSSID, ETH_ALEN );
#endif // WARP
			}
			/* Once the data is copied to the wireless struct, invalidate the
			   scan result to initiate a rescan on the next request */
			(*p)/*lp->probe_results*/.scan_complete = FALSE;
			/* 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_get_ap_list
/*============================================================================*/




/*******************************************************************************
 *	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_set_sensitivity
/*============================================================================*/




/*******************************************************************************
 *	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_get_sensitivity
/*============================================================================*/




/*******************************************************************************
 *	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"
		 * ;?but there ain't no STAP anymore*/
		if ( CNV_INT_TO_LITTLE( lp->hcfCtx.IFB_FWIdentity.comp_id ) == COMP_ID_FW_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 );
	}
		
	DBG_NOTICE( 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_set_essid
/*============================================================================*/




/*******************************************************************************
 *	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 1 //;? (HCF_TYPE) & HCF_TYPE_STA
					//;?should we return an error status in AP mode

	lp->ltvRecord.typ = CFG_DESIRED_SSID;

#endif


#if 1 //;? (HCF_TYPE) & HCF_TYPE_AP
		//;?should we restore this to allow smaller memory footprint

	if ( CNV_INT_TO_LITTLE( lp->hcfCtx.IFB_FWIdentity.comp_id ) == COMP_ID_FW_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 1 //;? (HCF_TYPE) & HCF_TYPE_STA
					//;?should we return an error status in AP mode
#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;
			lp->ltvRecord.typ = CFG_CUR_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_get_essid
/*============================================================================*/




/*******************************************************************************
 *	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_set_encode
/*============================================================================*/




/*******************************************************************************
 *	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_get_encode
/*============================================================================*/




/*******************************************************************************
 *	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_set_nickname
/*============================================================================*/




/*******************************************************************************
 *	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_get_nickname
/*============================================================================*/




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

		break;


#if 0 //;? (HCF_TYPE) & HCF_TYPE_AP   

	case IW_MODE_MASTER:

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

		lp->CreateIBSS       = 0;
		lp->DownloadFirmware = WVLAN_DRV_MODE_AP; //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_set_porttype
/*============================================================================*/




/*******************************************************************************
 *	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 ( CNV_INT_TO_LITTLE( lp->hcfCtx.IFB_FWIdentity.comp_id ) == COMP_ID_FW_AP  ) {
				*pData = IW_MODE_MASTER;
			} else {
				*pData = IW_MODE_INFRA;
			}

#else

			*pData = IW_MODE_INFRA;

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

			if ( CNV_INT_TO_LITTLE( lp->hcfCtx.IFB_FWIdentity.comp_id ) == COMP_ID_FW_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_get_porttype
/*============================================================================*/




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

	DBG_PRINT( "THIS CORRUPTS PMEnabled ;?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" );

	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_set_power
/*============================================================================*/




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

	DBG_PRINT( "THIS IS PROBABLY AN OVER-SIMPLIFICATION ;?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" );

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

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




/*******************************************************************************
 *	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_get_tx_power
/*============================================================================*/




/*******************************************************************************
 *	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_set_rts_threshold
/*============================================================================*/




/*******************************************************************************
 *	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_get_rts_threshold
/*============================================================================*/





/*******************************************************************************
 *	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;
#ifdef WARP 
	int status = -1;
	int index = 0;
#endif  // WARP
	/*------------------------------------------------------------------------*/


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


#ifdef WARP 

	/* Determine if the card is operating in the 2.4 or 5.0 GHz band; check
	   if Bit 9 is set in the current channel RID */
	lp->ltvRecord.len = 2;
	lp->ltvRecord.typ = CFG_CUR_CHANNEL;
	
	status = hcf_get_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
	
	if( status == HCF_SUCCESS ) {
		index = ( CNV_LITTLE_TO_INT( lp->ltvRecord.u.u16[0] ) & 0x100 ) ? 1 : 0;

		DBG_PRINT( "Index: %d\n", index );
	} else {
		DBG_ERROR( DbgInfo, "Could not determine radio frequency\n" );
		DBG_LEAVE( DbgInfo );
		return -EINVAL;
	}

	if( wrq->u.bitrate.value > 0 && 
		wrq->u.bitrate.value <= 1 * MEGABIT ) {
		lp->TxRateControl[index] = 0x0001;
	}
	else if( wrq->u.bitrate.value > 1 * MEGABIT && 
			 wrq->u.bitrate.value <= 2 * MEGABIT ) {
		if( wrq->u.bitrate.fixed == 1 ) {
			lp->TxRateControl[index] = 0x0002;
		} else {
			lp->TxRateControl[index] = 0x0003;
		}
	}
	else if( wrq->u.bitrate.value > 2 * MEGABIT && 
			 wrq->u.bitrate.value <= 5 * MEGABIT ) {
		if( wrq->u.bitrate.fixed == 1 ) {
			lp->TxRateControl[index] = 0x0004;
		} else {
			lp->TxRateControl[index] = 0x0007;
		}
	}
	else if( wrq->u.bitrate.value > 5 * MEGABIT && 
			 wrq->u.bitrate.value <= 6 * MEGABIT ) {
		if( wrq->u.bitrate.fixed == 1 ) {
			lp->TxRateControl[index] = 0x0010;
		} else {
			lp->TxRateControl[index] = 0x0017;
		}
	}
	else if( wrq->u.bitrate.value > 6 * MEGABIT && 
			 wrq->u.bitrate.value <= 9 * MEGABIT ) {
		if( wrq->u.bitrate.fixed == 1 ) {
			lp->TxRateControl[index] = 0x0020;
		} else {
			lp->TxRateControl[index] = 0x0037;
		}
	}
	else if( wrq->u.bitrate.value > 9 * MEGABIT && 
			 wrq->u.bitrate.value <= 11 * MEGABIT ) {
		if( wrq->u.bitrate.fixed == 1 ) {
			lp->TxRateControl[index] = 0x0008;
		} else {
			lp->TxRateControl[index] = 0x003F;
		}
	}
	else if( wrq->u.bitrate.value > 11 * MEGABIT && 
			 wrq->u.bitrate.value <= 12 * MEGABIT ) {
		if( wrq->u.bitrate.fixed == 1 ) {
			lp->TxRateControl[index] = 0x0040;
		} else {
			lp->TxRateControl[index] = 0x007F;
		}
	}
	else if( wrq->u.bitrate.value > 12 * MEGABIT && 
			 wrq->u.bitrate.value <= 18 * MEGABIT ) {
		if( wrq->u.bitrate.fixed == 1 ) {
			lp->TxRateControl[index] = 0x0080;
		} else {
			lp->TxRateControl[index] = 0x00FF;
		}
	}
	else if( wrq->u.bitrate.value > 18 * MEGABIT && 
			 wrq->u.bitrate.value <= 24 * MEGABIT ) {
		if( wrq->u.bitrate.fixed == 1 ) {
			lp->TxRateControl[index] = 0x0100;
		} else {
			lp->TxRateControl[index] = 0x01FF;
		}
	}
	else if( wrq->u.bitrate.value > 24 * MEGABIT && 
			 wrq->u.bitrate.value <= 36 * MEGABIT ) {
		if( wrq->u.bitrate.fixed == 1 ) {
			lp->TxRateControl[index] = 0x0200;
		} else {
			lp->TxRateControl[index] = 0x03FF;
		}
	}
	else if( wrq->u.bitrate.value > 36 * MEGABIT && 
			 wrq->u.bitrate.value <= 48 * MEGABIT ) {
		if( wrq->u.bitrate.fixed == 1 ) {
			lp->TxRateControl[index] = 0x0400;
		} else {
			lp->TxRateControl[index] = 0x07FF;
		}
	}
	else if( wrq->u.bitrate.value > 48 * MEGABIT && 
			 wrq->u.bitrate.value <= 54 * MEGABIT ) {
		if( wrq->u.bitrate.fixed == 1 ) {
			lp->TxRateControl[index] = 0x0800;
		} else {
			lp->TxRateControl[index] = 0x0FFF;
		}
	}
	else if( wrq->u.bitrate.fixed == 0 ) {
		/* In this case, the user has not specified a bitrate, only the "auto"
		   moniker. So, set to all supported rates */
		lp->TxRateControl[index] = PARM_MAX_TX_RATE;
	} else {
		wrq->u.bitrate.value = 0;
		ret = -EINVAL; 
	}


#else

	if( wrq->u.bitrate.value > 0 && 
		wrq->u.bitrate.value <= 1 * MEGABIT ) {
		lp->TxRateControl[0] = 1;
	}
	else if( wrq->u.bitrate.value > 1 * MEGABIT && 
			 wrq->u.bitrate.value <= 2 * MEGABIT ) {
		if( wrq->u.bitrate.fixed == 1 ) {
			lp->TxRateControl[0] = 2;
		} else {
			lp->TxRateControl[0] = 6;
		}
	}
	else if( wrq->u.bitrate.value > 2 * MEGABIT && 
			 wrq->u.bitrate.value <= 5 * MEGABIT ) {
		if( wrq->u.bitrate.fixed == 1 ) {
			lp->TxRateControl[0] = 4;
		} else {
			lp->TxRateControl[0] = 7;
		}
	}
	else if( wrq->u.bitrate.value > 5 * MEGABIT && 
			 wrq->u.bitrate.value <= 11 * MEGABIT ) {
		if( wrq->u.bitrate.fixed == 1 ) {
			lp->TxRateControl[0] = 5;
		} else {
			lp->TxRateControl[0] = 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[0] = 3;
	} else {
		wrq->u.bitrate.value = 0;
		ret = -EINVAL; 
	}

#endif  // WARP


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

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




/*******************************************************************************
 *	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;
	hcf_16  txRate;
	/*------------------------------------------------------------------------*/


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


	/* Get the current transmit rate from the adapter */
	lp->ltvRecord.typ = 2;
	lp->ltvRecord.typ = CFG_CUR_TX_RATE;
		
	status = hcf_get_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
	if( status == HCF_SUCCESS ) {
		txRate = CNV_LITTLE_TO_INT( lp->ltvRecord.u.u16[0] );

#ifdef WARP 

		if( txRate & 0x0001 ) {
			txRate = 1;
		}
		else if( txRate & 0x0002 ) {
			txRate = 2;
		}
		else if( txRate & 0x0004 ) {
			txRate = 5;
		}
		else if( txRate & 0x0008 ) {
			txRate = 11;
		}
		else if( txRate & 0x00010 ) {
			txRate = 6;
		}
		else if( txRate & 0x00020 ) {
			txRate = 9;
		}
		else if( txRate & 0x00040 ) {
			txRate = 12;
		}
		else if( txRate & 0x00080 ) {
			txRate = 18;
		}
		else if( txRate & 0x00100 ) {
			txRate = 24;
		}
		else if( txRate & 0x00200 ) {
			txRate = 36;
		}
		else if( txRate & 0x00400 ) {
			txRate = 48;
		}
		else if( txRate & 0x00800 ) {
			txRate = 54;
		}

#else

		txRate = (hcf_16)CNV_LITTLE_TO_LONG( lp->ltvRecord.u.u32[0] );

#endif  // WARP

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

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




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



#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 */
#ifdef WARP 
	lp->ltvRecord.len       = 5;
	lp->ltvRecord.typ       = CFG_SCAN_CHANNEL;
	lp->ltvRecord.u.u16[0]  = CNV_INT_TO_LITTLE( 0x3FFF );  // 2.4 GHz Band
	lp->ltvRecord.u.u16[1]  = CNV_INT_TO_LITTLE( 0xFFFF );  // 5.0 GHz Band
	lp->ltvRecord.u.u16[2]  = CNV_INT_TO_LITTLE( 0xFFFF );  //      ..
	lp->ltvRecord.u.u16[3]  = CNV_INT_TO_LITTLE( 0x0007 );  //      ..
#else
	lp->ltvRecord.len       = 2;
	lp->ltvRecord.typ       = CFG_SCAN_CHANNEL;
	lp->ltvRecord.u.u16[0]  = CNV_INT_TO_LITTLE( 0x7FFF );
#endif  // WARP

	status = hcf_put_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
	DBG_TRACE( DbgInfo, "CFG_SCAN_CHANNEL 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       = 18;
	lp->ltvRecord.typ       = CFG_SCAN_SSID;
	lp->ltvRecord.u.u16[0]  = CNV_INT_TO_LITTLE( 0 );
	lp->ltvRecord.u.u16[1]  = 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_set_scan
/*============================================================================*/




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

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




/*******************************************************************************
 *	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" );
		} else
#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_get_wireless_stats


/*******************************************************************************
 *	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_spy_gather
/*============================================================================*/




/*******************************************************************************
 *	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_freq
/*============================================================================*/




/*******************************************************************************
 *	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 ( CNV_INT_TO_LITTLE( lp->hcfCtx.IFB_FWIdentity.comp_id ) == COMP_ID_FW_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_mode
/*============================================================================*/




/*******************************************************************************
 *	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_essid
/*============================================================================*/




/*******************************************************************************
 *	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 1 //;? (HCF_TYPE) & HCF_TYPE_AP
		//;?should we restore this to allow smaller memory footprint

		if ( CNV_INT_TO_LITTLE( lp->hcfCtx.IFB_FWIdentity.comp_id ) == COMP_ID_FW_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_encode
/*============================================================================*/




/*******************************************************************************
 *	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 send 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_CUR_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_ap
/*============================================================================*/




/*******************************************************************************
 *	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_scan_complete
/*============================================================================*/




/*******************************************************************************
 *	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_new_sta
/*============================================================================*/




/*******************************************************************************
 *	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_expired_sta
/*============================================================================*/




/*******************************************************************************
 *	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_mic_failed
/*============================================================================*/




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

#endif // WIRELESS_EXT
