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