Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable
[linux-2.6] / net / wireless / ibss.c
1 /*
2  * Some IBSS support code for cfg80211.
3  *
4  * Copyright 2009       Johannes Berg <johannes@sipsolutions.net>
5  */
6
7 #include <linux/etherdevice.h>
8 #include <linux/if_arp.h>
9 #include <net/cfg80211.h>
10 #include "nl80211.h"
11
12
13 void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp)
14 {
15         struct wireless_dev *wdev = dev->ieee80211_ptr;
16         struct cfg80211_bss *bss;
17 #ifdef CONFIG_WIRELESS_EXT
18         union iwreq_data wrqu;
19 #endif
20
21         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
22                 return;
23
24         if (WARN_ON(!wdev->ssid_len))
25                 return;
26
27         if (memcmp(bssid, wdev->bssid, ETH_ALEN) == 0)
28                 return;
29
30         bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
31                                wdev->ssid, wdev->ssid_len,
32                                WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS);
33
34         if (WARN_ON(!bss))
35                 return;
36
37         if (wdev->current_bss) {
38                 cfg80211_unhold_bss(wdev->current_bss);
39                 cfg80211_put_bss(wdev->current_bss);
40         }
41
42         cfg80211_hold_bss(bss);
43         wdev->current_bss = bss;
44         memcpy(wdev->bssid, bssid, ETH_ALEN);
45
46         nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, gfp);
47 #ifdef CONFIG_WIRELESS_EXT
48         memset(&wrqu, 0, sizeof(wrqu));
49         memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
50         wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
51 #endif
52 }
53 EXPORT_SYMBOL(cfg80211_ibss_joined);
54
55 int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
56                        struct net_device *dev,
57                        struct cfg80211_ibss_params *params)
58 {
59         struct wireless_dev *wdev = dev->ieee80211_ptr;
60         int err;
61
62         if (wdev->ssid_len)
63                 return -EALREADY;
64
65 #ifdef CONFIG_WIRELESS_EXT
66         wdev->wext.ibss.channel = params->channel;
67 #endif
68         err = rdev->ops->join_ibss(&rdev->wiphy, dev, params);
69
70         if (err)
71                 return err;
72
73         memcpy(wdev->ssid, params->ssid, params->ssid_len);
74         wdev->ssid_len = params->ssid_len;
75
76         return 0;
77 }
78
79 void cfg80211_clear_ibss(struct net_device *dev, bool nowext)
80 {
81         struct wireless_dev *wdev = dev->ieee80211_ptr;
82
83         if (wdev->current_bss) {
84                 cfg80211_unhold_bss(wdev->current_bss);
85                 cfg80211_put_bss(wdev->current_bss);
86         }
87
88         wdev->current_bss = NULL;
89         wdev->ssid_len = 0;
90         memset(wdev->bssid, 0, ETH_ALEN);
91 #ifdef CONFIG_WIRELESS_EXT
92         if (!nowext)
93                 wdev->wext.ibss.ssid_len = 0;
94 #endif
95 }
96
97 int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
98                         struct net_device *dev, bool nowext)
99 {
100         int err;
101
102         err = rdev->ops->leave_ibss(&rdev->wiphy, dev);
103
104         if (err)
105                 return err;
106
107         cfg80211_clear_ibss(dev, nowext);
108
109         return 0;
110 }
111
112 #ifdef CONFIG_WIRELESS_EXT
113 static int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
114                                    struct wireless_dev *wdev)
115 {
116         enum ieee80211_band band;
117         int i;
118
119         if (!wdev->wext.ibss.beacon_interval)
120                 wdev->wext.ibss.beacon_interval = 100;
121
122         /* try to find an IBSS channel if none requested ... */
123         if (!wdev->wext.ibss.channel) {
124                 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
125                         struct ieee80211_supported_band *sband;
126                         struct ieee80211_channel *chan;
127
128                         sband = rdev->wiphy.bands[band];
129                         if (!sband)
130                                 continue;
131
132                         for (i = 0; i < sband->n_channels; i++) {
133                                 chan = &sband->channels[i];
134                                 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
135                                         continue;
136                                 if (chan->flags & IEEE80211_CHAN_DISABLED)
137                                         continue;
138                                 wdev->wext.ibss.channel = chan;
139                                 break;
140                         }
141
142                         if (wdev->wext.ibss.channel)
143                                 break;
144                 }
145
146                 if (!wdev->wext.ibss.channel)
147                         return -EINVAL;
148         }
149
150         /* don't join -- SSID is not there */
151         if (!wdev->wext.ibss.ssid_len)
152                 return 0;
153
154         if (!netif_running(wdev->netdev))
155                 return 0;
156
157         return cfg80211_join_ibss(wiphy_to_dev(wdev->wiphy),
158                                   wdev->netdev, &wdev->wext.ibss);
159 }
160
161 int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
162                                struct iw_request_info *info,
163                                struct iw_freq *freq, char *extra)
164 {
165         struct wireless_dev *wdev = dev->ieee80211_ptr;
166         struct ieee80211_channel *chan;
167         int err;
168
169         /* call only for ibss! */
170         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
171                 return -EINVAL;
172
173         if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
174                 return -EOPNOTSUPP;
175
176         chan = cfg80211_wext_freq(wdev->wiphy, freq);
177         if (chan && IS_ERR(chan))
178                 return PTR_ERR(chan);
179
180         if (chan &&
181             (chan->flags & IEEE80211_CHAN_NO_IBSS ||
182              chan->flags & IEEE80211_CHAN_DISABLED))
183                 return -EINVAL;
184
185         if (wdev->wext.ibss.channel == chan)
186                 return 0;
187
188         if (wdev->ssid_len) {
189                 err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy),
190                                           dev, true);
191                 if (err)
192                         return err;
193         }
194
195         if (chan) {
196                 wdev->wext.ibss.channel = chan;
197                 wdev->wext.ibss.channel_fixed = true;
198         } else {
199                 /* cfg80211_ibss_wext_join will pick one if needed */
200                 wdev->wext.ibss.channel_fixed = false;
201         }
202
203         return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
204 }
205 /* temporary symbol - mark GPL - in the future the handler won't be */
206 EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwfreq);
207
208 int cfg80211_ibss_wext_giwfreq(struct net_device *dev,
209                                struct iw_request_info *info,
210                                struct iw_freq *freq, char *extra)
211 {
212         struct wireless_dev *wdev = dev->ieee80211_ptr;
213         struct ieee80211_channel *chan = NULL;
214
215         /* call only for ibss! */
216         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
217                 return -EINVAL;
218
219         if (wdev->current_bss)
220                 chan = wdev->current_bss->channel;
221         else if (wdev->wext.ibss.channel)
222                 chan = wdev->wext.ibss.channel;
223
224         if (chan) {
225                 freq->m = chan->center_freq;
226                 freq->e = 6;
227                 return 0;
228         }
229
230         /* no channel if not joining */
231         return -EINVAL;
232 }
233 /* temporary symbol - mark GPL - in the future the handler won't be */
234 EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwfreq);
235
236 int cfg80211_ibss_wext_siwessid(struct net_device *dev,
237                                 struct iw_request_info *info,
238                                 struct iw_point *data, char *ssid)
239 {
240         struct wireless_dev *wdev = dev->ieee80211_ptr;
241         size_t len = data->length;
242         int err;
243
244         /* call only for ibss! */
245         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
246                 return -EINVAL;
247
248         if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
249                 return -EOPNOTSUPP;
250
251         if (wdev->ssid_len) {
252                 err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy),
253                                           dev, true);
254                 if (err)
255                         return err;
256         }
257
258         /* iwconfig uses nul termination in SSID.. */
259         if (len > 0 && ssid[len - 1] == '\0')
260                 len--;
261
262         wdev->wext.ibss.ssid = wdev->ssid;
263         memcpy(wdev->wext.ibss.ssid, ssid, len);
264         wdev->wext.ibss.ssid_len = len;
265
266         return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
267 }
268 /* temporary symbol - mark GPL - in the future the handler won't be */
269 EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwessid);
270
271 int cfg80211_ibss_wext_giwessid(struct net_device *dev,
272                                 struct iw_request_info *info,
273                                 struct iw_point *data, char *ssid)
274 {
275         struct wireless_dev *wdev = dev->ieee80211_ptr;
276
277         /* call only for ibss! */
278         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
279                 return -EINVAL;
280
281         data->flags = 0;
282
283         if (wdev->ssid_len) {
284                 data->flags = 1;
285                 data->length = wdev->ssid_len;
286                 memcpy(ssid, wdev->ssid, data->length);
287         } else if (wdev->wext.ibss.ssid && wdev->wext.ibss.ssid_len) {
288                 data->flags = 1;
289                 data->length = wdev->wext.ibss.ssid_len;
290                 memcpy(ssid, wdev->wext.ibss.ssid, data->length);
291         }
292
293         return 0;
294 }
295 /* temporary symbol - mark GPL - in the future the handler won't be */
296 EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwessid);
297
298 int cfg80211_ibss_wext_siwap(struct net_device *dev,
299                              struct iw_request_info *info,
300                              struct sockaddr *ap_addr, char *extra)
301 {
302         struct wireless_dev *wdev = dev->ieee80211_ptr;
303         u8 *bssid = ap_addr->sa_data;
304         int err;
305
306         /* call only for ibss! */
307         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
308                 return -EINVAL;
309
310         if (!wiphy_to_dev(wdev->wiphy)->ops->join_ibss)
311                 return -EOPNOTSUPP;
312
313         if (ap_addr->sa_family != ARPHRD_ETHER)
314                 return -EINVAL;
315
316         /* automatic mode */
317         if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
318                 bssid = NULL;
319
320         /* both automatic */
321         if (!bssid && !wdev->wext.ibss.bssid)
322                 return 0;
323
324         /* fixed already - and no change */
325         if (wdev->wext.ibss.bssid && bssid &&
326             compare_ether_addr(bssid, wdev->wext.ibss.bssid) == 0)
327                 return 0;
328
329         if (wdev->ssid_len) {
330                 err = cfg80211_leave_ibss(wiphy_to_dev(wdev->wiphy),
331                                           dev, true);
332                 if (err)
333                         return err;
334         }
335
336         if (bssid) {
337                 memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
338                 wdev->wext.ibss.bssid = wdev->wext.bssid;
339         } else
340                 wdev->wext.ibss.bssid = NULL;
341
342         return cfg80211_ibss_wext_join(wiphy_to_dev(wdev->wiphy), wdev);
343 }
344 /* temporary symbol - mark GPL - in the future the handler won't be */
345 EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwap);
346
347 int cfg80211_ibss_wext_giwap(struct net_device *dev,
348                              struct iw_request_info *info,
349                              struct sockaddr *ap_addr, char *extra)
350 {
351         struct wireless_dev *wdev = dev->ieee80211_ptr;
352
353         /* call only for ibss! */
354         if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
355                 return -EINVAL;
356
357         ap_addr->sa_family = ARPHRD_ETHER;
358
359         if (wdev->wext.ibss.bssid) {
360                 memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN);
361                 return 0;
362         }
363
364         memcpy(ap_addr->sa_data, wdev->bssid, ETH_ALEN);
365         return 0;
366 }
367 /* temporary symbol - mark GPL - in the future the handler won't be */
368 EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwap);
369 #endif