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