]> git.karo-electronics.de Git - karo-tx-linux.git/blob - net/wireless/wext-spy.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6
[karo-tx-linux.git] / net / wireless / wext-spy.c
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);