xref: /linux/drivers/net/wireless/intel/ipw2x00/libipw_spy.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
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