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