diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-09-29 17:27:28 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-10-07 16:39:43 -0400 |
commit | 3d23e349d807177eaf519d444677cee86b1a04cf (patch) | |
tree | e800dee247598e59185ddd2dd47d2ac6fe090b9c /net/wireless/wext-spy.c | |
parent | bc974f4a230756faf4f69114f271de2e678b363b (diff) |
wext: refactor
Refactor wext to
* split out iwpriv handling
* split out iwspy handling
* split out procfs support
* allow cfg80211 to have wireless extensions compat code
w/o CONFIG_WIRELESS_EXT
After this, drivers need to
- select WIRELESS_EXT - for wext support
- select WEXT_PRIV - for iwpriv support
- select WEXT_SPY - for iwspy support
except cfg80211 -- which gets new hooks in wext-core.c
and can then get wext handlers without CONFIG_WIRELESS_EXT.
Wireless extensions procfs support is auto-selected
based on PROC_FS and anything that requires the wext core
(i.e. WIRELESS_EXT or CFG80211_WEXT).
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless/wext-spy.c')
-rw-r--r-- | net/wireless/wext-spy.c | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/net/wireless/wext-spy.c b/net/wireless/wext-spy.c new file mode 100644 index 000000000000..6dcfe65a2d1a --- /dev/null +++ b/net/wireless/wext-spy.c | |||
@@ -0,0 +1,231 @@ | |||
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); | ||