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