1/* 2 * This file implement the Wireless Extensions spy API. 3 * 4 * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> 5 * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. 6 * 7 * (As all part of the Linux kernel, this file is GPL) 8 */ 9 10#include <linux/wireless.h> 11#include <linux/netdevice.h> 12#include <linux/etherdevice.h> 13#include <linux/export.h> 14#include <net/iw_handler.h> 15#include <net/arp.h> 16#include <net/wext.h> 17 18static inline struct iw_spy_data *get_spydata(struct net_device *dev) 19{ 20 /* This is the new way */ 21 if (dev->wireless_data) 22 return dev->wireless_data->spy_data; 23 return NULL; 24} 25 26int iw_handler_set_spy(struct net_device * dev, 27 struct iw_request_info * info, 28 union iwreq_data * wrqu, 29 char * extra) 30{ 31 struct iw_spy_data * spydata = get_spydata(dev); 32 struct sockaddr * address = (struct sockaddr *) extra; 33 34 /* Make sure driver is not buggy or using the old API */ 35 if (!spydata) 36 return -EOPNOTSUPP; 37 38 /* Disable spy collection while we copy the addresses. 39 * While we copy addresses, any call to wireless_spy_update() 40 * will NOP. This is OK, as anyway the addresses are changing. */ 41 spydata->spy_number = 0; 42 43 /* We want to operate without locking, because wireless_spy_update() 44 * most likely will happen in the interrupt handler, and therefore 45 * have its own locking constraints and needs performance. 46 * The rtnl_lock() make sure we don't race with the other iw_handlers. 47 * This make sure wireless_spy_update() "see" that the spy list 48 * is temporarily disabled. */ 49 smp_wmb(); 50 51 /* Are there are addresses to copy? */ 52 if (wrqu->data.length > 0) { 53 int i; 54 55 /* Copy addresses */ 56 for (i = 0; i < wrqu->data.length; i++) 57 memcpy(spydata->spy_address[i], address[i].sa_data, 58 ETH_ALEN); 59 /* Reset stats */ 60 memset(spydata->spy_stat, 0, 61 sizeof(struct iw_quality) * IW_MAX_SPY); 62 } 63 64 /* Make sure above is updated before re-enabling */ 65 smp_wmb(); 66 67 /* Enable addresses */ 68 spydata->spy_number = wrqu->data.length; 69 70 return 0; 71} 72EXPORT_SYMBOL(iw_handler_set_spy); 73 74int iw_handler_get_spy(struct net_device * dev, 75 struct iw_request_info * info, 76 union iwreq_data * wrqu, 77 char * extra) 78{ 79 struct iw_spy_data * spydata = get_spydata(dev); 80 struct sockaddr * address = (struct sockaddr *) extra; 81 int i; 82 83 /* Make sure driver is not buggy or using the old API */ 84 if (!spydata) 85 return -EOPNOTSUPP; 86 87 wrqu->data.length = spydata->spy_number; 88 89 /* Copy addresses. */ 90 for (i = 0; i < spydata->spy_number; i++) { 91 memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN); 92 address[i].sa_family = AF_UNIX; 93 } 94 /* Copy stats to the user buffer (just after). */ 95 if (spydata->spy_number > 0) 96 memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number), 97 spydata->spy_stat, 98 sizeof(struct iw_quality) * spydata->spy_number); 99 /* Reset updated flags. */ 100 for (i = 0; i < spydata->spy_number; i++) 101 spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED; 102 return 0; 103} 104EXPORT_SYMBOL(iw_handler_get_spy); 105 106/*------------------------------------------------------------------*/ 107/* 108 * Standard Wireless Handler : set spy threshold 109 */ 110int iw_handler_set_thrspy(struct net_device * dev, 111 struct iw_request_info *info, 112 union iwreq_data * wrqu, 113 char * extra) 114{ 115 struct iw_spy_data * spydata = get_spydata(dev); 116 struct iw_thrspy * threshold = (struct iw_thrspy *) extra; 117 118 /* Make sure driver is not buggy or using the old API */ 119 if (!spydata) 120 return -EOPNOTSUPP; 121 122 /* Just do it */ 123 memcpy(&(spydata->spy_thr_low), &(threshold->low), 124 2 * sizeof(struct iw_quality)); 125 126 /* Clear flag */ 127 memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under)); 128 129 return 0; 130} 131EXPORT_SYMBOL(iw_handler_set_thrspy); 132 133/*------------------------------------------------------------------*/ 134/* 135 * Standard Wireless Handler : get spy threshold 136 */ 137int iw_handler_get_thrspy(struct net_device * dev, 138 struct iw_request_info *info, 139 union iwreq_data * wrqu, 140 char * extra) 141{ 142 struct iw_spy_data * spydata = get_spydata(dev); 143 struct iw_thrspy * threshold = (struct iw_thrspy *) extra; 144 145 /* Make sure driver is not buggy or using the old API */ 146 if (!spydata) 147 return -EOPNOTSUPP; 148 149 /* Just do it */ 150 memcpy(&(threshold->low), &(spydata->spy_thr_low), 151 2 * sizeof(struct iw_quality)); 152 153 return 0; 154} 155EXPORT_SYMBOL(iw_handler_get_thrspy); 156 157/*------------------------------------------------------------------*/ 158/* 159 * Prepare and send a Spy Threshold event 160 */ 161static void iw_send_thrspy_event(struct net_device * dev, 162 struct iw_spy_data * spydata, 163 unsigned char * address, 164 struct iw_quality * wstats) 165{ 166 union iwreq_data wrqu; 167 struct iw_thrspy threshold; 168 169 /* Init */ 170 wrqu.data.length = 1; 171 wrqu.data.flags = 0; 172 /* Copy address */ 173 memcpy(threshold.addr.sa_data, address, ETH_ALEN); 174 threshold.addr.sa_family = ARPHRD_ETHER; 175 /* Copy stats */ 176 memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality)); 177 /* Copy also thresholds */ 178 memcpy(&(threshold.low), &(spydata->spy_thr_low), 179 2 * sizeof(struct iw_quality)); 180 181 /* Send event to user space */ 182 wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); 183} 184 185/* ---------------------------------------------------------------- */ 186/* 187 * Call for the driver to update the spy data. 188 * For now, the spy data is a simple array. As the size of the array is 189 * small, this is good enough. If we wanted to support larger number of 190 * spy addresses, we should use something more efficient... 191 */ 192void wireless_spy_update(struct net_device * dev, 193 unsigned char * address, 194 struct iw_quality * wstats) 195{ 196 struct iw_spy_data * spydata = get_spydata(dev); 197 int i; 198 int match = -1; 199 200 /* Make sure driver is not buggy or using the old API */ 201 if (!spydata) 202 return; 203 204 /* Update all records that match */ 205 for (i = 0; i < spydata->spy_number; i++) 206 if (ether_addr_equal(address, spydata->spy_address[i])) { 207 memcpy(&(spydata->spy_stat[i]), wstats, 208 sizeof(struct iw_quality)); 209 match = i; 210 } 211 212 /* Generate an event if we cross the spy threshold. 213 * To avoid event storms, we have a simple hysteresis : we generate 214 * event only when we go under the low threshold or above the 215 * high threshold. */ 216 if (match >= 0) { 217 if (spydata->spy_thr_under[match]) { 218 if (wstats->level > spydata->spy_thr_high.level) { 219 spydata->spy_thr_under[match] = 0; 220 iw_send_thrspy_event(dev, spydata, 221 address, wstats); 222 } 223 } else { 224 if (wstats->level < spydata->spy_thr_low.level) { 225 spydata->spy_thr_under[match] = 1; 226 iw_send_thrspy_event(dev, spydata, 227 address, wstats); 228 } 229 } 230 } 231} 232EXPORT_SYMBOL(wireless_spy_update); 233