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