aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/ibss.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-04-19 15:24:32 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-04-22 16:57:17 -0400
commit04a773ade0680d862b479d7219973df60f7a3834 (patch)
treefc759eb79099fefd7f1329bcb2b703008cb0adfe /net/wireless/ibss.c
parent691597cb26f236ac7471f1adf925a134c86799d6 (diff)
cfg80211/nl80211: add IBSS API
This adds IBSS API along with (preliminary) wext handlers. The wext handlers can only do IBSS so you need to call them from your own wext handlers if the mode is IBSS. The nl80211 API requires * an SSID * a channel (frequency) for the case that a new IBSS has to be created It optionally supports * a flag to fix the channel * a fixed BSSID The cfg80211 code also takes care to leave the IBSS before the netdev is set down. If wireless extensions are used, it also caches values when the interface is down and instructs the driver to join when the interface is set up. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/wireless/ibss.c')
-rw-r--r--net/wireless/ibss.c360
1 files changed, 360 insertions, 0 deletions
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
new file mode 100644
index 000000000000..2bf42fdef3a1
--- /dev/null
+++ b/net/wireless/ibss.c
@@ -0,0 +1,360 @@
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
14void 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}
54EXPORT_SYMBOL(cfg80211_ibss_joined);
55
56int 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
80void 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
94int 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
110static 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
155int 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 */
199EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwfreq);
200
201int 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 */
227EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwfreq);
228
229int 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 */
261EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwessid);
262
263int 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 */
288EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwessid);
289
290int 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 */
336EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_siwap);
337
338int 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 */
359EXPORT_SYMBOL_GPL(cfg80211_ibss_wext_giwap);
360#endif