d0f2cc5aea
Cc: Henk de Groot <pe1dnn@amsat.org> Cc: Randy Dunlap <randy.dunlap@oracle.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
1588 lines
44 KiB
C
1588 lines
44 KiB
C
/*******************************************************************************
|
|
* Agere Systems Inc.
|
|
* Wireless device driver for Linux (wlags49).
|
|
*
|
|
* Copyright (c) 1998-2003 Agere Systems Inc.
|
|
* All rights reserved.
|
|
* http://www.agere.com
|
|
*
|
|
* Initially developed by TriplePoint, Inc.
|
|
* http://www.triplepoint.com
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* This file defines misc utility functions.
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*
|
|
* SOFTWARE LICENSE
|
|
*
|
|
* This software is provided subject to the following terms and conditions,
|
|
* which you should read carefully before using the software. Using this
|
|
* software indicates your acceptance of these terms and conditions. If you do
|
|
* not agree with these terms and conditions, do not use the software.
|
|
*
|
|
* Copyright © 2003 Agere Systems Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source or binary forms, with or without
|
|
* modifications, are permitted provided that the following conditions are met:
|
|
*
|
|
* . Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following Disclaimer as comments in the code as
|
|
* well as in the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* . Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following Disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* . Neither the name of Agere Systems Inc. nor the names of the contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* Disclaimer
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
* INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ANY
|
|
* USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
|
|
* RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
|
* DAMAGE.
|
|
*
|
|
******************************************************************************/
|
|
|
|
/*******************************************************************************
|
|
* include files
|
|
******************************************************************************/
|
|
#include <wl_version.h>
|
|
|
|
#include <linux/kernel.h>
|
|
// #include <linux/sched.h>
|
|
// #include <linux/ptrace.h>
|
|
#include <linux/ctype.h>
|
|
// #include <linux/string.h>
|
|
// #include <linux/timer.h>
|
|
// #include <linux/interrupt.h>
|
|
// #include <linux/in.h>
|
|
// #include <linux/delay.h>
|
|
// #include <asm/io.h>
|
|
// #include <asm/system.h>
|
|
// #include <asm/bitops.h>
|
|
|
|
#include <linux/netdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
// #include <linux/skbuff.h>
|
|
// #include <linux/if_arp.h>
|
|
// #include <linux/ioport.h>
|
|
|
|
#include <debug.h>
|
|
#include <hcf.h>
|
|
// #include <hcfdef.h>
|
|
|
|
#include <wl_if.h>
|
|
#include <wl_internal.h>
|
|
#include <wl_util.h>
|
|
#include <wl_wext.h>
|
|
#include <wl_main.h>
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* global variables
|
|
******************************************************************************/
|
|
|
|
/* A matrix which maps channels to frequencies */
|
|
#define MAX_CHAN_FREQ_MAP_ENTRIES 50
|
|
static const long chan_freq_list[][MAX_CHAN_FREQ_MAP_ENTRIES] =
|
|
{
|
|
{1,2412},
|
|
{2,2417},
|
|
{3,2422},
|
|
{4,2427},
|
|
{5,2432},
|
|
{6,2437},
|
|
{7,2442},
|
|
{8,2447},
|
|
{9,2452},
|
|
{10,2457},
|
|
{11,2462},
|
|
{12,2467},
|
|
{13,2472},
|
|
{14,2484},
|
|
{36,5180},
|
|
{40,5200},
|
|
{44,5220},
|
|
{48,5240},
|
|
{52,5260},
|
|
{56,5280},
|
|
{60,5300},
|
|
{64,5320},
|
|
{149,5745},
|
|
{153,5765},
|
|
{157,5785},
|
|
{161,5805}
|
|
};
|
|
|
|
#if DBG
|
|
extern dbg_info_t *DbgInfo;
|
|
#endif /* DBG */
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* dbm()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Return an energy value in dBm.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* value - the energy value to be converted
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* the value in dBm
|
|
*
|
|
******************************************************************************/
|
|
int dbm( int value )
|
|
{
|
|
/* Truncate the value to be between min and max. */
|
|
if( value < HCF_MIN_SIGNAL_LEVEL )
|
|
value = HCF_MIN_SIGNAL_LEVEL;
|
|
|
|
if( value > HCF_MAX_SIGNAL_LEVEL )
|
|
value = HCF_MAX_SIGNAL_LEVEL;
|
|
|
|
/* Return the energy value in dBm. */
|
|
return ( value - HCF_0DBM_OFFSET );
|
|
} // dbm
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* percent()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Return a value as a percentage of min to max.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* value - the value in question
|
|
* min - the minimum range value
|
|
* max - the maximum range value
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* the percentage value
|
|
*
|
|
******************************************************************************/
|
|
int percent( int value, int min, int max )
|
|
{
|
|
/* Truncate the value to be between min and max. */
|
|
if( value < min )
|
|
value = min;
|
|
|
|
if( value > max )
|
|
value = max;
|
|
|
|
/* Return the value as a percentage of min to max. */
|
|
return ((( value - min ) * 100 ) / ( max - min ));
|
|
} // percent
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* is_valid_key_string()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Checks to determine if the WEP key string is valid
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* s - the string in question
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* non-zero if the string contains a valid key
|
|
*
|
|
******************************************************************************/
|
|
int is_valid_key_string( char *s )
|
|
{
|
|
int l;
|
|
int i;
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
l = strlen( s );
|
|
|
|
/* 0x followed by 5 or 13 hexadecimal digit pairs is valid */
|
|
if( s[0] == '0' && ( s[1] == 'x' || s[1] == 'X' )) {
|
|
if( l == 12 || l == 28 ) {
|
|
for( i = 2; i < l; i++ ) {
|
|
if( !isxdigit( s[i] ))
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* string with 0, 5, or 13 characters is valid */
|
|
else
|
|
{
|
|
return( l == 0 || l == 5 || l == 13 );
|
|
}
|
|
} // is_valid_key_string
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* hexdigit2int()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Converts a hexadecimal digit character to an integer
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* c - the hexadecimal digit character
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* the converted integer
|
|
*
|
|
******************************************************************************/
|
|
int hexdigit2int( char c )
|
|
{
|
|
if( c >= '0' && c <= '9' )
|
|
return c - '0';
|
|
|
|
if( c >= 'A' && c <= 'F' )
|
|
return c - 'A' + 10;
|
|
|
|
if( c >= 'a' && c <= 'f' )
|
|
return c - 'a' + 10;
|
|
|
|
return 0;
|
|
} // hexdigit2int
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* key_string2key()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Converts a key_string to a key, Assumes the key_string is validated with
|
|
* is_valid_key_string().
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* ks - the valid key string
|
|
* key - a pointer to a KEY_STRUCT where the converted key information will
|
|
* be stored.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* N/A
|
|
*
|
|
******************************************************************************/
|
|
void key_string2key( char *ks, KEY_STRCT *key )
|
|
{
|
|
int l,i,n;
|
|
char *p;
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
l = strlen( ks );
|
|
|
|
/* 0x followed by hexadecimal digit pairs */
|
|
if( ks[0] == '0' && ( ks[1] == 'x' || ks[1] == 'X' )) {
|
|
n = 0;
|
|
p = (char *)key->key;
|
|
|
|
for( i = 2; i < l; i+=2 ) {
|
|
*p++ = ( hexdigit2int( ks[i] ) << 4 ) + hexdigit2int (ks[i+1] );
|
|
n++;
|
|
}
|
|
|
|
/* Note that endian translation of the length field is not needed here
|
|
because it's performed in wl_put_ltv() */
|
|
key->len = n;
|
|
}
|
|
/* character string */
|
|
else
|
|
{
|
|
strcpy( (char *)key->key, ks );
|
|
key->len = l;
|
|
}
|
|
|
|
return;
|
|
} // key_string2key
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
#if DBG
|
|
/*******************************************************************************
|
|
* DbgHwAddr()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Convert a hardware ethernet address to a character string
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* hwAddr - an ethernet address
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* a pointer to a string representing the ethernet address
|
|
*
|
|
******************************************************************************/
|
|
const char *DbgHwAddr(unsigned char *hwAddr)
|
|
{
|
|
static char buffer[18];
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
sprintf( buffer, "%02X:%02X:%02X:%02X:%02X:%02X",
|
|
hwAddr[0], hwAddr[1], hwAddr[2], hwAddr[3], hwAddr[4], hwAddr[5] );
|
|
|
|
return buffer;
|
|
} // DbgHwAddr
|
|
/*============================================================================*/
|
|
|
|
#endif /* DBG */
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* wl_has_wep()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Checks to see if the device supports WEP
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* ifbp - the IFB pointer of the device in question
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* 1 if WEP is known enabled, else 0
|
|
*
|
|
******************************************************************************/
|
|
int wl_has_wep (IFBP ifbp)
|
|
{
|
|
CFG_PRIVACY_OPT_IMPLEMENTED_STRCT ltv;
|
|
int rc, privacy;
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
/* This function allows us to distiguish bronze cards from other types, to
|
|
know if WEP exists. Does not distinguish (because there's no way to)
|
|
between silver and gold cards. */
|
|
ltv.len = 2;
|
|
ltv.typ = CFG_PRIVACY_OPT_IMPLEMENTED;
|
|
|
|
rc = hcf_get_info( ifbp, (LTVP) <v );
|
|
|
|
privacy = CNV_LITTLE_TO_INT( ltv.privacy_opt_implemented );
|
|
|
|
//return rc ? 0 : privacy;
|
|
return 1;
|
|
} // wl_has_wep
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* wl_hcf_error()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Report the type of HCF error message
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* none
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* A descriptive string indicating the error, quiet otherwise.
|
|
*
|
|
******************************************************************************/
|
|
void wl_hcf_error( struct net_device *dev, int hcfStatus )
|
|
{
|
|
char buffer[64], *pMsg;
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
if( hcfStatus != HCF_SUCCESS ) {
|
|
switch( hcfStatus ) {
|
|
|
|
case HCF_ERR_TIME_OUT:
|
|
|
|
pMsg = "Expected adapter event did not occur in expected time";
|
|
break;
|
|
|
|
|
|
case HCF_ERR_NO_NIC:
|
|
|
|
pMsg = "Card not found (ejected unexpectedly)";
|
|
break;
|
|
|
|
|
|
case HCF_ERR_LEN:
|
|
|
|
pMsg = "Command buffer size insufficient";
|
|
break;
|
|
|
|
|
|
case HCF_ERR_INCOMP_PRI:
|
|
|
|
pMsg = "Primary functions are not compatible";
|
|
break;
|
|
|
|
|
|
case HCF_ERR_INCOMP_FW:
|
|
|
|
pMsg = "Primary functions are compatible, "
|
|
"station/ap functions are not";
|
|
break;
|
|
|
|
|
|
case HCF_ERR_BUSY:
|
|
|
|
pMsg = "Inquire cmd while another Inquire in progress";
|
|
break;
|
|
|
|
|
|
//case HCF_ERR_SEQ_BUG:
|
|
|
|
// pMsg = "Unexpected command completed";
|
|
// break;
|
|
|
|
|
|
case HCF_ERR_DEFUNCT_AUX:
|
|
|
|
pMsg = "Timeout on ack for enable/disable of AUX registers";
|
|
break;
|
|
|
|
|
|
case HCF_ERR_DEFUNCT_TIMER:
|
|
pMsg = "Timeout on timer calibration during initialization process";
|
|
break;
|
|
|
|
|
|
case HCF_ERR_DEFUNCT_TIME_OUT:
|
|
pMsg = "Timeout on Busy bit drop during BAP setup";
|
|
break;
|
|
|
|
|
|
case HCF_ERR_DEFUNCT_CMD_SEQ:
|
|
pMsg = "Hermes and HCF are out of sync";
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
sprintf( buffer, "Error code %d", hcfStatus );
|
|
pMsg = buffer;
|
|
break;
|
|
}
|
|
|
|
printk( KERN_INFO "%s: Wireless, HCF failure: \"%s\"\n",
|
|
dev->name, pMsg );
|
|
}
|
|
} // wl_hcf_error
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* wl_endian_translate_event()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Determines what type of data is in the mailbox and performs the proper
|
|
* endian translation.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* pLtv - an LTV pointer
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* N/A
|
|
*
|
|
******************************************************************************/
|
|
void wl_endian_translate_event( ltv_t *pLtv )
|
|
{
|
|
DBG_FUNC( "wl_endian_translate_event" );
|
|
DBG_ENTER( DbgInfo );
|
|
|
|
|
|
switch( pLtv->typ ) {
|
|
case CFG_TALLIES:
|
|
break;
|
|
|
|
|
|
case CFG_SCAN:
|
|
{
|
|
int numAPs;
|
|
SCAN_RS_STRCT *pAps = (SCAN_RS_STRCT*)&pLtv->u.u8[0];
|
|
|
|
numAPs = (hcf_16)(( (size_t)( pLtv->len - 1 ) * 2 ) /
|
|
(sizeof( SCAN_RS_STRCT )));
|
|
|
|
while( numAPs >= 1 ) {
|
|
numAPs--;
|
|
|
|
pAps[numAPs].channel_id =
|
|
CNV_LITTLE_TO_INT( pAps[numAPs].channel_id );
|
|
|
|
pAps[numAPs].noise_level =
|
|
CNV_LITTLE_TO_INT( pAps[numAPs].noise_level );
|
|
|
|
pAps[numAPs].signal_level =
|
|
CNV_LITTLE_TO_INT( pAps[numAPs].signal_level );
|
|
|
|
pAps[numAPs].beacon_interval_time =
|
|
CNV_LITTLE_TO_INT( pAps[numAPs].beacon_interval_time );
|
|
|
|
pAps[numAPs].capability =
|
|
CNV_LITTLE_TO_INT( pAps[numAPs].capability );
|
|
|
|
pAps[numAPs].ssid_len =
|
|
CNV_LITTLE_TO_INT( pAps[numAPs].ssid_len );
|
|
|
|
pAps[numAPs].ssid_val[pAps[numAPs].ssid_len] = 0;
|
|
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case CFG_ACS_SCAN:
|
|
{
|
|
PROBE_RESP *probe_resp = (PROBE_RESP *)pLtv;
|
|
|
|
probe_resp->frameControl = CNV_LITTLE_TO_INT( probe_resp->frameControl );
|
|
probe_resp->durID = CNV_LITTLE_TO_INT( probe_resp->durID );
|
|
probe_resp->sequence = CNV_LITTLE_TO_INT( probe_resp->sequence );
|
|
probe_resp->dataLength = CNV_LITTLE_TO_INT( probe_resp->dataLength );
|
|
|
|
#ifndef WARP
|
|
probe_resp->lenType = CNV_LITTLE_TO_INT( probe_resp->lenType );
|
|
#endif // WARP
|
|
|
|
probe_resp->beaconInterval = CNV_LITTLE_TO_INT( probe_resp->beaconInterval );
|
|
probe_resp->capability = CNV_LITTLE_TO_INT( probe_resp->capability );
|
|
probe_resp->flags = CNV_LITTLE_TO_INT( probe_resp->flags );
|
|
}
|
|
break;
|
|
|
|
|
|
case CFG_LINK_STAT:
|
|
#define ls ((LINK_STATUS_STRCT *)pLtv)
|
|
ls->linkStatus = CNV_LITTLE_TO_INT( ls->linkStatus );
|
|
break;
|
|
#undef ls
|
|
|
|
case CFG_ASSOC_STAT:
|
|
{
|
|
ASSOC_STATUS_STRCT *pAs = (ASSOC_STATUS_STRCT *)pLtv;
|
|
|
|
pAs->assocStatus = CNV_LITTLE_TO_INT( pAs->assocStatus );
|
|
}
|
|
break;
|
|
|
|
|
|
case CFG_SECURITY_STAT:
|
|
{
|
|
SECURITY_STATUS_STRCT *pSs = (SECURITY_STATUS_STRCT *)pLtv;
|
|
|
|
pSs->securityStatus = CNV_LITTLE_TO_INT( pSs->securityStatus );
|
|
pSs->reason = CNV_LITTLE_TO_INT( pSs->reason );
|
|
}
|
|
break;
|
|
|
|
|
|
case CFG_WMP:
|
|
break;
|
|
|
|
|
|
case CFG_NULL:
|
|
break;
|
|
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
return;
|
|
} // wl_endian_translate_event
|
|
/*============================================================================*/
|
|
|
|
|
|
/*******************************************************************************
|
|
* msf_assert()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Print statement used to display asserts from within the HCF. Only called
|
|
* when asserts in the HCF are turned on. See hcfcfg.h for more information.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* file_namep - the filename in which the assert occurred.
|
|
* line_number - the line number on which the assert occurred.
|
|
* trace - a comment associated with the assert.
|
|
* qual - return code or other value related to the assert
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* N/A
|
|
*
|
|
******************************************************************************/
|
|
void msf_assert( unsigned int line_number, hcf_16 trace, hcf_32 qual )
|
|
{
|
|
DBG_PRINT( "HCF ASSERT: Line %d, VAL: 0x%.8x\n", line_number, /*;?*/(u32)qual );
|
|
} // msf_assert
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* wl_parse_ds_ie()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function parses the Direct Sequence Parameter Set IE, used to
|
|
* determine channel/frequency information.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* probe_rsp - a pointer to a PROBE_RESP structure containing the probe
|
|
* response.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* The channel on which the BSS represented by this probe response is
|
|
* transmitting.
|
|
*
|
|
******************************************************************************/
|
|
hcf_8 wl_parse_ds_ie( PROBE_RESP *probe_rsp )
|
|
{
|
|
int i;
|
|
int ie_length = 0;
|
|
hcf_8 *buf;
|
|
hcf_8 buf_size;
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
if( probe_rsp == NULL ) {
|
|
return 0;
|
|
}
|
|
|
|
buf = probe_rsp->rawData;
|
|
buf_size = sizeof( probe_rsp->rawData );
|
|
|
|
|
|
for( i = 0; i < buf_size; i++ ) {
|
|
if( buf[i] == DS_INFO_ELEM ) {
|
|
/* Increment by 1 to get the length, and test it; in a DS element,
|
|
length should always be 1 */
|
|
i++;
|
|
ie_length = buf[i];
|
|
|
|
if( buf[i] == 1 ) {
|
|
/* Get the channel information */
|
|
i++;
|
|
return buf[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If we get here, we didn't find a DS-IE, which is strange */
|
|
return 0;
|
|
} // wl_parse_ds_ie
|
|
|
|
|
|
/*******************************************************************************
|
|
* wl_parse_wpa_ie()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function parses the Probe Response for a valid WPA-IE.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* probe_rsp - a pointer to a PROBE_RESP structure containing the probe
|
|
* response
|
|
* length - a pointer to an hcf_16 in which the size of the WPA-IE will
|
|
* be stored (if found).
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* A pointer to the location in the probe response buffer where a valid
|
|
* WPA-IE lives. The length of this IE is written back to the 'length'
|
|
* argument passed to the function.
|
|
*
|
|
******************************************************************************/
|
|
hcf_8 * wl_parse_wpa_ie( PROBE_RESP *probe_rsp, hcf_16 *length )
|
|
{
|
|
int i;
|
|
int ie_length = 0;
|
|
hcf_8 *buf;
|
|
hcf_8 buf_size;
|
|
hcf_8 wpa_oui[] = WPA_OUI_TYPE;
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
if( probe_rsp == NULL || length == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
buf = probe_rsp->rawData;
|
|
buf_size = sizeof( probe_rsp->rawData );
|
|
*length = 0;
|
|
|
|
|
|
for( i = 0; i < buf_size; i++ ) {
|
|
if( buf[i] == GENERIC_INFO_ELEM ) {
|
|
/* Increment by one to get the IE length */
|
|
i++;
|
|
ie_length = probe_rsp->rawData[i];
|
|
|
|
/* Increment by one to point to the IE payload */
|
|
i++;
|
|
|
|
/* Does the IE contain a WPA OUI? If not, it's a proprietary IE */
|
|
if( memcmp( &buf[i], &wpa_oui, WPA_SELECTOR_LEN ) == 0 ) {
|
|
/* Pass back length and return a pointer to the WPA-IE */
|
|
/* NOTE: Length contained in the WPA-IE is only the length of
|
|
the payload. The entire WPA-IE, including the IE identifier
|
|
and the length, is 2 bytes larger */
|
|
*length = ie_length + 2;
|
|
|
|
/* Back up the pointer 2 bytes to include the IE identifier and
|
|
the length in the buffer returned */
|
|
i -= 2;
|
|
return &buf[i];
|
|
}
|
|
|
|
/* Increment past this non-WPA IE and continue looking */
|
|
i += ( ie_length - 1 );
|
|
}
|
|
}
|
|
|
|
/* If we're here, we didn't find a WPA-IE in the buffer */
|
|
return NULL;
|
|
} // wl_parse_wpa_ie
|
|
|
|
|
|
/*******************************************************************************
|
|
* wl_print_wpa_ie()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Function used to take a WPA Information Element (WPA-IE) buffer and
|
|
* display it in a readable format.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* buffer - the byte buffer containing the WPA-IE
|
|
* length - the length of the above buffer
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* A pointer to the formatted WPA-IE string. Note that the format used is
|
|
* byte-by-byte printing as %02x hex values with no spaces. This is
|
|
* required for proper operation with some WPA supplicants.
|
|
*
|
|
******************************************************************************/
|
|
hcf_8 * wl_print_wpa_ie( hcf_8 *buffer, int length )
|
|
{
|
|
int count;
|
|
int rows;
|
|
int remainder;
|
|
int rowsize = 4;
|
|
hcf_8 row_buf[64];
|
|
static hcf_8 output[512];
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
memset( output, 0, sizeof( output ));
|
|
memset( row_buf, 0, sizeof( row_buf ));
|
|
|
|
|
|
/* Determine how many rows will be needed, and the remainder */
|
|
rows = length / rowsize;
|
|
remainder = length % rowsize;
|
|
|
|
|
|
/* Format the rows */
|
|
for( count = 0; count < rows; count++ ) {
|
|
sprintf( row_buf, "%02x%02x%02x%02x",
|
|
buffer[count*rowsize], buffer[count*rowsize+1],
|
|
buffer[count*rowsize+2], buffer[count*rowsize+3]);
|
|
strcat( output, row_buf );
|
|
}
|
|
|
|
memset( row_buf, 0, sizeof( row_buf ));
|
|
|
|
|
|
/* Format the remainder */
|
|
for( count = 0; count < remainder; count++ ) {
|
|
sprintf( row_buf, "%02x", buffer[(rows*rowsize)+count]);
|
|
strcat( output, row_buf );
|
|
}
|
|
|
|
return output;
|
|
} // wl_print_wpa_ie
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* wl_is_a_valid_chan()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Checks if a given channel is valid
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* channel - the channel
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* 1 if TRUE
|
|
* 0 if FALSE
|
|
*
|
|
******************************************************************************/
|
|
int wl_is_a_valid_chan( int channel )
|
|
{
|
|
int i;
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
/* Strip out the high bit set by the FW for 802.11a channels */
|
|
if( channel & 0x100 ) {
|
|
channel = channel & 0x0FF;
|
|
}
|
|
|
|
/* Iterate through the matrix and retrieve the frequency */
|
|
for( i = 0; i < MAX_CHAN_FREQ_MAP_ENTRIES; i++ ) {
|
|
if( chan_freq_list[i][0] == channel ) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
} // wl_is_a_valid_chan
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* wl_get_chan_from_freq()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Checks if a given frequency is valid
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* freq - the frequency
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* 1 if TRUE
|
|
* 0 if FALSE
|
|
*
|
|
******************************************************************************/
|
|
int wl_is_a_valid_freq( long frequency )
|
|
{
|
|
int i;
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
/* Iterate through the matrix and retrieve the channel */
|
|
for( i = 0; i < MAX_CHAN_FREQ_MAP_ENTRIES; i++ ) {
|
|
if( chan_freq_list[i][1] == frequency ) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
} // wl_is_a_valid_freq
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* wl_get_freq_from_chan()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Function used to look up the frequency for a given channel on which the
|
|
* adapter is Tx/Rx.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* channel - the channel
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* The corresponding frequency
|
|
*
|
|
******************************************************************************/
|
|
long wl_get_freq_from_chan( int channel )
|
|
{
|
|
int i;
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
/* Strip out the high bit set by the FW for 802.11a channels */
|
|
if( channel & 0x100 ) {
|
|
channel = channel & 0x0FF;
|
|
}
|
|
|
|
/* Iterate through the matrix and retrieve the frequency */
|
|
for( i = 0; i < MAX_CHAN_FREQ_MAP_ENTRIES; i++ ) {
|
|
if( chan_freq_list[i][0] == channel ) {
|
|
return chan_freq_list[i][1];
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
} // wl_get_freq_from_chan
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* wl_get_chan_from_freq()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Function used to look up the channel for a given frequency on which the
|
|
* adapter is Tx/Rx.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* frequency - the frequency
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* The corresponding channel
|
|
*
|
|
******************************************************************************/
|
|
int wl_get_chan_from_freq( long frequency )
|
|
{
|
|
int i;
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
/* Iterate through the matrix and retrieve the channel */
|
|
for( i = 0; i < MAX_CHAN_FREQ_MAP_ENTRIES; i++ ) {
|
|
if( chan_freq_list[i][1] == frequency ) {
|
|
return chan_freq_list[i][0];
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
} // wl_get_chan_from_freq
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* wl_process_link_status()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Process the link status message signaled by the device.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* lp - a pointer to the device's private structure
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* N/A
|
|
*
|
|
******************************************************************************/
|
|
void wl_process_link_status( struct wl_private *lp )
|
|
{
|
|
hcf_16 link_stat;
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
DBG_FUNC( "wl_process_link_status" );
|
|
DBG_ENTER( DbgInfo );
|
|
|
|
if( lp != NULL ) {
|
|
//link_stat = lp->hcfCtx.IFB_DSLinkStat & CFG_LINK_STAT_FW;
|
|
link_stat = lp->hcfCtx.IFB_LinkStat & CFG_LINK_STAT_FW;
|
|
switch( link_stat ) {
|
|
case 1:
|
|
DBG_TRACE( DbgInfo, "Link Status : Connected\n" );
|
|
wl_wext_event_ap( lp->dev );
|
|
break;
|
|
case 2:
|
|
DBG_TRACE( DbgInfo, "Link Status : Disconnected\n" );
|
|
break;
|
|
case 3:
|
|
DBG_TRACE( DbgInfo, "Link Status : Access Point Change\n" );
|
|
break;
|
|
case 4:
|
|
DBG_TRACE( DbgInfo, "Link Status : Access Point Out of Range\n" );
|
|
break;
|
|
case 5:
|
|
DBG_TRACE( DbgInfo, "Link Status : Access Point In Range\n" );
|
|
break;
|
|
default:
|
|
DBG_TRACE( DbgInfo, "Link Status : UNKNOWN (0x%04x)\n", link_stat );
|
|
break;
|
|
}
|
|
}
|
|
DBG_LEAVE( DbgInfo );
|
|
return;
|
|
} // wl_process_link_status
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* wl_process_probe_response()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Process the probe responses retunred by the device as a result of an
|
|
* active scan.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* lp - a pointer to the device's private structure
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* N/A
|
|
*
|
|
******************************************************************************/
|
|
void wl_process_probe_response( struct wl_private *lp )
|
|
{
|
|
PROBE_RESP *probe_rsp;
|
|
hcf_8 *wpa_ie = NULL;
|
|
hcf_16 wpa_ie_len = 0;
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
DBG_FUNC( "wl_process_probe_response" );
|
|
DBG_ENTER( DbgInfo );
|
|
|
|
|
|
if( lp != NULL ) {
|
|
probe_rsp = (PROBE_RESP *)&lp->ProbeResp;
|
|
|
|
wl_endian_translate_event( (ltv_t *)probe_rsp );
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) =========================\n", lp->dev->name );
|
|
DBG_TRACE( DbgInfo, "(%s) length : 0x%04x.\n", lp->dev->name,
|
|
probe_rsp->length );
|
|
|
|
if( probe_rsp->length > 1 ) {
|
|
DBG_TRACE( DbgInfo, "(%s) infoType : 0x%04x.\n", lp->dev->name,
|
|
probe_rsp->infoType );
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) signal : 0x%02x.\n", lp->dev->name,
|
|
probe_rsp->signal );
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) silence : 0x%02x.\n", lp->dev->name,
|
|
probe_rsp->silence );
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) rxFlow : 0x%02x.\n", lp->dev->name,
|
|
probe_rsp->rxFlow );
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) rate : 0x%02x.\n", lp->dev->name,
|
|
probe_rsp->rate );
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) frame cntl : 0x%04x.\n", lp->dev->name,
|
|
probe_rsp->frameControl );
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) durID : 0x%04x.\n", lp->dev->name,
|
|
probe_rsp->durID );
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) address1 : %s\n", lp->dev->name,
|
|
DbgHwAddr( probe_rsp->address1 ));
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) address2 : %s\n", lp->dev->name,
|
|
DbgHwAddr( probe_rsp->address2 ));
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) BSSID : %s\n", lp->dev->name,
|
|
DbgHwAddr( probe_rsp->BSSID ));
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) sequence : 0x%04x.\n", lp->dev->name,
|
|
probe_rsp->sequence );
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) address4 : %s\n", lp->dev->name,
|
|
DbgHwAddr( probe_rsp->address4 ));
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) datalength : 0x%04x.\n", lp->dev->name,
|
|
probe_rsp->dataLength );
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) DA : %s\n", lp->dev->name,
|
|
DbgHwAddr( probe_rsp->DA ));
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) SA : %s\n", lp->dev->name,
|
|
DbgHwAddr( probe_rsp->SA ));
|
|
|
|
#ifdef WARP
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) channel : %d\n", lp->dev->name,
|
|
probe_rsp->channel );
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) band : %d\n", lp->dev->name,
|
|
probe_rsp->band );
|
|
#else
|
|
DBG_TRACE( DbgInfo, "(%s) lenType : 0x%04x.\n", lp->dev->name,
|
|
probe_rsp->lenType );
|
|
#endif // WARP
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) timeStamp : %d.%d.%d.%d.%d.%d.%d.%d\n",
|
|
lp->dev->name,
|
|
probe_rsp->timeStamp[0],
|
|
probe_rsp->timeStamp[1],
|
|
probe_rsp->timeStamp[2],
|
|
probe_rsp->timeStamp[3],
|
|
probe_rsp->timeStamp[4],
|
|
probe_rsp->timeStamp[5],
|
|
probe_rsp->timeStamp[6],
|
|
probe_rsp->timeStamp[7]);
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) beaconInt : 0x%04x.\n", lp->dev->name,
|
|
probe_rsp->beaconInterval );
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) capability : 0x%04x.\n", lp->dev->name,
|
|
probe_rsp->capability );
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) SSID len : 0x%04x.\n", lp->dev->name,
|
|
probe_rsp->rawData[1] );
|
|
|
|
|
|
if( probe_rsp->rawData[1] > 0 ) {
|
|
char ssid[HCF_MAX_NAME_LEN];
|
|
|
|
memset( ssid, 0, sizeof( ssid ));
|
|
strncpy( ssid, &probe_rsp->rawData[2],
|
|
probe_rsp->rawData[1] );
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) SSID : %s\n",
|
|
lp->dev->name, ssid );
|
|
}
|
|
|
|
|
|
/* Parse out the WPA-IE, if one exists */
|
|
wpa_ie = wl_parse_wpa_ie( probe_rsp, &wpa_ie_len );
|
|
if( wpa_ie != NULL ) {
|
|
DBG_TRACE( DbgInfo, "(%s) WPA-IE : %s\n",
|
|
lp->dev->name, wl_print_wpa_ie( wpa_ie, wpa_ie_len ));
|
|
}
|
|
|
|
DBG_TRACE( DbgInfo, "(%s) flags : 0x%04x.\n",
|
|
lp->dev->name, probe_rsp->flags );
|
|
}
|
|
|
|
DBG_TRACE( DbgInfo, "\n" );
|
|
|
|
|
|
/* If probe response length is 1, then the scan is complete */
|
|
if( probe_rsp->length == 1 ) {
|
|
DBG_TRACE( DbgInfo, "SCAN COMPLETE\n" );
|
|
lp->probe_results.num_aps = lp->probe_num_aps;
|
|
lp->probe_results.scan_complete = TRUE;
|
|
|
|
/* Reset the counter for the next scan request */
|
|
lp->probe_num_aps = 0;
|
|
|
|
/* Send a wireless extensions event that the scan completed */
|
|
wl_wext_event_scan_complete( lp->dev );
|
|
} else {
|
|
/* Only copy to the table if the entry is unique; APs sometimes
|
|
respond more than once to a probe */
|
|
if( lp->probe_num_aps == 0 ) {
|
|
/* Copy the info to the ScanResult structure in the private
|
|
adapter struct */
|
|
memcpy( &( lp->probe_results.ProbeTable[lp->probe_num_aps] ),
|
|
probe_rsp, sizeof( PROBE_RESP ));
|
|
|
|
/* Increment the number of APs detected */
|
|
lp->probe_num_aps++;
|
|
} else {
|
|
int count;
|
|
int unique = 1;
|
|
|
|
for( count = 0; count < lp->probe_num_aps; count++ ) {
|
|
if( memcmp( &( probe_rsp->BSSID ),
|
|
lp->probe_results.ProbeTable[count].BSSID,
|
|
ETH_ALEN ) == 0 ) {
|
|
unique = 0;
|
|
}
|
|
}
|
|
|
|
if( unique ) {
|
|
/* Copy the info to the ScanResult structure in the
|
|
private adapter struct. Only copy if there's room in the
|
|
table */
|
|
if( lp->probe_num_aps < MAX_NAPS )
|
|
{
|
|
memcpy( &( lp->probe_results.ProbeTable[lp->probe_num_aps] ),
|
|
probe_rsp, sizeof( PROBE_RESP ));
|
|
}
|
|
else
|
|
{
|
|
DBG_WARNING( DbgInfo, "Num of scan results exceeds storage, truncating\n" );
|
|
}
|
|
|
|
/* Increment the number of APs detected. Note I do this
|
|
here even when I don't copy the probe response to the
|
|
buffer in order to detect the overflow condition */
|
|
lp->probe_num_aps++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
return;
|
|
} // wl_process_probe_response
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* wl_process_updated_record()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Process the updated information record message signaled by the device.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* lp - a pointer to the device's private structure
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* N/A
|
|
*
|
|
******************************************************************************/
|
|
void wl_process_updated_record( struct wl_private *lp )
|
|
{
|
|
DBG_FUNC( "wl_process_updated_record" );
|
|
DBG_ENTER( DbgInfo );
|
|
|
|
|
|
if( lp != NULL ) {
|
|
lp->updatedRecord.u.u16[0] = CNV_LITTLE_TO_INT( lp->updatedRecord.u.u16[0] );
|
|
|
|
switch( lp->updatedRecord.u.u16[0] ) {
|
|
case CFG_CUR_COUNTRY_INFO:
|
|
DBG_TRACE( DbgInfo, "Updated Record: CFG_CUR_COUNTRY_INFO\n" );
|
|
wl_connect( lp );
|
|
break;
|
|
|
|
case CFG_PORT_STAT:
|
|
DBG_TRACE( DbgInfo, "Updated Record: WAIT_FOR_CONNECT (0xFD40)\n" );
|
|
//wl_connect( lp );
|
|
break;
|
|
|
|
default:
|
|
DBG_TRACE( DbgInfo, "UNKNOWN: 0x%04x\n",
|
|
lp->updatedRecord.u.u16[0] );
|
|
}
|
|
}
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
return;
|
|
} // wl_process_updated_record
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* wl_process_assoc_status()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Process the association status event signaled by the device.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* lp - a pointer to the device's private structure
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* N/A
|
|
*
|
|
******************************************************************************/
|
|
void wl_process_assoc_status( struct wl_private *lp )
|
|
{
|
|
ASSOC_STATUS_STRCT *assoc_stat;
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
DBG_FUNC( "wl_process_assoc_status" );
|
|
DBG_ENTER( DbgInfo );
|
|
|
|
|
|
if( lp != NULL ) {
|
|
assoc_stat = (ASSOC_STATUS_STRCT *)&lp->assoc_stat;
|
|
|
|
wl_endian_translate_event( (ltv_t *)assoc_stat );
|
|
|
|
switch( assoc_stat->assocStatus ) {
|
|
case 1:
|
|
DBG_TRACE( DbgInfo, "Association Status : STA Associated\n" );
|
|
break;
|
|
|
|
case 2:
|
|
DBG_TRACE( DbgInfo, "Association Status : STA Reassociated\n" );
|
|
break;
|
|
|
|
case 3:
|
|
DBG_TRACE( DbgInfo, "Association Status : STA Disassociated\n" );
|
|
break;
|
|
|
|
default:
|
|
DBG_TRACE( DbgInfo, "Association Status : UNKNOWN (0x%04x)\n",
|
|
assoc_stat->assocStatus );
|
|
break;
|
|
}
|
|
|
|
DBG_TRACE( DbgInfo, "STA Address : %s\n",
|
|
DbgHwAddr( assoc_stat->staAddr ));
|
|
|
|
if(( assoc_stat->assocStatus == 2 ) && ( assoc_stat->len == 8 )) {
|
|
DBG_TRACE( DbgInfo, "Old AP Address : %s\n",
|
|
DbgHwAddr( assoc_stat->oldApAddr ));
|
|
}
|
|
}
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
return;
|
|
} // wl_process_assoc_status
|
|
/*============================================================================*/
|
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
* wl_process_security_status()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Process the security status message signaled by the device.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* lp - a pointer to the device's private structure
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* N/A
|
|
*
|
|
******************************************************************************/
|
|
void wl_process_security_status( struct wl_private *lp )
|
|
{
|
|
SECURITY_STATUS_STRCT *sec_stat;
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
|
|
DBG_FUNC( "wl_process_security_status" );
|
|
DBG_ENTER( DbgInfo );
|
|
|
|
|
|
if( lp != NULL ) {
|
|
sec_stat = (SECURITY_STATUS_STRCT *)&lp->sec_stat;
|
|
|
|
wl_endian_translate_event( (ltv_t *)sec_stat );
|
|
|
|
switch( sec_stat->securityStatus ) {
|
|
case 1:
|
|
DBG_TRACE( DbgInfo, "Security Status : Dissassociate [AP]\n" );
|
|
break;
|
|
|
|
case 2:
|
|
DBG_TRACE( DbgInfo, "Security Status : Deauthenticate [AP]\n" );
|
|
break;
|
|
|
|
case 3:
|
|
DBG_TRACE( DbgInfo, "Security Status : Authenticate Fail [STA] or [AP]\n" );
|
|
break;
|
|
|
|
case 4:
|
|
DBG_TRACE( DbgInfo, "Security Status : MIC Fail\n" );
|
|
break;
|
|
|
|
case 5:
|
|
DBG_TRACE( DbgInfo, "Security Status : Associate Fail\n" );
|
|
break;
|
|
|
|
default:
|
|
DBG_TRACE( DbgInfo, "Security Status : UNKNOWN (0x%04x)\n",
|
|
sec_stat->securityStatus );
|
|
break;
|
|
}
|
|
|
|
DBG_TRACE( DbgInfo, "STA Address : %s\n",
|
|
DbgHwAddr( sec_stat->staAddr ));
|
|
DBG_TRACE( DbgInfo, "Reason : 0x%04x \n", sec_stat->reason );
|
|
|
|
}
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
return;
|
|
} // wl_process_security_status
|
|
/*============================================================================*/
|
|
|
|
int wl_get_tallies(struct wl_private *lp,
|
|
CFG_HERMES_TALLIES_STRCT *tallies)
|
|
{
|
|
int ret = 0;
|
|
int status;
|
|
CFG_HERMES_TALLIES_STRCT *pTallies;
|
|
|
|
DBG_FUNC( "wl_get_tallies" );
|
|
DBG_ENTER(DbgInfo);
|
|
|
|
/* Get the current tallies from the adapter */
|
|
lp->ltvRecord.len = 1 + HCF_TOT_TAL_CNT * sizeof(hcf_16);
|
|
lp->ltvRecord.typ = CFG_TALLIES;
|
|
|
|
status = hcf_get_info(&(lp->hcfCtx), (LTVP)&(lp->ltvRecord));
|
|
|
|
if( status == HCF_SUCCESS ) {
|
|
pTallies = (CFG_HERMES_TALLIES_STRCT *)&(lp->ltvRecord.u.u32);
|
|
memcpy(tallies, pTallies, sizeof(*tallies));
|
|
DBG_TRACE( DbgInfo, "Get tallies okay, dixe: %d\n", sizeof(*tallies) );
|
|
} else {
|
|
DBG_TRACE( DbgInfo, "Get tallies failed\n" );
|
|
ret = -EFAULT;
|
|
}
|
|
|
|
DBG_LEAVE( DbgInfo );
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* wl_atoi()
|
|
*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Believe it or not, we need our own implementation of atoi in the kernel.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* string - the ASCII string to convert to an integer
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* unsigned integer
|
|
*
|
|
******************************************************************************/
|
|
unsigned int wl_atoi( char *string )
|
|
{
|
|
unsigned int base = 10; //default to decimal
|
|
unsigned int value = 0;
|
|
unsigned int c;
|
|
int i = strlen( string );
|
|
|
|
if ( i > 2 && string[0] == '0' && ( string[1] | ('X'^'x') ) == 'x' ) {
|
|
base = 16;
|
|
string +=2;
|
|
}
|
|
while ( ( c = *string++ ) != '\0' ) {
|
|
if ( value > UINT_MAX / base ) { //test for overrun
|
|
DBG_FUNC( "wl_atoi" ); //don't overload the log file with good messages
|
|
DBG_ENTER( DbgInfo );
|
|
DBG_ERROR( DbgInfo, "string \"%s\", lenght exceeds expectations\n", string );
|
|
printk( "<1>string \"%s\", lenght exceeds expectations\n", string );
|
|
DBG_LEAVE( DbgInfo );
|
|
break;
|
|
}
|
|
c -= '0';
|
|
if ( 0 <= c && c <= 9 ) value = base * value + c;
|
|
else if ( base == 16 ) {
|
|
c += '0';
|
|
c |= 'A'^'a';
|
|
c = c - 'a'+ 10;
|
|
if ( 10 <= c && c <= 15 ) value = base * value + c;
|
|
}
|
|
}
|
|
return value;
|
|
} // wl_atoi
|
|
|