2009-09-30 01:27:28 +04:00
/*
* This file implement the Wireless Extensions spy API .
*
* Authors : Jean Tourrilhes - HPL - < jt @ hpl . hp . com >
* Copyright ( c ) 1997 - 2007 Jean Tourrilhes , All Rights Reserved .
*
* ( As all part of the Linux kernel , this file is GPL )
*/
# include <linux/wireless.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
2011-07-15 19:47:34 +04:00
# include <linux/export.h>
2009-09-30 01:27:28 +04:00
# include <net/iw_handler.h>
# include <net/arp.h>
# include <net/wext.h>
static inline struct iw_spy_data * get_spydata ( struct net_device * dev )
{
/* This is the new way */
if ( dev - > wireless_data )
return dev - > wireless_data - > spy_data ;
return NULL ;
}
int iw_handler_set_spy ( struct net_device * dev ,
struct iw_request_info * info ,
union iwreq_data * wrqu ,
char * extra )
{
struct iw_spy_data * spydata = get_spydata ( dev ) ;
struct sockaddr * address = ( struct sockaddr * ) extra ;
/* Make sure driver is not buggy or using the old API */
if ( ! spydata )
return - EOPNOTSUPP ;
/* Disable spy collection while we copy the addresses.
* While we copy addresses , any call to wireless_spy_update ( )
* will NOP . This is OK , as anyway the addresses are changing . */
spydata - > spy_number = 0 ;
/* We want to operate without locking, because wireless_spy_update()
* most likely will happen in the interrupt handler , and therefore
* have its own locking constraints and needs performance .
* The rtnl_lock ( ) make sure we don ' t race with the other iw_handlers .
* This make sure wireless_spy_update ( ) " see " that the spy list
* is temporarily disabled . */
smp_wmb ( ) ;
/* Are there are addresses to copy? */
if ( wrqu - > data . length > 0 ) {
int i ;
/* Copy addresses */
for ( i = 0 ; i < wrqu - > data . length ; i + + )
memcpy ( spydata - > spy_address [ i ] , address [ i ] . sa_data ,
ETH_ALEN ) ;
/* Reset stats */
memset ( spydata - > spy_stat , 0 ,
sizeof ( struct iw_quality ) * IW_MAX_SPY ) ;
}
/* Make sure above is updated before re-enabling */
smp_wmb ( ) ;
/* Enable addresses */
spydata - > spy_number = wrqu - > data . length ;
return 0 ;
}
EXPORT_SYMBOL ( iw_handler_set_spy ) ;
int iw_handler_get_spy ( struct net_device * dev ,
struct iw_request_info * info ,
union iwreq_data * wrqu ,
char * extra )
{
struct iw_spy_data * spydata = get_spydata ( dev ) ;
struct sockaddr * address = ( struct sockaddr * ) extra ;
int i ;
/* Make sure driver is not buggy or using the old API */
if ( ! spydata )
return - EOPNOTSUPP ;
wrqu - > data . length = spydata - > spy_number ;
/* Copy addresses. */
for ( i = 0 ; i < spydata - > spy_number ; i + + ) {
memcpy ( address [ i ] . sa_data , spydata - > spy_address [ i ] , ETH_ALEN ) ;
address [ i ] . sa_family = AF_UNIX ;
}
/* Copy stats to the user buffer (just after). */
if ( spydata - > spy_number > 0 )
memcpy ( extra + ( sizeof ( struct sockaddr ) * spydata - > spy_number ) ,
spydata - > spy_stat ,
sizeof ( struct iw_quality ) * spydata - > spy_number ) ;
/* Reset updated flags. */
for ( i = 0 ; i < spydata - > spy_number ; i + + )
spydata - > spy_stat [ i ] . updated & = ~ IW_QUAL_ALL_UPDATED ;
return 0 ;
}
EXPORT_SYMBOL ( iw_handler_get_spy ) ;
/*------------------------------------------------------------------*/
/*
* Standard Wireless Handler : set spy threshold
*/
int iw_handler_set_thrspy ( struct net_device * dev ,
struct iw_request_info * info ,
union iwreq_data * wrqu ,
char * extra )
{
struct iw_spy_data * spydata = get_spydata ( dev ) ;
struct iw_thrspy * threshold = ( struct iw_thrspy * ) extra ;
/* Make sure driver is not buggy or using the old API */
if ( ! spydata )
return - EOPNOTSUPP ;
/* Just do it */
memcpy ( & ( spydata - > spy_thr_low ) , & ( threshold - > low ) ,
2 * sizeof ( struct iw_quality ) ) ;
/* Clear flag */
memset ( spydata - > spy_thr_under , ' \0 ' , sizeof ( spydata - > spy_thr_under ) ) ;
return 0 ;
}
EXPORT_SYMBOL ( iw_handler_set_thrspy ) ;
/*------------------------------------------------------------------*/
/*
* Standard Wireless Handler : get spy threshold
*/
int iw_handler_get_thrspy ( struct net_device * dev ,
struct iw_request_info * info ,
union iwreq_data * wrqu ,
char * extra )
{
struct iw_spy_data * spydata = get_spydata ( dev ) ;
struct iw_thrspy * threshold = ( struct iw_thrspy * ) extra ;
/* Make sure driver is not buggy or using the old API */
if ( ! spydata )
return - EOPNOTSUPP ;
/* Just do it */
memcpy ( & ( threshold - > low ) , & ( spydata - > spy_thr_low ) ,
2 * sizeof ( struct iw_quality ) ) ;
return 0 ;
}
EXPORT_SYMBOL ( iw_handler_get_thrspy ) ;
/*------------------------------------------------------------------*/
/*
* Prepare and send a Spy Threshold event
*/
static void iw_send_thrspy_event ( struct net_device * dev ,
struct iw_spy_data * spydata ,
unsigned char * address ,
struct iw_quality * wstats )
{
union iwreq_data wrqu ;
struct iw_thrspy threshold ;
/* Init */
wrqu . data . length = 1 ;
wrqu . data . flags = 0 ;
/* Copy address */
memcpy ( threshold . addr . sa_data , address , ETH_ALEN ) ;
threshold . addr . sa_family = ARPHRD_ETHER ;
/* Copy stats */
memcpy ( & ( threshold . qual ) , wstats , sizeof ( struct iw_quality ) ) ;
/* Copy also thresholds */
memcpy ( & ( threshold . low ) , & ( spydata - > spy_thr_low ) ,
2 * sizeof ( struct iw_quality ) ) ;
/* Send event to user space */
wireless_send_event ( dev , SIOCGIWTHRSPY , & wrqu , ( char * ) & threshold ) ;
}
/* ---------------------------------------------------------------- */
/*
* Call for the driver to update the spy data .
* For now , the spy data is a simple array . As the size of the array is
* small , this is good enough . If we wanted to support larger number of
* spy addresses , we should use something more efficient . . .
*/
void wireless_spy_update ( struct net_device * dev ,
unsigned char * address ,
struct iw_quality * wstats )
{
struct iw_spy_data * spydata = get_spydata ( dev ) ;
int i ;
int match = - 1 ;
/* Make sure driver is not buggy or using the old API */
if ( ! spydata )
return ;
/* Update all records that match */
for ( i = 0 ; i < spydata - > spy_number ; i + + )
if ( ! compare_ether_addr ( address , spydata - > spy_address [ i ] ) ) {
memcpy ( & ( spydata - > spy_stat [ i ] ) , wstats ,
sizeof ( struct iw_quality ) ) ;
match = i ;
}
/* Generate an event if we cross the spy threshold.
* To avoid event storms , we have a simple hysteresis : we generate
* event only when we go under the low threshold or above the
* high threshold . */
if ( match > = 0 ) {
if ( spydata - > spy_thr_under [ match ] ) {
if ( wstats - > level > spydata - > spy_thr_high . level ) {
spydata - > spy_thr_under [ match ] = 0 ;
iw_send_thrspy_event ( dev , spydata ,
address , wstats ) ;
}
} else {
if ( wstats - > level < spydata - > spy_thr_low . level ) {
spydata - > spy_thr_under [ match ] = 1 ;
iw_send_thrspy_event ( dev , spydata ,
address , wstats ) ;
}
}
}
}
EXPORT_SYMBOL ( wireless_spy_update ) ;