2009-02-05 02:05:50 +03:00
/* Helpers for managing scan queues
*
* See copyright notice in main . c
*/
# include <linux/kernel.h>
# include <linux/string.h>
2009-06-19 02:21:33 +04:00
# include <linux/ieee80211.h>
# include <net/cfg80211.h>
2009-02-05 02:05:50 +03:00
# include "hermes.h"
# include "orinoco.h"
2009-06-19 02:21:33 +04:00
# include "main.h"
2009-02-05 02:05:50 +03:00
# include "scan.h"
2009-06-19 02:21:33 +04:00
# define ZERO_DBM_OFFSET 0x95
# define MAX_SIGNAL_LEVEL 0x8A
# define MIN_SIGNAL_LEVEL 0x2F
2009-02-05 02:05:50 +03:00
2009-06-19 02:21:33 +04:00
# define SIGNAL_TO_DBM(x) \
( clamp_t ( s32 , ( x ) , MIN_SIGNAL_LEVEL , MAX_SIGNAL_LEVEL ) \
- ZERO_DBM_OFFSET )
# define SIGNAL_TO_MBM(x) (SIGNAL_TO_DBM(x) * 100)
2009-02-05 02:05:50 +03:00
2009-06-19 02:21:33 +04:00
static int symbol_build_supp_rates ( u8 * buf , const __le16 * rates )
2009-02-05 02:05:50 +03:00
{
2009-06-19 02:21:33 +04:00
int i ;
u8 rate ;
buf [ 0 ] = WLAN_EID_SUPP_RATES ;
for ( i = 0 ; i < 5 ; i + + ) {
rate = le16_to_cpu ( rates [ i ] ) ;
/* NULL terminated */
if ( rate = = 0x0 )
break ;
buf [ i + 2 ] = rate ;
2009-02-05 02:05:50 +03:00
}
2009-06-19 02:21:33 +04:00
buf [ 1 ] = i ;
2009-02-05 02:05:50 +03:00
2009-06-19 02:21:33 +04:00
return i + 2 ;
2009-02-05 02:05:50 +03:00
}
2009-06-19 02:21:33 +04:00
static int prism_build_supp_rates ( u8 * buf , const u8 * rates )
2009-02-05 02:05:50 +03:00
{
int i ;
2009-06-19 02:21:33 +04:00
buf [ 0 ] = WLAN_EID_SUPP_RATES ;
for ( i = 0 ; i < 8 ; i + + ) {
/* NULL terminated */
if ( rates [ i ] = = 0x0 )
break ;
buf [ i + 2 ] = rates [ i ] ;
}
buf [ 1 ] = i ;
/* We might still have another 2 rates, which need to go in
* extended supported rates */
if ( i = = 8 & & rates [ i ] > 0 ) {
buf [ 10 ] = WLAN_EID_EXT_SUPP_RATES ;
for ( ; i < 10 ; i + + ) {
/* NULL terminated */
if ( rates [ i ] = = 0x0 )
break ;
buf [ i + 2 ] = rates [ i ] ;
2009-02-05 02:05:50 +03:00
}
2009-06-19 02:21:33 +04:00
buf [ 11 ] = i - 8 ;
2009-02-05 02:05:50 +03:00
}
2009-06-19 02:21:33 +04:00
return ( i < 8 ) ? i + 2 : i + 4 ;
2009-02-05 02:05:50 +03:00
}
2009-06-19 02:21:33 +04:00
static void orinoco_add_hostscan_result ( struct orinoco_private * priv ,
const union hermes_scan_info * bss )
2009-02-05 02:05:50 +03:00
{
2009-06-19 02:21:33 +04:00
struct wiphy * wiphy = priv_to_wiphy ( priv ) ;
struct ieee80211_channel * channel ;
u8 * ie ;
u8 ie_buf [ 46 ] ;
u64 timestamp ;
s32 signal ;
u16 capability ;
u16 beacon_interval ;
int ie_len ;
int freq ;
int len ;
len = le16_to_cpu ( bss - > a . essid_len ) ;
/* Reconstruct SSID and bitrate IEs to pass up */
ie_buf [ 0 ] = WLAN_EID_SSID ;
ie_buf [ 1 ] = len ;
memcpy ( & ie_buf [ 2 ] , bss - > a . essid , len ) ;
ie = ie_buf + len + 2 ;
ie_len = ie_buf [ 1 ] + 2 ;
switch ( priv - > firmware_type ) {
case FIRMWARE_TYPE_SYMBOL :
ie_len + = symbol_build_supp_rates ( ie , bss - > s . rates ) ;
2009-02-05 02:05:50 +03:00
break ;
2009-06-19 02:21:33 +04:00
case FIRMWARE_TYPE_INTERSIL :
ie_len + = prism_build_supp_rates ( ie , bss - > p . rates ) ;
break ;
2009-02-05 02:05:50 +03:00
2009-06-19 02:21:33 +04:00
case FIRMWARE_TYPE_AGERE :
default :
break ;
2009-02-05 02:05:50 +03:00
}
2009-06-19 02:21:33 +04:00
freq = ieee80211_dsss_chan_to_freq ( le16_to_cpu ( bss - > a . channel ) ) ;
channel = ieee80211_get_channel ( wiphy , freq ) ;
timestamp = 0 ;
capability = le16_to_cpu ( bss - > a . capabilities ) ;
beacon_interval = le16_to_cpu ( bss - > a . beacon_interv ) ;
signal = SIGNAL_TO_MBM ( le16_to_cpu ( bss - > a . level ) ) ;
cfg80211_inform_bss ( wiphy , channel , bss - > a . bssid , timestamp ,
capability , beacon_interval , ie_buf , ie_len ,
signal , GFP_KERNEL ) ;
2009-02-05 02:05:50 +03:00
}
2009-06-19 02:21:33 +04:00
void orinoco_add_extscan_result ( struct orinoco_private * priv ,
struct agere_ext_scan_info * bss ,
size_t len )
2009-02-05 02:05:50 +03:00
{
2009-06-19 02:21:33 +04:00
struct wiphy * wiphy = priv_to_wiphy ( priv ) ;
struct ieee80211_channel * channel ;
u8 * ie ;
u64 timestamp ;
s32 signal ;
u16 capability ;
u16 beacon_interval ;
size_t ie_len ;
int chan , freq ;
ie_len = len - sizeof ( * bss ) ;
ie = orinoco_get_ie ( bss - > data , ie_len , WLAN_EID_DS_PARAMS ) ;
chan = ie ? ie [ 2 ] : 0 ;
freq = ieee80211_dsss_chan_to_freq ( chan ) ;
channel = ieee80211_get_channel ( wiphy , freq ) ;
timestamp = le64_to_cpu ( bss - > timestamp ) ;
capability = le16_to_cpu ( bss - > capabilities ) ;
beacon_interval = le16_to_cpu ( bss - > beacon_interval ) ;
ie = bss - > data ;
signal = SIGNAL_TO_MBM ( bss - > level ) ;
cfg80211_inform_bss ( wiphy , channel , bss - > bssid , timestamp ,
capability , beacon_interval , ie , ie_len ,
signal , GFP_KERNEL ) ;
}
void orinoco_add_hostscan_results ( struct orinoco_private * priv ,
unsigned char * buf ,
size_t len )
{
int offset ; /* In the scan data */
size_t atom_len ;
bool abort = false ;
2009-02-05 02:05:50 +03:00
switch ( priv - > firmware_type ) {
case FIRMWARE_TYPE_AGERE :
atom_len = sizeof ( struct agere_scan_apinfo ) ;
offset = 0 ;
break ;
2009-06-19 02:21:33 +04:00
2009-02-05 02:05:50 +03:00
case FIRMWARE_TYPE_SYMBOL :
/* Lack of documentation necessitates this hack.
* Different firmwares have 68 or 76 byte long atoms .
* We try modulo first . If the length divides by both ,
* we check what would be the channel in the second
* frame for a 68 - byte atom . 76 - byte atoms have 0 there .
* Valid channel cannot be 0. */
if ( len % 76 )
atom_len = 68 ;
else if ( len % 68 )
atom_len = 76 ;
else if ( len > = 1292 & & buf [ 68 ] = = 0 )
atom_len = 76 ;
else
atom_len = 68 ;
offset = 0 ;
break ;
2009-06-19 02:21:33 +04:00
2009-02-05 02:05:50 +03:00
case FIRMWARE_TYPE_INTERSIL :
offset = 4 ;
if ( priv - > has_hostscan ) {
atom_len = le16_to_cpup ( ( __le16 * ) buf ) ;
/* Sanity check for atom_len */
if ( atom_len < sizeof ( struct prism2_scan_apinfo ) ) {
printk ( KERN_ERR " %s: Invalid atom_len in scan "
2009-07-02 23:26:45 +04:00
" data: %zu \n " , priv - > ndev - > name ,
2009-02-05 02:05:50 +03:00
atom_len ) ;
2009-06-19 02:21:33 +04:00
abort = true ;
goto scan_abort ;
2009-02-05 02:05:50 +03:00
}
} else
atom_len = offsetof ( struct prism2_scan_apinfo , atim ) ;
break ;
2009-06-19 02:21:33 +04:00
2009-02-05 02:05:50 +03:00
default :
2009-06-19 02:21:33 +04:00
abort = true ;
goto scan_abort ;
2009-02-05 02:05:50 +03:00
}
/* Check that we got an whole number of atoms */
if ( ( len - offset ) % atom_len ) {
2009-07-02 23:26:45 +04:00
printk ( KERN_ERR " %s: Unexpected scan data length %zu, "
" atom_len %zu, offset %d \n " , priv - > ndev - > name , len ,
2009-02-05 02:05:50 +03:00
atom_len , offset ) ;
2009-06-19 02:21:33 +04:00
abort = true ;
goto scan_abort ;
2009-02-05 02:05:50 +03:00
}
2009-06-19 02:21:33 +04:00
/* Process the entries one by one */
2009-02-05 02:05:50 +03:00
for ( ; offset + atom_len < = len ; offset + = atom_len ) {
2009-06-19 02:21:33 +04:00
union hermes_scan_info * atom ;
2009-02-05 02:05:50 +03:00
atom = ( union hermes_scan_info * ) ( buf + offset ) ;
2009-06-19 02:21:33 +04:00
orinoco_add_hostscan_result ( priv , atom ) ;
2009-02-05 02:05:50 +03:00
}
2009-06-19 02:21:33 +04:00
scan_abort :
if ( priv - > scan_request ) {
cfg80211_scan_done ( priv - > scan_request , abort ) ;
priv - > scan_request = NULL ;
}
2009-02-05 02:05:50 +03:00
}