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