cfg80211/nl80211: add IBSS API
[deliverable/linux.git] / net / wireless / ibss.c
CommitLineData
04a773ad
JB
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
This page took 0.035564 seconds and 5 git commands to generate.