162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file implement the Wireless Extensions spy API. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> 562306a36Sopenharmony_ci * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * (As all part of the Linux kernel, this file is GPL) 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/wireless.h> 1162306a36Sopenharmony_ci#include <linux/netdevice.h> 1262306a36Sopenharmony_ci#include <linux/etherdevice.h> 1362306a36Sopenharmony_ci#include <linux/export.h> 1462306a36Sopenharmony_ci#include <net/iw_handler.h> 1562306a36Sopenharmony_ci#include <net/arp.h> 1662306a36Sopenharmony_ci#include <net/wext.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic inline struct iw_spy_data *get_spydata(struct net_device *dev) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci /* This is the new way */ 2162306a36Sopenharmony_ci if (dev->wireless_data) 2262306a36Sopenharmony_ci return dev->wireless_data->spy_data; 2362306a36Sopenharmony_ci return NULL; 2462306a36Sopenharmony_ci} 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ciint iw_handler_set_spy(struct net_device * dev, 2762306a36Sopenharmony_ci struct iw_request_info * info, 2862306a36Sopenharmony_ci union iwreq_data * wrqu, 2962306a36Sopenharmony_ci char * extra) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct iw_spy_data * spydata = get_spydata(dev); 3262306a36Sopenharmony_ci struct sockaddr * address = (struct sockaddr *) extra; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci /* Make sure driver is not buggy or using the old API */ 3562306a36Sopenharmony_ci if (!spydata) 3662306a36Sopenharmony_ci return -EOPNOTSUPP; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci /* Disable spy collection while we copy the addresses. 3962306a36Sopenharmony_ci * While we copy addresses, any call to wireless_spy_update() 4062306a36Sopenharmony_ci * will NOP. This is OK, as anyway the addresses are changing. */ 4162306a36Sopenharmony_ci spydata->spy_number = 0; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci /* We want to operate without locking, because wireless_spy_update() 4462306a36Sopenharmony_ci * most likely will happen in the interrupt handler, and therefore 4562306a36Sopenharmony_ci * have its own locking constraints and needs performance. 4662306a36Sopenharmony_ci * The rtnl_lock() make sure we don't race with the other iw_handlers. 4762306a36Sopenharmony_ci * This make sure wireless_spy_update() "see" that the spy list 4862306a36Sopenharmony_ci * is temporarily disabled. */ 4962306a36Sopenharmony_ci smp_wmb(); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* Are there are addresses to copy? */ 5262306a36Sopenharmony_ci if (wrqu->data.length > 0) { 5362306a36Sopenharmony_ci int i; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci /* Copy addresses */ 5662306a36Sopenharmony_ci for (i = 0; i < wrqu->data.length; i++) 5762306a36Sopenharmony_ci memcpy(spydata->spy_address[i], address[i].sa_data, 5862306a36Sopenharmony_ci ETH_ALEN); 5962306a36Sopenharmony_ci /* Reset stats */ 6062306a36Sopenharmony_ci memset(spydata->spy_stat, 0, 6162306a36Sopenharmony_ci sizeof(struct iw_quality) * IW_MAX_SPY); 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* Make sure above is updated before re-enabling */ 6562306a36Sopenharmony_ci smp_wmb(); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* Enable addresses */ 6862306a36Sopenharmony_ci spydata->spy_number = wrqu->data.length; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return 0; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ciEXPORT_SYMBOL(iw_handler_set_spy); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ciint iw_handler_get_spy(struct net_device * dev, 7562306a36Sopenharmony_ci struct iw_request_info * info, 7662306a36Sopenharmony_ci union iwreq_data * wrqu, 7762306a36Sopenharmony_ci char * extra) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct iw_spy_data * spydata = get_spydata(dev); 8062306a36Sopenharmony_ci struct sockaddr * address = (struct sockaddr *) extra; 8162306a36Sopenharmony_ci int i; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci /* Make sure driver is not buggy or using the old API */ 8462306a36Sopenharmony_ci if (!spydata) 8562306a36Sopenharmony_ci return -EOPNOTSUPP; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci wrqu->data.length = spydata->spy_number; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* Copy addresses. */ 9062306a36Sopenharmony_ci for (i = 0; i < spydata->spy_number; i++) { 9162306a36Sopenharmony_ci memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN); 9262306a36Sopenharmony_ci address[i].sa_family = AF_UNIX; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci /* Copy stats to the user buffer (just after). */ 9562306a36Sopenharmony_ci if (spydata->spy_number > 0) 9662306a36Sopenharmony_ci memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number), 9762306a36Sopenharmony_ci spydata->spy_stat, 9862306a36Sopenharmony_ci sizeof(struct iw_quality) * spydata->spy_number); 9962306a36Sopenharmony_ci /* Reset updated flags. */ 10062306a36Sopenharmony_ci for (i = 0; i < spydata->spy_number; i++) 10162306a36Sopenharmony_ci spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED; 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ciEXPORT_SYMBOL(iw_handler_get_spy); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 10762306a36Sopenharmony_ci/* 10862306a36Sopenharmony_ci * Standard Wireless Handler : set spy threshold 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ciint iw_handler_set_thrspy(struct net_device * dev, 11162306a36Sopenharmony_ci struct iw_request_info *info, 11262306a36Sopenharmony_ci union iwreq_data * wrqu, 11362306a36Sopenharmony_ci char * extra) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct iw_spy_data * spydata = get_spydata(dev); 11662306a36Sopenharmony_ci struct iw_thrspy * threshold = (struct iw_thrspy *) extra; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* Make sure driver is not buggy or using the old API */ 11962306a36Sopenharmony_ci if (!spydata) 12062306a36Sopenharmony_ci return -EOPNOTSUPP; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* Just do it */ 12362306a36Sopenharmony_ci spydata->spy_thr_low = threshold->low; 12462306a36Sopenharmony_ci spydata->spy_thr_high = threshold->high; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* Clear flag */ 12762306a36Sopenharmony_ci memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under)); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ciEXPORT_SYMBOL(iw_handler_set_thrspy); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 13462306a36Sopenharmony_ci/* 13562306a36Sopenharmony_ci * Standard Wireless Handler : get spy threshold 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ciint iw_handler_get_thrspy(struct net_device * dev, 13862306a36Sopenharmony_ci struct iw_request_info *info, 13962306a36Sopenharmony_ci union iwreq_data * wrqu, 14062306a36Sopenharmony_ci char * extra) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct iw_spy_data * spydata = get_spydata(dev); 14362306a36Sopenharmony_ci struct iw_thrspy * threshold = (struct iw_thrspy *) extra; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* Make sure driver is not buggy or using the old API */ 14662306a36Sopenharmony_ci if (!spydata) 14762306a36Sopenharmony_ci return -EOPNOTSUPP; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* Just do it */ 15062306a36Sopenharmony_ci threshold->low = spydata->spy_thr_low; 15162306a36Sopenharmony_ci threshold->high = spydata->spy_thr_high; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ciEXPORT_SYMBOL(iw_handler_get_thrspy); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci/*------------------------------------------------------------------*/ 15862306a36Sopenharmony_ci/* 15962306a36Sopenharmony_ci * Prepare and send a Spy Threshold event 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_cistatic void iw_send_thrspy_event(struct net_device * dev, 16262306a36Sopenharmony_ci struct iw_spy_data * spydata, 16362306a36Sopenharmony_ci unsigned char * address, 16462306a36Sopenharmony_ci struct iw_quality * wstats) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci union iwreq_data wrqu; 16762306a36Sopenharmony_ci struct iw_thrspy threshold; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* Init */ 17062306a36Sopenharmony_ci wrqu.data.length = 1; 17162306a36Sopenharmony_ci wrqu.data.flags = 0; 17262306a36Sopenharmony_ci /* Copy address */ 17362306a36Sopenharmony_ci memcpy(threshold.addr.sa_data, address, ETH_ALEN); 17462306a36Sopenharmony_ci threshold.addr.sa_family = ARPHRD_ETHER; 17562306a36Sopenharmony_ci /* Copy stats */ 17662306a36Sopenharmony_ci threshold.qual = *wstats; 17762306a36Sopenharmony_ci /* Copy also thresholds */ 17862306a36Sopenharmony_ci threshold.low = spydata->spy_thr_low; 17962306a36Sopenharmony_ci threshold.high = spydata->spy_thr_high; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* Send event to user space */ 18262306a36Sopenharmony_ci wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* ---------------------------------------------------------------- */ 18662306a36Sopenharmony_ci/* 18762306a36Sopenharmony_ci * Call for the driver to update the spy data. 18862306a36Sopenharmony_ci * For now, the spy data is a simple array. As the size of the array is 18962306a36Sopenharmony_ci * small, this is good enough. If we wanted to support larger number of 19062306a36Sopenharmony_ci * spy addresses, we should use something more efficient... 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_civoid wireless_spy_update(struct net_device * dev, 19362306a36Sopenharmony_ci unsigned char * address, 19462306a36Sopenharmony_ci struct iw_quality * wstats) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct iw_spy_data * spydata = get_spydata(dev); 19762306a36Sopenharmony_ci int i; 19862306a36Sopenharmony_ci int match = -1; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* Make sure driver is not buggy or using the old API */ 20162306a36Sopenharmony_ci if (!spydata) 20262306a36Sopenharmony_ci return; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Update all records that match */ 20562306a36Sopenharmony_ci for (i = 0; i < spydata->spy_number; i++) 20662306a36Sopenharmony_ci if (ether_addr_equal(address, spydata->spy_address[i])) { 20762306a36Sopenharmony_ci memcpy(&(spydata->spy_stat[i]), wstats, 20862306a36Sopenharmony_ci sizeof(struct iw_quality)); 20962306a36Sopenharmony_ci match = i; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Generate an event if we cross the spy threshold. 21362306a36Sopenharmony_ci * To avoid event storms, we have a simple hysteresis : we generate 21462306a36Sopenharmony_ci * event only when we go under the low threshold or above the 21562306a36Sopenharmony_ci * high threshold. */ 21662306a36Sopenharmony_ci if (match >= 0) { 21762306a36Sopenharmony_ci if (spydata->spy_thr_under[match]) { 21862306a36Sopenharmony_ci if (wstats->level > spydata->spy_thr_high.level) { 21962306a36Sopenharmony_ci spydata->spy_thr_under[match] = 0; 22062306a36Sopenharmony_ci iw_send_thrspy_event(dev, spydata, 22162306a36Sopenharmony_ci address, wstats); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci } else { 22462306a36Sopenharmony_ci if (wstats->level < spydata->spy_thr_low.level) { 22562306a36Sopenharmony_ci spydata->spy_thr_under[match] = 1; 22662306a36Sopenharmony_ci iw_send_thrspy_event(dev, spydata, 22762306a36Sopenharmony_ci address, wstats); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ciEXPORT_SYMBOL(wireless_spy_update); 233