diff options
Diffstat (limited to 'drivers/staging/wlags49_h2/wl_wext.c')
-rw-r--r-- | drivers/staging/wlags49_h2/wl_wext.c | 4130 |
1 files changed, 4130 insertions, 0 deletions
diff --git a/drivers/staging/wlags49_h2/wl_wext.c b/drivers/staging/wlags49_h2/wl_wext.c new file mode 100644 index 00000000000..4434e006548 --- /dev/null +++ b/drivers/staging/wlags49_h2/wl_wext.c @@ -0,0 +1,4130 @@ +/******************************************************************************* + * 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. + * + ******************************************************************************/ + +/******************************************************************************* + * include files + ******************************************************************************/ +#include <wl_version.h> + +#include <linux/if_arp.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <asm/uaccess.h> + +#include <debug.h> +#include <hcf.h> +#include <hcfdef.h> + +#include <wl_if.h> +#include <wl_internal.h> +#include <wl_util.h> +#include <wl_main.h> +#include <wl_wext.h> +#include <wl_priv.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 + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) +#define IWE_STREAM_ADD_EVENT(info, buf, end, iwe, len) \ + iwe_stream_add_event(buf, end, iwe, len) +#define IWE_STREAM_ADD_POINT(info, buf, end, iwe, msg) \ + iwe_stream_add_point(buf, end, iwe, msg) +#else +#define IWE_STREAM_ADD_EVENT(info, buf, end, iwe, len) \ + iwe_stream_add_event(info, buf, end, iwe, len) +#define IWE_STREAM_ADD_POINT(info, buf, end, iwe, msg) \ + iwe_stream_add_point(info, buf, end, iwe, msg) +#endif + + + +/******************************************************************************* + * global definitions + ******************************************************************************/ +#if DBG +extern dbg_info_t *DbgInfo; +#endif // DBG + + + + +/******************************************************************************* + * wireless_commit() + ******************************************************************************* + * + * DESCRIPTION: + * + * Commit + * protocol used. + * + * PARAMETERS: + * + * wrq - the wireless request buffer + * + * RETURNS: + * + * N/A + * + ******************************************************************************/ +static int wireless_commit(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *rqu, char *extra) +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = 0; + /*------------------------------------------------------------------------*/ + + DBG_FUNC( "wireless_commit" ); + DBG_ENTER(DbgInfo); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + wl_apply(lp); + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + DBG_LEAVE( DbgInfo ); + return ret; +} // wireless_commit +/*============================================================================*/ + + + + +/******************************************************************************* + * 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 + * + ******************************************************************************/ +static int wireless_get_protocol(struct net_device *dev, struct iw_request_info *info, char *name, char *extra) +{ + 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(name, "IEEE 802.11b"); + + DBG_LEAVE(DbgInfo); + return 0; +} // 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 + * + ******************************************************************************/ +static int wireless_set_frequency(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra) +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int channel = 0; + int ret = 0; + /*------------------------------------------------------------------------*/ + + + DBG_FUNC( "wireless_set_frequency" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + if( !capable( CAP_NET_ADMIN )) { + ret = -EPERM; + DBG_LEAVE( DbgInfo ); + return ret; + } + + + /* If frequency specified, look up channel */ + if( freq->e == 1 ) { + int f = freq->m / 100000; + channel = wl_get_chan_from_freq( f ); + } + + + /* Channel specified */ + if( freq->e == 0 ) { + channel = freq->m; + } + + + /* If the channel is an 802.11a channel, set Bit 8 */ + if( channel > 14 ) { + channel = channel | 0x100; + } + + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + 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 ); + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + 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 + * + ******************************************************************************/ +static int wireless_get_frequency(struct net_device *dev, struct iw_request_info *info, struct iw_freq *freq, char *extra) + +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = -1; + /*------------------------------------------------------------------------*/ + + + DBG_FUNC( "wireless_get_frequency" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + 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 + + freq->m = wl_get_freq_from_chan( channel ) * 100000; + freq->e = 1; +#else + + freq->m = channel; + freq->e = 0; + +#endif /* USE_FREQUENCY */ + } + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + + ret = (ret == HCF_SUCCESS ? 0 : -EFAULT); + +out: + DBG_LEAVE( DbgInfo ); + return ret; +} // 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 + * + ******************************************************************************/ +static int wireless_get_range(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra) +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + struct iw_range *range = (struct iw_range *) extra; + int ret = 0; + int status = -1; + int count; + __u16 *pTxRate; + int retries = 0; + /*------------------------------------------------------------------------*/ + + + DBG_FUNC( "wireless_get_range" ); + DBG_ENTER( DbgInfo ); + + /* Set range information */ + data->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + /* Set range information */ + memset( range, 0, sizeof( struct iw_range )); + +retry: + /* Get the current transmit rate from the adapter */ + lp->ltvRecord.len = 1 + (sizeof(*pTxRate) / sizeof(hcf_16)); + lp->ltvRecord.typ = CFG_CUR_TX_RATE; + + status = hcf_get_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord )); + if( status != HCF_SUCCESS ) { + /* Recovery action: reset and retry up to 10 times */ + DBG_TRACE( DbgInfo, "Get CFG_CUR_TX_RATE failed: 0x%x\n", status ); + + if (retries < 10) { + retries++; + + /* Holding the lock too long, make a gap to allow other processes */ + wl_unlock(lp, &flags); + wl_lock( lp, &flags ); + + status = wl_reset( dev ); + if ( status != HCF_SUCCESS ) { + DBG_TRACE( DbgInfo, "reset failed: 0x%x\n", status ); + + ret = -EFAULT; + goto out_unlock; + } + + /* Holding the lock too long, make a gap to allow other processes */ + wl_unlock(lp, &flags); + wl_lock( lp, &flags ); + + goto retry; + + } else { + DBG_TRACE( DbgInfo, "Get CFG_CUR_TX_RATE failed: %d retries\n", retries ); + ret = -EFAULT; + goto out_unlock; + } + } + + /* Holding the lock too long, make a gap to allow other processes */ + wl_unlock(lp, &flags); + wl_lock( lp, &flags ); + + pTxRate = (__u16 *)&( lp->ltvRecord.u.u32 ); + + range->throughput = CNV_LITTLE_TO_INT( *pTxRate ) * MEGABIT; + + if (retries > 0) { + DBG_TRACE( DbgInfo, "Get CFG_CUR_TX_RATE succes: %d retries\n", retries ); + } + + // 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++; + } + } else { + DBG_TRACE( DbgInfo, "CFG_SUPPORTED_DATA_RATES: 0x%x\n", status ); + ret = -EFAULT; + goto out_unlock; + } + + /* 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 + + /* Holding the lock too long, make a gap to allow other processes */ + wl_unlock(lp, &flags); + wl_lock( lp, &flags ); + + /* 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; + } + +#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 + + /* Holding the lock too long, make a gap to allow other processes */ + wl_unlock(lp, &flags); + wl_lock( lp, &flags ); + + DBG_TRACE( DbgInfo, "calling wl_wireless_stats\n" ); + wl_wireless_stats( lp->dev ); + range->avg_qual = lp->wstats.qual; + DBG_TRACE( DbgInfo, "wl_wireless_stats done\n" ); + +#endif + + /* Event capability (kernel + driver) */ + range->event_capa[0] = (IW_EVENT_CAPA_K_0 | + IW_EVENT_CAPA_MASK(SIOCGIWAP) | + IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + range->event_capa[4] = (IW_EVENT_CAPA_MASK(IWEVREGISTERED) | + IW_EVENT_CAPA_MASK(IWEVCUSTOM) | + IW_EVENT_CAPA_MASK(IWEVEXPIRED)); + + range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_CIPHER_TKIP; + +out_unlock: + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + + DBG_LEAVE(DbgInfo); + return ret; +} // wireless_get_range +/*============================================================================*/ + + +/******************************************************************************* + * 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 + * + ******************************************************************************/ +static int wireless_get_bssid(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + 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 ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + memset( &ap_addr->sa_data, 0, ETH_ALEN ); + + 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(&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_CUR_BSSID; + lp->ltvRecord.len = 4; + status = hcf_get_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord )); + + if( status == HCF_SUCCESS ) { + /* Copy info into sockaddr struct */ + memcpy(&ap_addr->sa_data, lp->ltvRecord.u.u8, ETH_ALEN); + } else { + ret = -EFAULT; + } + } + +#endif // (HCF_TYPE) & HCF_TYPE_STA + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + 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! + * + ******************************************************************************/ +static int wireless_get_ap_list (struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra) +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + 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 ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + /* 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 + + wl_act_int_on( lp ); + + //;? unlock? what about the access to lp below? is it broken? + wl_unlock(lp, &flags); + + 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( ); + } + } + } + + rmb(); + + if ( ret != HCF_SUCCESS ) { + DBG_ERROR( DbgInfo, "timeout waiting for scan results\n" ); + } else { + num_aps = (*p)/*lp->probe_results*/.num_aps; + if (num_aps > IW_MAX_AP) { + num_aps = IW_MAX_AP; + } + data->length = num_aps; + hwa = (struct sockaddr *)extra; + qual = (struct iw_quality *) extra + + ( 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 */ + 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 ); + } + } +out: + 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 + * + ******************************************************************************/ +static int wireless_set_sensitivity(struct net_device *dev, struct iw_request_info *info, struct iw_param *sens, char *extra) +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = 0; + int dens = sens->value; + /*------------------------------------------------------------------------*/ + + + DBG_FUNC( "wireless_set_sensitivity" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + if(( dens < 1 ) || ( dens > 3 )) { + ret = -EINVAL; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + lp->DistanceBetweenAPs = dens; + wl_apply( lp ); + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + 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 + * + ******************************************************************************/ +static int wireless_get_sensitivity(struct net_device *dev, struct iw_request_info *info, struct iw_param *sens, char *extra) +{ + struct wl_private *lp = wl_priv(dev); + int ret = 0; + /*------------------------------------------------------------------------*/ + /*------------------------------------------------------------------------*/ + + + DBG_FUNC( "wireless_get_sensitivity" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + /* not worth locking ... */ + sens->value = lp->DistanceBetweenAPs; + sens->fixed = 0; /* auto */ +out: + DBG_LEAVE( DbgInfo ); + return ret; +} // 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 + * + ******************************************************************************/ +static int wireless_set_essid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *ssid) +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = 0; + + DBG_FUNC( "wireless_set_essid" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + if (data->flags != 0 && data->length > HCF_MAX_NAME_LEN + 1) { + ret = -EINVAL; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + memset( lp->NetworkName, 0, sizeof( lp->NetworkName )); + + /* data->flags is zero to ask for "any" */ + if( 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 { + memcpy( lp->NetworkName, ssid, data->length ); + } + + DBG_NOTICE( DbgInfo, "set NetworkName: %s\n", ssid ); + + /* Commit the adapter parameters */ + wl_apply( lp ); + + /* Send an event that ESSID has been set */ + wl_wext_event_essid( lp->dev ); + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + 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 + * + ******************************************************************************/ +static int wireless_get_essid(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *essid) + +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = 0; + int status = -1; + wvName_t *pName; + /*------------------------------------------------------------------------*/ + + + DBG_FUNC( "wireless_get_essid" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + /* 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 */ + data->length = pName->length; + + /* NOTE: Null terminating is necessary for proper display of the SSID in + the wireless tools */ + data->length = pName->length + 1; + if( pName->length < HCF_MAX_NAME_LEN ) { + pName->name[pName->length] = '\0'; + } + + 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_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 */ + data->length = pName->length + 1; + if( pName->length < HCF_MAX_NAME_LEN ) { + pName->name[pName->length] = '\0'; + } + + data->flags = 1; + } else { + ret = -EFAULT; + goto out_unlock; + } + } + +#endif // RETURN_CURRENT_NETWORKNAME +#endif // HCF_STA + + data->length--; + + if (pName->length > IW_ESSID_MAX_SIZE) { + ret = -EFAULT; + goto out_unlock; + } + + memcpy(essid, pName->name, pName->length); + } else { + ret = -EFAULT; + goto out_unlock; + } + +out_unlock: + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + 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 + * + ******************************************************************************/ +static int wireless_set_encode(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *keybuf) +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = 0; + +#if 1 //;? #if WIRELESS_EXT > 8 - used unconditionally in the rest of the code... + hcf_8 encryption_state; +#endif // WIRELESS_EXT > 8 + /*------------------------------------------------------------------------*/ + + + DBG_FUNC( "wireless_set_encode" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + /* Is encryption supported? */ + if( !wl_has_wep( &( lp->hcfCtx ))) { + DBG_WARNING( DbgInfo, "WEP not supported on this device\n" ); + ret = -EOPNOTSUPP; + goto out_unlock; + } + + DBG_NOTICE( DbgInfo, "pointer: %p, length: %d, flags: %#x\n", + keybuf, erq->length, + erq->flags); + + /* Save state of Encryption switch */ + encryption_state = lp->EnableEncryption; + + /* Basic checking: do we have a key to set? */ + if((erq->length) != 0) { + int index = ( erq->flags & IW_ENCODE_INDEX ) - 1; + int tk = lp->TransmitKeyID - 1; // current key + + + /* Check the size of the key */ + switch(erq->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 */ + memcpy( lp->DefaultKeys.key[index].key, keybuf, erq->length); + + /* Set the length */ + lp->DefaultKeys.key[index].len = erq->length; + + DBG_NOTICE( DbgInfo, "encoding.length: %d\n", erq->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; + goto out_unlock; + } + } else { + int index = ( erq->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( erq->flags & IW_ENCODE_DISABLED ) { + lp->EnableEncryption = 0; // disable encryption + } else { + lp->EnableEncryption = 1; + } + + if( erq->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, "erq->length : %d\n", + erq->length); + DBG_TRACE( DbgInfo, "erq->flags : 0x%x\n", + erq->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( erq->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( dev ); + +out_unlock: + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + 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 + * + ******************************************************************************/ +static int wireless_get_encode(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *key) + +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = 0; + int index; + /*------------------------------------------------------------------------*/ + + + DBG_FUNC( "wireless_get_encode" ); + DBG_ENTER( DbgInfo ); + DBG_NOTICE(DbgInfo, "GIWENCODE: encrypt: %d, ID: %d\n", lp->EnableEncryption, lp->TransmitKeyID); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + /* Only super-user can see WEP key */ + if( !capable( CAP_NET_ADMIN )) { + ret = -EPERM; + DBG_LEAVE( DbgInfo ); + return ret; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + /* Is it supported? */ + if( !wl_has_wep( &( lp->hcfCtx ))) { + ret = -EOPNOTSUPP; + goto out_unlock; + } + + /* Basic checking */ + index = (erq->flags & IW_ENCODE_INDEX ) - 1; + + + /* Set the flags */ + erq->flags = 0; + + if( lp->EnableEncryption == 0 ) { + erq->flags |= IW_ENCODE_DISABLED; + } + + /* Which key do we want */ + if(( index < 0 ) || ( index >= MAX_KEYS )) { + index = lp->TransmitKeyID - 1; + } + + erq->flags |= index + 1; + + /* Copy the key to the user buffer */ + erq->length = lp->DefaultKeys.key[index].len; + + memcpy(key, lp->DefaultKeys.key[index].key, erq->length); + +out_unlock: + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + 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 + * + ******************************************************************************/ +static int wireless_set_nickname(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *nickname) +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = 0; + /*------------------------------------------------------------------------*/ + + + DBG_FUNC( "wireless_set_nickname" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + +#if 0 //;? Needed, was present in original code but not in 7.18 Linux 2.6 kernel version + if( !capable(CAP_NET_ADMIN )) { + ret = -EPERM; + DBG_LEAVE( DbgInfo ); + return ret; + } +#endif + + /* Validate the new value */ + if(data->length > HCF_MAX_NAME_LEN) { + ret = -EINVAL; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + memset( lp->StationName, 0, sizeof( lp->StationName )); + + memcpy( lp->StationName, nickname, data->length ); + + /* Commit the adapter parameters */ + wl_apply( lp ); + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + 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 + * + ******************************************************************************/ +static int wireless_get_nickname(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *nickname) +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = 0; + int status = -1; + wvName_t *pName; + /*------------------------------------------------------------------------*/ + + + DBG_FUNC( "wireless_get_nickname" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + /* 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 ); + + if ( pName->length > IW_ESSID_MAX_SIZE ) { + ret = -EFAULT; + } else { + /* Copy the information into the user buffer */ + data->length = pName->length; + memcpy(nickname, pName->name, pName->length); + } + } else { + ret = -EFAULT; + } + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + 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 + * + ******************************************************************************/ +static int wireless_set_porttype(struct net_device *dev, struct iw_request_info *info, __u32 *mode, char *extra) +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = 0; + hcf_16 portType; + hcf_16 createIBSS; + /*------------------------------------------------------------------------*/ + + DBG_FUNC( "wireless_set_porttype" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + /* Validate the new value */ + switch( *mode ) { + case IW_MODE_ADHOC: + + /* When user requests ad-hoc, set IBSS mode! */ + portType = 1; + 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; + 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; + createIBSS = 0; + ret = -EINVAL; + } + + if( portType != 0 ) { + /* Only do something if there is a mode change */ + if( ( lp->PortType != portType ) || (lp->CreateIBSS != createIBSS)) { + lp->PortType = portType; + lp->CreateIBSS = createIBSS; + + /* Commit the adapter parameters */ + wl_go( lp ); + + /* Send an event that mode has been set */ + wl_wext_event_mode( lp->dev ); + } + } + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + 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 + * + ******************************************************************************/ +static int wireless_get_porttype(struct net_device *dev, struct iw_request_info *info, __u32 *mode, char *extra) + +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = 0; + int status = -1; + hcf_16 *pPortType; + /*------------------------------------------------------------------------*/ + + + DBG_FUNC( "wireless_get_porttype" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + /* 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 ); + + *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 ) { + *mode = IW_MODE_MASTER; + } else { + *mode = IW_MODE_INFRA; + } + +#else + + *mode = 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 ) { + *mode = IW_MODE_MASTER; + } else { + if( lp->CreateIBSS ) { + *mode = IW_MODE_ADHOC; + } else { + *mode = IW_MODE_INFRA; + } + } + + break; + + + case 3: + *mode = IW_MODE_ADHOC; + break; + + default: + ret = -EFAULT; + break; + } + } else { + ret = -EFAULT; + } + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + 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 + * + ******************************************************************************/ +static int wireless_set_power(struct net_device *dev, struct iw_request_info *info, struct iw_param *wrq, char *extra) +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = 0; + /*------------------------------------------------------------------------*/ + + + DBG_FUNC( "wireless_set_power" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + DBG_PRINT( "THIS CORRUPTS PMEnabled ;?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" ); + +#if 0 //;? Needed, was present in original code but not in 7.18 Linux 2.6 kernel version + if( !capable( CAP_NET_ADMIN )) { + ret = -EPERM; + + DBG_LEAVE( DbgInfo ); + return ret; + } +#endif + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + /* Set the power management state based on the 'disabled' value */ + if( wrq->disabled ) { + lp->PMEnabled = 0; + } else { + lp->PMEnabled = 1; + } + + /* Commit the adapter parameters */ + wl_apply( lp ); + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + 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 + * + ******************************************************************************/ +static int wireless_get_power(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, char *extra) + +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = 0; + /*------------------------------------------------------------------------*/ + DBG_FUNC( "wireless_get_power" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + DBG_PRINT( "THIS IS PROBABLY AN OVER-SIMPLIFICATION ;?!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" ); + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + rrq->flags = 0; + rrq->value = 0; + + if( lp->PMEnabled ) { + rrq->disabled = 0; + } else { + rrq->disabled = 1; + } + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + DBG_LEAVE( DbgInfo ); + return ret; +} // 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 + * + ******************************************************************************/ +static int wireless_get_tx_power(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, char *extra) +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = 0; + /*------------------------------------------------------------------------*/ + DBG_FUNC( "wireless_get_tx_power" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + +#ifdef USE_POWER_DBM + rrq->value = RADIO_TX_POWER_DBM; + rrq->flags = IW_TXPOW_DBM; +#else + rrq->value = RADIO_TX_POWER_MWATT; + rrq->flags = IW_TXPOW_MWATT; +#endif + rrq->fixed = 1; + rrq->disabled = 0; + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + DBG_LEAVE( DbgInfo ); + return ret; +} // 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 + * + ******************************************************************************/ +static int wireless_set_rts_threshold (struct net_device *dev, struct iw_request_info *info, struct iw_param *rts, char *extra) +{ + int ret = 0; + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int rthr = rts->value; + /*------------------------------------------------------------------------*/ + + + DBG_FUNC( "wireless_set_rts_threshold" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + if(rts->fixed == 0) { + ret = -EINVAL; + goto out; + } + +#if WIRELESS_EXT > 8 + if( rts->disabled ) { + rthr = 2347; + } +#endif /* WIRELESS_EXT > 8 */ + + if(( rthr < 256 ) || ( rthr > 2347 )) { + ret = -EINVAL; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + lp->RTSThreshold = rthr; + + wl_apply( lp ); + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + 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 + * + ******************************************************************************/ +static int wireless_get_rts_threshold (struct net_device *dev, struct iw_request_info *info, struct iw_param *rts, char *extra) +{ + int ret = 0; + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + /*------------------------------------------------------------------------*/ + + DBG_FUNC( "wireless_get_rts_threshold" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + rts->value = lp->RTSThreshold; + +#if WIRELESS_EXT > 8 + + rts->disabled = ( rts->value == 2347 ); + +#endif /* WIRELESS_EXT > 8 */ + + rts->fixed = 1; + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + DBG_LEAVE( DbgInfo ); + return ret; +} // 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 + * + ******************************************************************************/ +static int wireless_set_rate(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, char *extra) +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = 0; +#ifdef WARP + int status = -1; + int index = 0; +#endif // WARP + /*------------------------------------------------------------------------*/ + + + DBG_FUNC( "wireless_set_rate" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + +#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 ); + ret = -EINVAL; + goto out_unlock; + } + + if( rrq->value > 0 && + rrq->value <= 1 * MEGABIT ) { + lp->TxRateControl[index] = 0x0001; + } + else if( rrq->value > 1 * MEGABIT && + rrq->value <= 2 * MEGABIT ) { + if( rrq->fixed == 1 ) { + lp->TxRateControl[index] = 0x0002; + } else { + lp->TxRateControl[index] = 0x0003; + } + } + else if( rrq->value > 2 * MEGABIT && + rrq->value <= 5 * MEGABIT ) { + if( rrq->fixed == 1 ) { + lp->TxRateControl[index] = 0x0004; + } else { + lp->TxRateControl[index] = 0x0007; + } + } + else if( rrq->value > 5 * MEGABIT && + rrq->value <= 6 * MEGABIT ) { + if( rrq->fixed == 1 ) { + lp->TxRateControl[index] = 0x0010; + } else { + lp->TxRateControl[index] = 0x0017; + } + } + else if( rrq->value > 6 * MEGABIT && + rrq->value <= 9 * MEGABIT ) { + if( rrq->fixed == 1 ) { + lp->TxRateControl[index] = 0x0020; + } else { + lp->TxRateControl[index] = 0x0037; + } + } + else if( rrq->value > 9 * MEGABIT && + rrq->value <= 11 * MEGABIT ) { + if( rrq->fixed == 1 ) { + lp->TxRateControl[index] = 0x0008; + } else { + lp->TxRateControl[index] = 0x003F; + } + } + else if( rrq->value > 11 * MEGABIT && + rrq->value <= 12 * MEGABIT ) { + if( rrq->fixed == 1 ) { + lp->TxRateControl[index] = 0x0040; + } else { + lp->TxRateControl[index] = 0x007F; + } + } + else if( rrq->value > 12 * MEGABIT && + rrq->value <= 18 * MEGABIT ) { + if( rrq->fixed == 1 ) { + lp->TxRateControl[index] = 0x0080; + } else { + lp->TxRateControl[index] = 0x00FF; + } + } + else if( rrq->value > 18 * MEGABIT && + rrq->value <= 24 * MEGABIT ) { + if( rrq->fixed == 1 ) { + lp->TxRateControl[index] = 0x0100; + } else { + lp->TxRateControl[index] = 0x01FF; + } + } + else if( rrq->value > 24 * MEGABIT && + rrq->value <= 36 * MEGABIT ) { + if( rrq->fixed == 1 ) { + lp->TxRateControl[index] = 0x0200; + } else { + lp->TxRateControl[index] = 0x03FF; + } + } + else if( rrq->value > 36 * MEGABIT && + rrq->value <= 48 * MEGABIT ) { + if( rrq->fixed == 1 ) { + lp->TxRateControl[index] = 0x0400; + } else { + lp->TxRateControl[index] = 0x07FF; + } + } + else if( rrq->value > 48 * MEGABIT && + rrq->value <= 54 * MEGABIT ) { + if( rrq->fixed == 1 ) { + lp->TxRateControl[index] = 0x0800; + } else { + lp->TxRateControl[index] = 0x0FFF; + } + } + else if( rrq->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 { + rrq->value = 0; + ret = -EINVAL; + goto out_unlock; + } + + +#else + + if( rrq->value > 0 && + rrq->value <= 1 * MEGABIT ) { + lp->TxRateControl[0] = 1; + } + else if( rrq->value > 1 * MEGABIT && + rrq->value <= 2 * MEGABIT ) { + if( rrq->fixed ) { + lp->TxRateControl[0] = 2; + } else { + lp->TxRateControl[0] = 6; + } + } + else if( rrq->value > 2 * MEGABIT && + rrq->value <= 5 * MEGABIT ) { + if( rrq->fixed ) { + lp->TxRateControl[0] = 4; + } else { + lp->TxRateControl[0] = 7; + } + } + else if( rrq->value > 5 * MEGABIT && + rrq->value <= 11 * MEGABIT ) { + if( rrq->fixed) { + lp->TxRateControl[0] = 5; + } else { + lp->TxRateControl[0] = 3; + } + } + else if( rrq->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 { + rrq->value = 0; + ret = -EINVAL; + goto out_unlock; + } + +#endif // WARP + + + /* Commit the adapter parameters */ + wl_apply( lp ); + +out_unlock: + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + 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 + * + ******************************************************************************/ +static int wireless_get_rate(struct net_device *dev, struct iw_request_info *info, struct iw_param *rrq, char *extra) + +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = 0; + int status = -1; + hcf_16 txRate; + /*------------------------------------------------------------------------*/ + + + DBG_FUNC( "wireless_get_rate" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + /* Get the current transmit rate from the adapter */ + lp->ltvRecord.len = 1 + ( sizeof(txRate)/sizeof(hcf_16)); + lp->ltvRecord.typ = CFG_CUR_TX_RATE; + + status = hcf_get_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord )); + + if( status == HCF_SUCCESS ) { +#ifdef WARP + + txRate = CNV_LITTLE_TO_INT( lp->ltvRecord.u.u16[0] ); + + 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 + + rrq->value = txRate * MEGABIT; + } else { + rrq->value = 0; + ret = -EFAULT; + } + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + DBG_LEAVE( DbgInfo ); + return ret; +} // wireless_get_rate +/*============================================================================*/ + + + + +#if 0 //;? Not used anymore +/******************************************************************************* + * 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(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + 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 )); + } + +out: + DBG_LEAVE( DbgInfo ); + return ret; +} // wireless_get_private_interface +/*============================================================================*/ +#endif + + + +#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 + * + ******************************************************************************/ +static int wireless_set_scan(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra) +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = 0; + int status = -1; + int retries = 0; + /*------------------------------------------------------------------------*/ + + //;? Note: shows results as trace, retruns always 0 unless BUSY + + DBG_FUNC( "wireless_set_scan" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + /* + * This looks like a nice place to test if the HCF is still + * communicating with the card. It seems that sometimes BAP_1 + * gets corrupted. By looking at the comments in HCF the + * cause is still a mistery. Okay, the communication to the + * card is dead, reset the card to revive. + */ + if((lp->hcfCtx.IFB_CardStat & CARD_STAT_DEFUNCT) != 0) + { + DBG_TRACE( DbgInfo, "CARD is in DEFUNCT mode, reset it to bring it back to life\n" ); + wl_reset( dev ); + } + +retry: + /* 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 ); + + // Holding the lock too long, make a gap to allow other processes + wl_unlock(lp, &flags); + wl_lock( lp, &flags ); + + if( status != HCF_SUCCESS ) { + //Recovery + retries++; + if(retries <= 10) { + DBG_TRACE( DbgInfo, "Reset card to recover, attempt: %d\n", retries ); + wl_reset( dev ); + + // Holding the lock too long, make a gap to allow other processes + wl_unlock(lp, &flags); + wl_lock( lp, &flags ); + + goto retry; + } + } + + /* 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 )); + + // Holding the lock too long, make a gap to allow other processes + wl_unlock(lp, &flags); + wl_lock( lp, &flags ); + + 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" ); + } else { + DBG_TRACE( DbgInfo, "INITIATE SCAN FAILED...\n" ); + } + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + 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 + * + ******************************************************************************/ +static int wireless_get_scan(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra) +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = 0; + int count; + char *buf; + char *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(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + /* If the scan is not done, tell the calling process to try again later */ + if( !lp->probe_results.scan_complete ) { + ret = -EAGAIN; + goto out_unlock; + } + + DBG_TRACE( DbgInfo, "SCAN COMPLETE, Num of APs: %d\n", + lp->probe_results.num_aps ); + + buf = extra; + buf_end = extra + 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(info, 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(info, 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 | IW_QUAL_DBM; + iwe.len = IW_EV_QUAL_LEN; + + buf = IWE_STREAM_ADD_EVENT(info, 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(info, 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(info, 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(info, 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(info, 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(info, buf, buf_end, &iwe, msg); + } + + /* Add other custom info in formatted string format as needed... */ +#endif + } + + data->length = buf - extra; + +out_unlock: + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + DBG_LEAVE( DbgInfo ); + return ret; +} // wireless_get_scan +/*============================================================================*/ + +#endif // WIRELESS_EXT > 13 + + +#if WIRELESS_EXT > 17 + +static int wireless_set_auth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *data, char *extra) +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret; + int iwa_idx = data->flags & IW_AUTH_INDEX; + int iwa_val = data->value; + + DBG_FUNC( "wireless_set_auth" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + switch (iwa_idx) { + case IW_AUTH_WPA_VERSION: + DBG_TRACE( DbgInfo, "IW_AUTH_WPA_VERSION\n"); + /* We do support WPA only; how should DISABLED be treated? */ + if (iwa_val == IW_AUTH_WPA_VERSION_WPA) + ret = 0; + else + ret = -EINVAL; + break; + + case IW_AUTH_WPA_ENABLED: + DBG_TRACE( DbgInfo, "IW_AUTH_WPA_ENABLED: val = %d\n", iwa_val); + if (iwa_val) + lp->EnableEncryption = 2; + else + lp->EnableEncryption = 0; + ret = 0; + break; + + case IW_AUTH_TKIP_COUNTERMEASURES: + DBG_TRACE( DbgInfo, "IW_AUTH_TKIP_COUNTERMEASURES\n"); + lp->driverEnable = !iwa_val; + if(lp->driverEnable) + hcf_cntl(&(lp->hcfCtx), HCF_CNTL_ENABLE | HCF_PORT_0); + else + hcf_cntl(&(lp->hcfCtx), HCF_CNTL_DISABLE | HCF_PORT_0); + ret = 0; + break; + + case IW_AUTH_DROP_UNENCRYPTED: + DBG_TRACE( DbgInfo, "IW_AUTH_DROP_UNENCRYPTED\n"); + /* We do not actually do anything here, just to silence + * wpa_supplicant */ + ret = 0; + break; + + case IW_AUTH_CIPHER_PAIRWISE: + DBG_TRACE( DbgInfo, "IW_AUTH_CIPHER_PAIRWISE\n"); + /* not implemented, return an error */ + ret = -EINVAL; + break; + + case IW_AUTH_CIPHER_GROUP: + DBG_TRACE( DbgInfo, "IW_AUTH_CIPHER_GROUP\n"); + /* not implemented, return an error */ + ret = -EINVAL; + break; + + case IW_AUTH_KEY_MGMT: + DBG_TRACE( DbgInfo, "IW_AUTH_KEY_MGMT\n"); + /* not implemented, return an error */ + ret = -EINVAL; + break; + + case IW_AUTH_80211_AUTH_ALG: + DBG_TRACE( DbgInfo, "IW_AUTH_80211_AUTH_ALG\n"); + /* not implemented, return an error */ + ret = -EINVAL; + break; + + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + DBG_TRACE( DbgInfo, "IW_AUTH_RX_UNENCRYPTED_EAPOL\n"); + /* not implemented, return an error */ + ret = -EINVAL; + break; + + case IW_AUTH_ROAMING_CONTROL: + DBG_TRACE( DbgInfo, "IW_AUTH_ROAMING_CONTROL\n"); + /* not implemented, return an error */ + ret = -EINVAL; + break; + + case IW_AUTH_PRIVACY_INVOKED: + DBG_TRACE( DbgInfo, "IW_AUTH_PRIVACY_INVOKED\n"); + /* not implemented, return an error */ + ret = -EINVAL; + break; + + default: + DBG_TRACE( DbgInfo, "IW_AUTH_?? (%d) unknown\n", iwa_idx); + /* return an error */ + ret = -EINVAL; + break; + } + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + DBG_LEAVE( DbgInfo ); + return ret; +} // wireless_set_auth +/*============================================================================*/ + + + +static int hermes_set_key(ltv_t *ltv, int alg, int key_idx, u8 *addr, + int set_tx, u8 *seq, u8 *key, size_t key_len) +{ + int ret = -EINVAL; + // int count = 0; + int buf_idx = 0; + hcf_8 tsc[IW_ENCODE_SEQ_MAX_SIZE] = + { 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00 }; + + DBG_FUNC( "hermes_set_key" ); + DBG_ENTER( DbgInfo ); + + /* + * Check the key index here; if 0, load as Pairwise Key, otherwise, + * load as a group key. Note that for the Hermes, the RIDs for + * group/pariwise keys are different from each other and different + * than the default WEP keys as well. + */ + switch (alg) + { + case IW_ENCODE_ALG_TKIP: + DBG_TRACE( DbgInfo, "IW_ENCODE_ALG_TKIP: key(%d)\n", key_idx); +#if 0 + /* + * Make sure that there is no data queued up in the firmware + * before setting the TKIP keys. If this check is not + * performed, some data may be sent out with incorrect MIC + * and cause synchronizarion errors with the AP + */ + /* Check every 1ms for 100ms */ + for( count = 0; count < 100; count++ ) + { + usleep( 1000 ); + + ltv.len = 2; + ltv.typ = 0xFD91; // This RID not defined in HCF yet!!! + ltv.u.u16[0] = 0; + + wl_get_info( sock, <v, ifname ); + + if( ltv.u.u16[0] == 0 ) + { + break; + } + } + + if( count == 100 ) + { + wpa_printf( MSG_DEBUG, "Timed out waiting for TxQ!" ); + } +#endif + + switch (key_idx) { + case 0: + ltv->len = 28; + ltv->typ = CFG_ADD_TKIP_MAPPED_KEY; + + /* Load the BSSID */ + memcpy(<v->u.u8[buf_idx], addr, ETH_ALEN); + buf_idx += ETH_ALEN; + + /* Load the TKIP key */ + memcpy(<v->u.u8[buf_idx], &key[0], 16); + buf_idx += 16; + + /* Load the TSC */ + memcpy(<v->u.u8[buf_idx], tsc, IW_ENCODE_SEQ_MAX_SIZE); + buf_idx += IW_ENCODE_SEQ_MAX_SIZE; + + /* Load the RSC */ + memcpy(<v->u.u8[buf_idx], seq, IW_ENCODE_SEQ_MAX_SIZE); + buf_idx += IW_ENCODE_SEQ_MAX_SIZE; + + /* Load the TxMIC key */ + memcpy(<v->u.u8[buf_idx], &key[16], 8); + buf_idx += 8; + + /* Load the RxMIC key */ + memcpy(<v->u.u8[buf_idx], &key[24], 8); + + ret = 0; + break; + case 1: + case 2: + case 3: + ltv->len = 26; + ltv->typ = CFG_ADD_TKIP_DEFAULT_KEY; + + /* Load the key Index */ + ltv->u.u16[buf_idx] = key_idx; + /* If this is a Tx Key, set bit 8000 */ + if(set_tx) + ltv->u.u16[buf_idx] |= 0x8000; + buf_idx += 2; + + /* Load the RSC */ + memcpy(<v->u.u8[buf_idx], seq, IW_ENCODE_SEQ_MAX_SIZE); + buf_idx += IW_ENCODE_SEQ_MAX_SIZE; + + /* Load the TKIP, TxMIC, and RxMIC keys in one shot, because in + CFG_ADD_TKIP_DEFAULT_KEY they are back-to-back */ + memcpy(<v->u.u8[buf_idx], key, key_len); + buf_idx += key_len; + + /* Load the TSC */ + memcpy(<v->u.u8[buf_idx], tsc, IW_ENCODE_SEQ_MAX_SIZE); + + ltv->u.u16[0] = CNV_INT_TO_LITTLE(ltv->u.u16[0]); + + ret = 0; + break; + default: + break; + } + + break; + + case IW_ENCODE_ALG_WEP: + DBG_TRACE( DbgInfo, "IW_ENCODE_ALG_WEP: key(%d)\n", key_idx); + break; + + case IW_ENCODE_ALG_CCMP: + DBG_TRACE( DbgInfo, "IW_ENCODE_ALG_CCMP: key(%d)\n", key_idx); + break; + + case IW_ENCODE_ALG_NONE: + DBG_TRACE( DbgInfo, "IW_ENCODE_ALG_NONE: key(%d)\n", key_idx); + switch (key_idx) { + case 0: + if (memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) { + //if (addr != NULL) { + ltv->len = 7; + ltv->typ = CFG_REMOVE_TKIP_MAPPED_KEY; + memcpy(<v->u.u8[0], addr, ETH_ALEN); + ret = 0; + } + break; + case 1: + case 2: + case 3: + /* Clear the Group TKIP keys by index */ + ltv->len = 2; + ltv->typ = CFG_REMOVE_TKIP_DEFAULT_KEY; + ltv->u.u16[0] = key_idx; + + ret = 0; + break; + default: + break; + } + break; + default: + DBG_TRACE( DbgInfo, "IW_ENCODE_??: key(%d)\n", key_idx); + break; + } + + DBG_LEAVE( DbgInfo ); + return ret; +} // hermes_set_key +/*============================================================================*/ + + + +static int wireless_set_encodeext (struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *keybuf) +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret; + int key_idx = (erq->flags&IW_ENCODE_INDEX) - 1; + ltv_t ltv; + struct iw_encode_ext *ext = (struct iw_encode_ext *)keybuf; + + DBG_FUNC( "wireless_set_encodeext" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + if (sizeof(ext->rx_seq) != 8) { + DBG_TRACE(DbgInfo, "rz_seq size mismatch\n"); + DBG_LEAVE(DbgInfo); + return -EINVAL; + } + + /* Handle WEP keys via the old set encode procedure */ + if(ext->alg == IW_ENCODE_ALG_WEP) { + struct iw_point wep_erq; + char *wep_keybuf; + + /* Build request structure */ + wep_erq.flags = erq->flags; // take over flags with key index + wep_erq.length = ext->key_len; // take length from extended key info + wep_keybuf = ext->key; // pointer to the key text + + /* Call wireless_set_encode tot handle the WEP key */ + ret = wireless_set_encode(dev, info, &wep_erq, wep_keybuf); + goto out; + } + + /* Proceed for extended encode functions for WAP and NONE */ + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + memset(<v, 0, sizeof(ltv)); + ret = hermes_set_key(<v, ext->alg, key_idx, ext->addr.sa_data, + ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY, + ext->rx_seq, ext->key, ext->key_len); + + if (ret != 0) { + DBG_TRACE( DbgInfo, "hermes_set_key returned != 0, key not set\n"); + goto out_unlock; + } + + /* Put the key in HCF */ + ret = hcf_put_info(&(lp->hcfCtx), (LTVP)<v); + +out_unlock: + if(ret == HCF_SUCCESS) { + DBG_TRACE( DbgInfo, "Put key info succes\n"); + } else { + DBG_TRACE( DbgInfo, "Put key info failed, key not set\n"); + } + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + DBG_LEAVE( DbgInfo ); + return ret; +} // wireless_set_encodeext +/*============================================================================*/ + + + +static int wireless_get_genie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) + +{ + struct wl_private *lp = wl_priv(dev); + unsigned long flags; + int ret = 0; + ltv_t ltv; + + DBG_FUNC( "wireless_get_genie" ); + DBG_ENTER( DbgInfo ); + + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + ret = -EBUSY; + goto out; + } + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + + memset(<v, 0, sizeof(ltv)); + ltv.len = 2; + ltv.typ = CFG_SET_WPA_AUTH_KEY_MGMT_SUITE; + lp->AuthKeyMgmtSuite = ltv.u.u16[0] = 4; + ltv.u.u16[0] = CNV_INT_TO_LITTLE(ltv.u.u16[0]); + + ret = hcf_put_info(&(lp->hcfCtx), (LTVP)<v); + + wl_act_int_on( lp ); + + wl_unlock(lp, &flags); + +out: + DBG_LEAVE( DbgInfo ); + return ret; +} +/*============================================================================*/ + + +#endif // WIRELESS_EXT > 17 + +/******************************************************************************* + * 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 = wl_priv(dev); + /*------------------------------------------------------------------------*/ + + + DBG_FUNC( "wl_wireless_stats" ); + DBG_ENTER(DbgInfo); + DBG_PARAM(DbgInfo, "dev", "%s (0x%p)", dev->name, dev); + + pStats = 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 tallies; + 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 )); + + pStats->qual.updated |= (IW_QUAL_QUAL_UPDATED | + IW_QUAL_LEVEL_UPDATED | + IW_QUAL_NOISE_UPDATED | + IW_QUAL_DBM); +#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 ); + + pStats->qual.updated |= (IW_QUAL_QUAL_UPDATED | + IW_QUAL_LEVEL_UPDATED | + IW_QUAL_NOISE_UPDATED); +#endif /* USE_DBM */ + } else { + memset( &( pStats->qual ), 0, sizeof( pStats->qual )); + } + + /* Get the current tallies from the adapter */ + /* Only possible when the device is open */ + if(lp->portState == WVLAN_PORT_STATE_DISABLED) { + if( wl_get_tallies( lp, &tallies ) == 0 ) { + /* 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 = tallies.RxWEPUndecryptable; + pStats->discard.misc = tallies.TxDiscards + + tallies.RxFCSErrors + + //tallies.RxDiscardsNoBuffer + + tallies.TxDiscardsWrongSA; + //;? Extra taken over from Linux driver based on 7.18 version + pStats->discard.retries = tallies.TxRetryLimitExceeded; + pStats->discard.fragment = tallies.RxMsgInBadMsgFragments; + } else { + memset( &( pStats->discard ), 0, sizeof( pStats->discard )); + } + } 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 = wl_priv(dev); + struct iw_statistics *pStats = NULL; + /*------------------------------------------------------------------------*/ + + DBG_FUNC( "wl_get_wireless_stats" ); + DBG_ENTER(DbgInfo); + + wl_lock( lp, &flags ); + + wl_act_int_off( lp ); + +#ifdef USE_RTS + if( lp->useRTS == 1 ) { + DBG_TRACE( DbgInfo, "Skipping wireless stats, in RTS mode\n" ); + } else +#endif + { + pStats = wl_wireless_stats( dev ); + } + wl_act_int_on( lp ); + + wl_unlock(lp, &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 ) +{ + struct iw_quality wstats; + int status; + u_char stats[2]; + DESC_STRCT desc[1]; + struct wl_private *lp = wl_priv(dev); + /*------------------------------------------------------------------------*/ + + /* shortcut */ + if (!lp->spy_data.spy_number) { + return; + } + + /* Gather wireless spy statistics: for each packet, compare the source + address with out list, and if match, get the stats. */ + 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 ) { + wstats.level = (u_char) dbm(stats[1]); + wstats.noise = (u_char) dbm(stats[0]); + wstats.qual = wstats.level > wstats.noise ? wstats.level - wstats.noise : 0; + + wstats.updated = (IW_QUAL_QUAL_UPDATED | + IW_QUAL_LEVEL_UPDATED | + IW_QUAL_NOISE_UPDATED | + IW_QUAL_DBM); + + wireless_spy_update( dev, mac, &wstats ); + } +} // 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 = wl_priv(dev); + /*------------------------------------------------------------------------*/ + + + 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 = wl_priv(dev); + /*------------------------------------------------------------------------*/ + + + 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 = wl_priv(dev); + /*------------------------------------------------------------------------*/ + + + 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 = wl_priv(dev); + int index = 0; + /*------------------------------------------------------------------------*/ + + + 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 = wl_priv(dev); + int status; + /*------------------------------------------------------------------------*/ + + + /* 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 = wl_priv(dev); + int key_idx; + char *addr1; + char *addr2; + WVLAN_RX_WMP_HDR *hdr; + /*------------------------------------------------------------------------*/ + + + 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 */ +#if DBG + sprintf( msg, "MLME-MICHAELMICFAILURE.indication(keyid=%d %scast addr=" + "%s)", key_idx, addr1[0] & 0x01 ? "broad" : "uni", + DbgHwAddr( addr2 )); +#endif + 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 = wl_priv(dev); + int status; + PROBE_RESP data; + hcf_16 length; + hcf_8 *wpa_ie; + /*------------------------------------------------------------------------*/ + + + 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 +/*============================================================================*/ +/* Structures to export the Wireless Handlers */ + +static const iw_handler wl_handler[] = +{ + (iw_handler) wireless_commit, /* SIOCSIWCOMMIT */ + (iw_handler) wireless_get_protocol, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) wireless_set_frequency, /* SIOCSIWFREQ */ + (iw_handler) wireless_get_frequency, /* SIOCGIWFREQ */ + (iw_handler) wireless_set_porttype, /* SIOCSIWMODE */ + (iw_handler) wireless_get_porttype, /* SIOCGIWMODE */ + (iw_handler) wireless_set_sensitivity, /* SIOCSIWSENS */ + (iw_handler) wireless_get_sensitivity, /* SIOCGIWSENS */ + (iw_handler) NULL , /* SIOCSIWRANGE */ + (iw_handler) wireless_get_range, /* SIOCGIWRANGE */ + (iw_handler) NULL , /* SIOCSIWPRIV */ + (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */ + (iw_handler) NULL , /* SIOCSIWSTATS */ + (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */ + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + NULL, /* SIOCSIWTHRSPY */ + NULL, /* SIOCGIWTHRSPY */ + (iw_handler) NULL, /* SIOCSIWAP */ +#if 1 //;? (HCF_TYPE) & HCF_TYPE_STA + (iw_handler) wireless_get_bssid, /* SIOCGIWAP */ +#else + (iw_handler) NULL, /* SIOCGIWAP */ +#endif + (iw_handler) NULL, /* SIOCSIWMLME */ + (iw_handler) wireless_get_ap_list, /* SIOCGIWAPLIST */ + (iw_handler) wireless_set_scan, /* SIOCSIWSCAN */ + (iw_handler) wireless_get_scan, /* SIOCGIWSCAN */ + (iw_handler) wireless_set_essid, /* SIOCSIWESSID */ + (iw_handler) wireless_get_essid, /* SIOCGIWESSID */ + (iw_handler) wireless_set_nickname, /* SIOCSIWNICKN */ + (iw_handler) wireless_get_nickname, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) wireless_set_rate, /* SIOCSIWRATE */ + (iw_handler) wireless_get_rate, /* SIOCGIWRATE */ + (iw_handler) wireless_set_rts_threshold,/* SIOCSIWRTS */ + (iw_handler) wireless_get_rts_threshold,/* SIOCGIWRTS */ + (iw_handler) NULL, /* SIOCSIWFRAG */ + (iw_handler) NULL, /* SIOCGIWFRAG */ + (iw_handler) NULL, /* SIOCSIWTXPOW */ + (iw_handler) wireless_get_tx_power, /* SIOCGIWTXPOW */ + (iw_handler) NULL, /* SIOCSIWRETRY */ + (iw_handler) NULL, /* SIOCGIWRETRY */ + (iw_handler) wireless_set_encode, /* SIOCSIWENCODE */ + (iw_handler) wireless_get_encode, /* SIOCGIWENCODE */ + (iw_handler) wireless_set_power, /* SIOCSIWPOWER */ + (iw_handler) wireless_get_power, /* SIOCGIWPOWER */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) wireless_get_genie, /* SIOCSIWGENIE */ + (iw_handler) NULL, /* SIOCGIWGENIE */ + (iw_handler) wireless_set_auth, /* SIOCSIWAUTH */ + (iw_handler) NULL, /* SIOCGIWAUTH */ + (iw_handler) wireless_set_encodeext, /* SIOCSIWENCODEEXT */ + (iw_handler) NULL, /* SIOCGIWENCODEEXT */ + (iw_handler) NULL, /* SIOCSIWPMKSA */ + (iw_handler) NULL, /* -- hole -- */ +}; + +static const iw_handler wl_private_handler[] = +{ /* SIOCIWFIRSTPRIV + */ + wvlan_set_netname, /* 0: SIOCSIWNETNAME */ + wvlan_get_netname, /* 1: SIOCGIWNETNAME */ + wvlan_set_station_nickname, /* 2: SIOCSIWSTANAME */ + wvlan_get_station_nickname, /* 3: SIOCGIWSTANAME */ +#if 1 //;? (HCF_TYPE) & HCF_TYPE_STA + wvlan_set_porttype, /* 4: SIOCSIWPORTTYPE */ + wvlan_get_porttype, /* 5: SIOCGIWPORTTYPE */ +#endif +}; + +struct iw_priv_args wl_priv_args[] = { + {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" }, +#if 1 //;? #if (HCF_TYPE) & HCF_TYPE_STA + {SIOCSIWPORTTYPE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "sport_type" }, + {SIOCGIWPORTTYPE, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gport_type" }, +#endif +}; + +const struct iw_handler_def wl_iw_handler_def = +{ + .num_private = sizeof(wl_private_handler) / sizeof(iw_handler), + .private = (iw_handler *) wl_private_handler, + .private_args = (struct iw_priv_args *) wl_priv_args, + .num_private_args = sizeof(wl_priv_args) / sizeof(struct iw_priv_args), + .num_standard = sizeof(wl_handler) / sizeof(iw_handler), + .standard = (iw_handler *) wl_handler, + .get_wireless_stats = wl_get_wireless_stats, +}; + +#endif // WIRELESS_EXT |