blob: 6ec2127f9a6054734c10e68a5ec0c986b223f903 [file] [log] [blame]
Jiri Bencf0706e822007-05-05 11:45:53 -07001/*
2 * mac80211 configuration hooks for cfg80211
3 *
Johannes Berg62da92f2007-12-19 02:03:31 +01004 * Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net>
Jiri Bencf0706e822007-05-05 11:45:53 -07005 *
6 * This file is GPLv2 as found in COPYING.
7 */
8
Johannes Berge8cbb4c2007-12-19 02:03:30 +01009#include <linux/ieee80211.h>
Jiri Bencf0706e822007-05-05 11:45:53 -070010#include <linux/nl80211.h>
11#include <linux/rtnetlink.h>
Eric W. Biederman881d9662007-09-17 11:56:21 -070012#include <net/net_namespace.h>
Johannes Berg5dfdaf52007-12-19 02:03:33 +010013#include <linux/rcupdate.h>
Jiri Bencf0706e822007-05-05 11:45:53 -070014#include <net/cfg80211.h>
15#include "ieee80211_i.h"
Michael Wue0eb6852007-09-18 17:29:21 -040016#include "cfg.h"
Johannes Berg2c8dccc2008-04-08 15:14:40 -040017#include "rate.h"
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +010018#include "mesh.h"
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +010019
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -070020struct ieee80211_hw *wiphy_to_hw(struct wiphy *wiphy)
21{
22 struct ieee80211_local *local = wiphy_priv(wiphy);
23 return &local->hw;
24}
25EXPORT_SYMBOL(wiphy_to_hw);
26
Johannes Berg42613db2007-09-28 21:52:27 +020027static enum ieee80211_if_types
28nl80211_type_to_mac80211_type(enum nl80211_iftype type)
29{
30 switch (type) {
31 case NL80211_IFTYPE_UNSPECIFIED:
32 return IEEE80211_IF_TYPE_STA;
33 case NL80211_IFTYPE_ADHOC:
34 return IEEE80211_IF_TYPE_IBSS;
35 case NL80211_IFTYPE_STATION:
36 return IEEE80211_IF_TYPE_STA;
37 case NL80211_IFTYPE_MONITOR:
38 return IEEE80211_IF_TYPE_MNTR;
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +010039#ifdef CONFIG_MAC80211_MESH
40 case NL80211_IFTYPE_MESH_POINT:
41 return IEEE80211_IF_TYPE_MESH_POINT;
42#endif
Johannes Bergb4540482008-04-14 15:37:03 +020043 case NL80211_IFTYPE_WDS:
44 return IEEE80211_IF_TYPE_WDS;
Johannes Berg42613db2007-09-28 21:52:27 +020045 default:
46 return IEEE80211_IF_TYPE_INVALID;
47 }
48}
49
Jiri Bencf0706e822007-05-05 11:45:53 -070050static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +010051 enum nl80211_iftype type, u32 *flags,
52 struct vif_params *params)
Jiri Bencf0706e822007-05-05 11:45:53 -070053{
54 struct ieee80211_local *local = wiphy_priv(wiphy);
Johannes Berg42613db2007-09-28 21:52:27 +020055 enum ieee80211_if_types itype;
Michael Wu8cc9a732008-01-31 19:48:23 +010056 struct net_device *dev;
57 struct ieee80211_sub_if_data *sdata;
58 int err;
Jiri Bencf0706e822007-05-05 11:45:53 -070059
Johannes Berg42613db2007-09-28 21:52:27 +020060 itype = nl80211_type_to_mac80211_type(type);
61 if (itype == IEEE80211_IF_TYPE_INVALID)
Jiri Bencf0706e822007-05-05 11:45:53 -070062 return -EINVAL;
Jiri Bencf0706e822007-05-05 11:45:53 -070063
Johannes Berg3e122be2008-07-09 14:40:34 +020064 err = ieee80211_if_add(local, name, &dev, itype, params);
Michael Wu8cc9a732008-01-31 19:48:23 +010065 if (err || itype != IEEE80211_IF_TYPE_MNTR || !flags)
66 return err;
67
68 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
69 sdata->u.mntr_flags = *flags;
70 return 0;
Jiri Bencf0706e822007-05-05 11:45:53 -070071}
72
73static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex)
74{
Jiri Bencf0706e822007-05-05 11:45:53 -070075 struct net_device *dev;
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +120076 struct ieee80211_sub_if_data *sdata;
Jiri Bencf0706e822007-05-05 11:45:53 -070077
Johannes Berg42613db2007-09-28 21:52:27 +020078 /* we're under RTNL */
79 dev = __dev_get_by_index(&init_net, ifindex);
Jiri Bencf0706e822007-05-05 11:45:53 -070080 if (!dev)
Johannes Berg75636522008-07-09 14:40:35 +020081 return -ENODEV;
Jiri Bencf0706e822007-05-05 11:45:53 -070082
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +120083 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
84
85 ieee80211_if_remove(sdata);
Jiri Bencf0706e822007-05-05 11:45:53 -070086
Johannes Berg75636522008-07-09 14:40:35 +020087 return 0;
Jiri Bencf0706e822007-05-05 11:45:53 -070088}
89
Johannes Berg42613db2007-09-28 21:52:27 +020090static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +010091 enum nl80211_iftype type, u32 *flags,
92 struct vif_params *params)
Johannes Berg42613db2007-09-28 21:52:27 +020093{
Johannes Berg14db74b2008-07-29 13:22:52 +020094 struct ieee80211_local *local = wiphy_priv(wiphy);
Johannes Berg42613db2007-09-28 21:52:27 +020095 struct net_device *dev;
96 enum ieee80211_if_types itype;
97 struct ieee80211_sub_if_data *sdata;
Johannes Bergf3947e22008-07-09 14:40:36 +020098 int ret;
Johannes Berg42613db2007-09-28 21:52:27 +020099
Johannes Berg42613db2007-09-28 21:52:27 +0200100 /* we're under RTNL */
101 dev = __dev_get_by_index(&init_net, ifindex);
102 if (!dev)
103 return -ENODEV;
104
Johannes Berg42613db2007-09-28 21:52:27 +0200105 itype = nl80211_type_to_mac80211_type(type);
106 if (itype == IEEE80211_IF_TYPE_INVALID)
107 return -EINVAL;
108
Johannes Berg14db74b2008-07-29 13:22:52 +0200109 if (dev == local->mdev)
110 return -EOPNOTSUPP;
111
Johannes Berg42613db2007-09-28 21:52:27 +0200112 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
113
Johannes Bergf3947e22008-07-09 14:40:36 +0200114 ret = ieee80211_if_change_type(sdata, itype);
115 if (ret)
116 return ret;
Johannes Berg42613db2007-09-28 21:52:27 +0200117
Johannes Berg902acc72008-02-23 15:17:19 +0100118 if (ieee80211_vif_is_mesh(&sdata->vif) && params->mesh_id_len)
Johannes Berg472dbc42008-09-11 00:01:49 +0200119 ieee80211_sdata_set_mesh_id(sdata,
120 params->mesh_id_len,
121 params->mesh_id);
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100122
Michael Wu8cc9a732008-01-31 19:48:23 +0100123 if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR || !flags)
124 return 0;
125
126 sdata->u.mntr_flags = *flags;
Johannes Berg42613db2007-09-28 21:52:27 +0200127 return 0;
128}
129
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100130static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
131 u8 key_idx, u8 *mac_addr,
132 struct key_params *params)
133{
Johannes Berg14db74b2008-07-29 13:22:52 +0200134 struct ieee80211_local *local = wiphy_priv(wiphy);
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100135 struct ieee80211_sub_if_data *sdata;
136 struct sta_info *sta = NULL;
137 enum ieee80211_key_alg alg;
Johannes Bergdb4d1162008-02-25 16:27:45 +0100138 struct ieee80211_key *key;
Johannes Berg3b967662008-04-08 17:56:52 +0200139 int err;
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100140
Johannes Berg14db74b2008-07-29 13:22:52 +0200141 if (dev == local->mdev)
142 return -EOPNOTSUPP;
143
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100144 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
145
146 switch (params->cipher) {
147 case WLAN_CIPHER_SUITE_WEP40:
148 case WLAN_CIPHER_SUITE_WEP104:
149 alg = ALG_WEP;
150 break;
151 case WLAN_CIPHER_SUITE_TKIP:
152 alg = ALG_TKIP;
153 break;
154 case WLAN_CIPHER_SUITE_CCMP:
155 alg = ALG_CCMP;
156 break;
157 default:
158 return -EINVAL;
159 }
160
Johannes Bergdb4d1162008-02-25 16:27:45 +0100161 key = ieee80211_key_alloc(alg, key_idx, params->key_len, params->key);
162 if (!key)
163 return -ENOMEM;
164
Johannes Berg3b967662008-04-08 17:56:52 +0200165 rcu_read_lock();
166
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100167 if (mac_addr) {
168 sta = sta_info_get(sdata->local, mac_addr);
Johannes Bergdb4d1162008-02-25 16:27:45 +0100169 if (!sta) {
170 ieee80211_key_free(key);
Johannes Berg3b967662008-04-08 17:56:52 +0200171 err = -ENOENT;
172 goto out_unlock;
Johannes Bergdb4d1162008-02-25 16:27:45 +0100173 }
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100174 }
175
Johannes Bergdb4d1162008-02-25 16:27:45 +0100176 ieee80211_key_link(key, sdata, sta);
177
Johannes Berg3b967662008-04-08 17:56:52 +0200178 err = 0;
179 out_unlock:
180 rcu_read_unlock();
181
182 return err;
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100183}
184
185static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
186 u8 key_idx, u8 *mac_addr)
187{
Johannes Berg14db74b2008-07-29 13:22:52 +0200188 struct ieee80211_local *local = wiphy_priv(wiphy);
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100189 struct ieee80211_sub_if_data *sdata;
190 struct sta_info *sta;
191 int ret;
192
Johannes Berg14db74b2008-07-29 13:22:52 +0200193 if (dev == local->mdev)
194 return -EOPNOTSUPP;
195
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100196 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
197
Johannes Berg3b967662008-04-08 17:56:52 +0200198 rcu_read_lock();
199
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100200 if (mac_addr) {
Johannes Berg3b967662008-04-08 17:56:52 +0200201 ret = -ENOENT;
202
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100203 sta = sta_info_get(sdata->local, mac_addr);
204 if (!sta)
Johannes Berg3b967662008-04-08 17:56:52 +0200205 goto out_unlock;
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100206
Johannes Bergdb4d1162008-02-25 16:27:45 +0100207 if (sta->key) {
Johannes Bergd0709a62008-02-25 16:27:46 +0100208 ieee80211_key_free(sta->key);
Johannes Bergdb4d1162008-02-25 16:27:45 +0100209 WARN_ON(sta->key);
Johannes Berg3b967662008-04-08 17:56:52 +0200210 ret = 0;
211 }
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100212
Johannes Berg3b967662008-04-08 17:56:52 +0200213 goto out_unlock;
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100214 }
215
Johannes Berg3b967662008-04-08 17:56:52 +0200216 if (!sdata->keys[key_idx]) {
217 ret = -ENOENT;
218 goto out_unlock;
219 }
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100220
Johannes Bergd0709a62008-02-25 16:27:46 +0100221 ieee80211_key_free(sdata->keys[key_idx]);
Johannes Bergdb4d1162008-02-25 16:27:45 +0100222 WARN_ON(sdata->keys[key_idx]);
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100223
Johannes Berg3b967662008-04-08 17:56:52 +0200224 ret = 0;
225 out_unlock:
226 rcu_read_unlock();
227
228 return ret;
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100229}
230
Johannes Berg62da92f2007-12-19 02:03:31 +0100231static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
232 u8 key_idx, u8 *mac_addr, void *cookie,
233 void (*callback)(void *cookie,
234 struct key_params *params))
235{
Johannes Berg14db74b2008-07-29 13:22:52 +0200236 struct ieee80211_local *local = wiphy_priv(wiphy);
237 struct ieee80211_sub_if_data *sdata;
Johannes Berg62da92f2007-12-19 02:03:31 +0100238 struct sta_info *sta = NULL;
239 u8 seq[6] = {0};
240 struct key_params params;
241 struct ieee80211_key *key;
242 u32 iv32;
243 u16 iv16;
244 int err = -ENOENT;
245
Johannes Berg14db74b2008-07-29 13:22:52 +0200246 if (dev == local->mdev)
247 return -EOPNOTSUPP;
248
249 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
250
Johannes Berg3b967662008-04-08 17:56:52 +0200251 rcu_read_lock();
252
Johannes Berg62da92f2007-12-19 02:03:31 +0100253 if (mac_addr) {
254 sta = sta_info_get(sdata->local, mac_addr);
255 if (!sta)
256 goto out;
257
258 key = sta->key;
259 } else
260 key = sdata->keys[key_idx];
261
262 if (!key)
263 goto out;
264
265 memset(&params, 0, sizeof(params));
266
267 switch (key->conf.alg) {
268 case ALG_TKIP:
269 params.cipher = WLAN_CIPHER_SUITE_TKIP;
270
Harvey Harrisonb0f76b32008-05-14 16:26:19 -0700271 iv32 = key->u.tkip.tx.iv32;
272 iv16 = key->u.tkip.tx.iv16;
Johannes Berg62da92f2007-12-19 02:03:31 +0100273
274 if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
275 sdata->local->ops->get_tkip_seq)
276 sdata->local->ops->get_tkip_seq(
277 local_to_hw(sdata->local),
278 key->conf.hw_key_idx,
279 &iv32, &iv16);
280
281 seq[0] = iv16 & 0xff;
282 seq[1] = (iv16 >> 8) & 0xff;
283 seq[2] = iv32 & 0xff;
284 seq[3] = (iv32 >> 8) & 0xff;
285 seq[4] = (iv32 >> 16) & 0xff;
286 seq[5] = (iv32 >> 24) & 0xff;
287 params.seq = seq;
288 params.seq_len = 6;
289 break;
290 case ALG_CCMP:
291 params.cipher = WLAN_CIPHER_SUITE_CCMP;
292 seq[0] = key->u.ccmp.tx_pn[5];
293 seq[1] = key->u.ccmp.tx_pn[4];
294 seq[2] = key->u.ccmp.tx_pn[3];
295 seq[3] = key->u.ccmp.tx_pn[2];
296 seq[4] = key->u.ccmp.tx_pn[1];
297 seq[5] = key->u.ccmp.tx_pn[0];
298 params.seq = seq;
299 params.seq_len = 6;
300 break;
301 case ALG_WEP:
302 if (key->conf.keylen == 5)
303 params.cipher = WLAN_CIPHER_SUITE_WEP40;
304 else
305 params.cipher = WLAN_CIPHER_SUITE_WEP104;
306 break;
307 }
308
309 params.key = key->conf.key;
310 params.key_len = key->conf.keylen;
311
312 callback(cookie, &params);
313 err = 0;
314
315 out:
Johannes Berg3b967662008-04-08 17:56:52 +0200316 rcu_read_unlock();
Johannes Berg62da92f2007-12-19 02:03:31 +0100317 return err;
318}
319
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100320static int ieee80211_config_default_key(struct wiphy *wiphy,
321 struct net_device *dev,
322 u8 key_idx)
323{
Johannes Berg14db74b2008-07-29 13:22:52 +0200324 struct ieee80211_local *local = wiphy_priv(wiphy);
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100325 struct ieee80211_sub_if_data *sdata;
326
Johannes Berg14db74b2008-07-29 13:22:52 +0200327 if (dev == local->mdev)
328 return -EOPNOTSUPP;
329
Johannes Berg3b967662008-04-08 17:56:52 +0200330 rcu_read_lock();
331
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100332 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
333 ieee80211_set_default_key(sdata, key_idx);
334
Johannes Berg3b967662008-04-08 17:56:52 +0200335 rcu_read_unlock();
336
Johannes Berge8cbb4c2007-12-19 02:03:30 +0100337 return 0;
338}
339
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100340static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
341{
Johannes Bergd0709a62008-02-25 16:27:46 +0100342 struct ieee80211_sub_if_data *sdata = sta->sdata;
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100343
344 sinfo->filled = STATION_INFO_INACTIVE_TIME |
345 STATION_INFO_RX_BYTES |
346 STATION_INFO_TX_BYTES;
347
348 sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
349 sinfo->rx_bytes = sta->rx_bytes;
350 sinfo->tx_bytes = sta->tx_bytes;
351
Johannes Berg902acc72008-02-23 15:17:19 +0100352 if (ieee80211_vif_is_mesh(&sdata->vif)) {
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100353#ifdef CONFIG_MAC80211_MESH
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100354 sinfo->filled |= STATION_INFO_LLID |
355 STATION_INFO_PLID |
356 STATION_INFO_PLINK_STATE;
357
358 sinfo->llid = le16_to_cpu(sta->llid);
359 sinfo->plid = le16_to_cpu(sta->plid);
360 sinfo->plink_state = sta->plink_state;
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100361#endif
Johannes Berg902acc72008-02-23 15:17:19 +0100362 }
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100363}
364
365
366static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
367 int idx, u8 *mac, struct station_info *sinfo)
368{
369 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
370 struct sta_info *sta;
Johannes Bergd0709a62008-02-25 16:27:46 +0100371 int ret = -ENOENT;
372
373 rcu_read_lock();
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100374
375 sta = sta_info_get_by_idx(local, idx, dev);
Johannes Bergd0709a62008-02-25 16:27:46 +0100376 if (sta) {
377 ret = 0;
378 memcpy(mac, sta->addr, ETH_ALEN);
379 sta_set_sinfo(sta, sinfo);
380 }
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100381
Johannes Bergd0709a62008-02-25 16:27:46 +0100382 rcu_read_unlock();
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100383
Johannes Bergd0709a62008-02-25 16:27:46 +0100384 return ret;
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100385}
386
Johannes Berg7bbdd2d2007-12-19 02:03:37 +0100387static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100388 u8 *mac, struct station_info *sinfo)
Johannes Berg7bbdd2d2007-12-19 02:03:37 +0100389{
390 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
391 struct sta_info *sta;
Johannes Bergd0709a62008-02-25 16:27:46 +0100392 int ret = -ENOENT;
Johannes Berg7bbdd2d2007-12-19 02:03:37 +0100393
Johannes Bergd0709a62008-02-25 16:27:46 +0100394 rcu_read_lock();
Johannes Berg7bbdd2d2007-12-19 02:03:37 +0100395
396 /* XXX: verify sta->dev == dev */
Johannes Berg7bbdd2d2007-12-19 02:03:37 +0100397
Johannes Bergd0709a62008-02-25 16:27:46 +0100398 sta = sta_info_get(local, mac);
399 if (sta) {
400 ret = 0;
401 sta_set_sinfo(sta, sinfo);
402 }
403
404 rcu_read_unlock();
405
406 return ret;
Johannes Berg7bbdd2d2007-12-19 02:03:37 +0100407}
408
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100409/*
410 * This handles both adding a beacon and setting new beacon info
411 */
412static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata,
413 struct beacon_parameters *params)
414{
415 struct beacon_data *new, *old;
416 int new_head_len, new_tail_len;
417 int size;
418 int err = -EINVAL;
419
420 old = sdata->u.ap.beacon;
421
422 /* head must not be zero-length */
423 if (params->head && !params->head_len)
424 return -EINVAL;
425
426 /*
427 * This is a kludge. beacon interval should really be part
428 * of the beacon information.
429 */
430 if (params->interval) {
431 sdata->local->hw.conf.beacon_int = params->interval;
432 if (ieee80211_hw_config(sdata->local))
433 return -EINVAL;
434 /*
435 * We updated some parameter so if below bails out
436 * it's not an error.
437 */
438 err = 0;
439 }
440
441 /* Need to have a beacon head if we don't have one yet */
442 if (!params->head && !old)
443 return err;
444
445 /* sorry, no way to start beaconing without dtim period */
446 if (!params->dtim_period && !old)
447 return err;
448
449 /* new or old head? */
450 if (params->head)
451 new_head_len = params->head_len;
452 else
453 new_head_len = old->head_len;
454
455 /* new or old tail? */
456 if (params->tail || !old)
457 /* params->tail_len will be zero for !params->tail */
458 new_tail_len = params->tail_len;
459 else
460 new_tail_len = old->tail_len;
461
462 size = sizeof(*new) + new_head_len + new_tail_len;
463
464 new = kzalloc(size, GFP_KERNEL);
465 if (!new)
466 return -ENOMEM;
467
468 /* start filling the new info now */
469
470 /* new or old dtim period? */
471 if (params->dtim_period)
472 new->dtim_period = params->dtim_period;
473 else
474 new->dtim_period = old->dtim_period;
475
476 /*
477 * pointers go into the block we allocated,
478 * memory is | beacon_data | head | tail |
479 */
480 new->head = ((u8 *) new) + sizeof(*new);
481 new->tail = new->head + new_head_len;
482 new->head_len = new_head_len;
483 new->tail_len = new_tail_len;
484
485 /* copy in head */
486 if (params->head)
487 memcpy(new->head, params->head, new_head_len);
488 else
489 memcpy(new->head, old->head, new_head_len);
490
491 /* copy in optional tail */
492 if (params->tail)
493 memcpy(new->tail, params->tail, new_tail_len);
494 else
495 if (old)
496 memcpy(new->tail, old->tail, new_tail_len);
497
498 rcu_assign_pointer(sdata->u.ap.beacon, new);
499
500 synchronize_rcu();
501
502 kfree(old);
503
Johannes Berg9d139c82008-07-09 14:40:37 +0200504 return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100505}
506
507static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev,
508 struct beacon_parameters *params)
509{
Johannes Berg14db74b2008-07-29 13:22:52 +0200510 struct ieee80211_local *local = wiphy_priv(wiphy);
511 struct ieee80211_sub_if_data *sdata;
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100512 struct beacon_data *old;
513
Johannes Berg14db74b2008-07-29 13:22:52 +0200514 if (dev == local->mdev)
515 return -EOPNOTSUPP;
516
517 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
518
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100519 if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
520 return -EINVAL;
521
522 old = sdata->u.ap.beacon;
523
524 if (old)
525 return -EALREADY;
526
527 return ieee80211_config_beacon(sdata, params);
528}
529
530static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev,
531 struct beacon_parameters *params)
532{
Johannes Berg14db74b2008-07-29 13:22:52 +0200533 struct ieee80211_local *local = wiphy_priv(wiphy);
534 struct ieee80211_sub_if_data *sdata;
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100535 struct beacon_data *old;
536
Johannes Berg14db74b2008-07-29 13:22:52 +0200537 if (dev == local->mdev)
538 return -EOPNOTSUPP;
539
540 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
541
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100542 if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
543 return -EINVAL;
544
545 old = sdata->u.ap.beacon;
546
547 if (!old)
548 return -ENOENT;
549
550 return ieee80211_config_beacon(sdata, params);
551}
552
553static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev)
554{
Johannes Berg14db74b2008-07-29 13:22:52 +0200555 struct ieee80211_local *local = wiphy_priv(wiphy);
556 struct ieee80211_sub_if_data *sdata;
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100557 struct beacon_data *old;
558
Johannes Berg14db74b2008-07-29 13:22:52 +0200559 if (dev == local->mdev)
560 return -EOPNOTSUPP;
561
562 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
563
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100564 if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
565 return -EINVAL;
566
567 old = sdata->u.ap.beacon;
568
569 if (!old)
570 return -ENOENT;
571
572 rcu_assign_pointer(sdata->u.ap.beacon, NULL);
573 synchronize_rcu();
574 kfree(old);
575
Johannes Berg9d139c82008-07-09 14:40:37 +0200576 return ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
Johannes Berg5dfdaf52007-12-19 02:03:33 +0100577}
578
Johannes Berg4fd69312007-12-19 02:03:35 +0100579/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */
580struct iapp_layer2_update {
581 u8 da[ETH_ALEN]; /* broadcast */
582 u8 sa[ETH_ALEN]; /* STA addr */
583 __be16 len; /* 6 */
584 u8 dsap; /* 0 */
585 u8 ssap; /* 0 */
586 u8 control;
587 u8 xid_info[3];
588} __attribute__ ((packed));
589
590static void ieee80211_send_layer2_update(struct sta_info *sta)
591{
592 struct iapp_layer2_update *msg;
593 struct sk_buff *skb;
594
595 /* Send Level 2 Update Frame to update forwarding tables in layer 2
596 * bridge devices */
597
598 skb = dev_alloc_skb(sizeof(*msg));
599 if (!skb)
600 return;
601 msg = (struct iapp_layer2_update *)skb_put(skb, sizeof(*msg));
602
603 /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID)
604 * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */
605
606 memset(msg->da, 0xff, ETH_ALEN);
607 memcpy(msg->sa, sta->addr, ETH_ALEN);
608 msg->len = htons(6);
609 msg->dsap = 0;
610 msg->ssap = 0x01; /* NULL LSAP, CR Bit: Response */
611 msg->control = 0xaf; /* XID response lsb.1111F101.
612 * F=0 (no poll command; unsolicited frame) */
613 msg->xid_info[0] = 0x81; /* XID format identifier */
614 msg->xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */
615 msg->xid_info[2] = 0; /* XID sender's receive window size (RW) */
616
Johannes Bergd0709a62008-02-25 16:27:46 +0100617 skb->dev = sta->sdata->dev;
618 skb->protocol = eth_type_trans(skb, sta->sdata->dev);
Johannes Berg4fd69312007-12-19 02:03:35 +0100619 memset(skb->cb, 0, sizeof(skb->cb));
620 netif_rx(skb);
621}
622
623static void sta_apply_parameters(struct ieee80211_local *local,
624 struct sta_info *sta,
625 struct station_parameters *params)
626{
627 u32 rates;
628 int i, j;
Johannes Berg8318d782008-01-24 19:38:38 +0100629 struct ieee80211_supported_band *sband;
Johannes Bergd0709a62008-02-25 16:27:46 +0100630 struct ieee80211_sub_if_data *sdata = sta->sdata;
Johannes Berg4fd69312007-12-19 02:03:35 +0100631
Johannes Berg73651ee2008-02-25 16:27:47 +0100632 /*
633 * FIXME: updating the flags is racy when this function is
634 * called from ieee80211_change_station(), this will
635 * be resolved in a future patch.
636 */
637
Johannes Berg4fd69312007-12-19 02:03:35 +0100638 if (params->station_flags & STATION_FLAG_CHANGED) {
Johannes Berg07346f812008-05-03 01:02:02 +0200639 spin_lock_bh(&sta->lock);
Johannes Berg4fd69312007-12-19 02:03:35 +0100640 sta->flags &= ~WLAN_STA_AUTHORIZED;
641 if (params->station_flags & STATION_FLAG_AUTHORIZED)
642 sta->flags |= WLAN_STA_AUTHORIZED;
643
644 sta->flags &= ~WLAN_STA_SHORT_PREAMBLE;
645 if (params->station_flags & STATION_FLAG_SHORT_PREAMBLE)
646 sta->flags |= WLAN_STA_SHORT_PREAMBLE;
647
648 sta->flags &= ~WLAN_STA_WME;
649 if (params->station_flags & STATION_FLAG_WME)
650 sta->flags |= WLAN_STA_WME;
Johannes Berg07346f812008-05-03 01:02:02 +0200651 spin_unlock_bh(&sta->lock);
Johannes Berg4fd69312007-12-19 02:03:35 +0100652 }
653
Johannes Berg73651ee2008-02-25 16:27:47 +0100654 /*
655 * FIXME: updating the following information is racy when this
656 * function is called from ieee80211_change_station().
657 * However, all this information should be static so
658 * maybe we should just reject attemps to change it.
659 */
660
Johannes Berg4fd69312007-12-19 02:03:35 +0100661 if (params->aid) {
662 sta->aid = params->aid;
663 if (sta->aid > IEEE80211_MAX_AID)
664 sta->aid = 0; /* XXX: should this be an error? */
665 }
666
667 if (params->listen_interval >= 0)
668 sta->listen_interval = params->listen_interval;
669
670 if (params->supported_rates) {
671 rates = 0;
Johannes Berg8318d782008-01-24 19:38:38 +0100672 sband = local->hw.wiphy->bands[local->oper_channel->band];
673
Johannes Berg4fd69312007-12-19 02:03:35 +0100674 for (i = 0; i < params->supported_rates_len; i++) {
675 int rate = (params->supported_rates[i] & 0x7f) * 5;
Johannes Berg8318d782008-01-24 19:38:38 +0100676 for (j = 0; j < sband->n_bitrates; j++) {
677 if (sband->bitrates[j].bitrate == rate)
Johannes Berg4fd69312007-12-19 02:03:35 +0100678 rates |= BIT(j);
679 }
680 }
Johannes Berg8318d782008-01-24 19:38:38 +0100681 sta->supp_rates[local->oper_channel->band] = rates;
Johannes Berg4fd69312007-12-19 02:03:35 +0100682 }
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100683
Jouni Malinen36aedc92008-08-25 11:58:58 +0300684 if (params->ht_capa) {
685 ieee80211_ht_cap_ie_to_ht_info(params->ht_capa,
686 &sta->ht_info);
687 }
688
Johannes Berg902acc72008-02-23 15:17:19 +0100689 if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) {
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100690 switch (params->plink_action) {
691 case PLINK_ACTION_OPEN:
692 mesh_plink_open(sta);
693 break;
694 case PLINK_ACTION_BLOCK:
695 mesh_plink_block(sta);
696 break;
697 }
Johannes Berg902acc72008-02-23 15:17:19 +0100698 }
Johannes Berg4fd69312007-12-19 02:03:35 +0100699}
700
701static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
702 u8 *mac, struct station_parameters *params)
703{
Johannes Berg14db74b2008-07-29 13:22:52 +0200704 struct ieee80211_local *local = wiphy_priv(wiphy);
Johannes Berg4fd69312007-12-19 02:03:35 +0100705 struct sta_info *sta;
706 struct ieee80211_sub_if_data *sdata;
Johannes Berg73651ee2008-02-25 16:27:47 +0100707 int err;
Johannes Berg4fd69312007-12-19 02:03:35 +0100708
Johannes Berg14db74b2008-07-29 13:22:52 +0200709 if (dev == local->mdev || params->vlan == local->mdev)
710 return -EOPNOTSUPP;
711
Johannes Berg4fd69312007-12-19 02:03:35 +0100712 /* Prevent a race with changing the rate control algorithm */
713 if (!netif_running(dev))
714 return -ENETDOWN;
715
Johannes Berg4fd69312007-12-19 02:03:35 +0100716 if (params->vlan) {
717 sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
718
Nicolas Kaiser679fda12008-05-20 18:42:54 +0200719 if (sdata->vif.type != IEEE80211_IF_TYPE_VLAN &&
Johannes Berg4fd69312007-12-19 02:03:35 +0100720 sdata->vif.type != IEEE80211_IF_TYPE_AP)
721 return -EINVAL;
722 } else
723 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
724
Johannes Berg03e44972008-02-27 09:56:40 +0100725 if (compare_ether_addr(mac, dev->dev_addr) == 0)
726 return -EINVAL;
727
728 if (is_multicast_ether_addr(mac))
729 return -EINVAL;
730
731 sta = sta_info_alloc(sdata, mac, GFP_KERNEL);
Johannes Berg73651ee2008-02-25 16:27:47 +0100732 if (!sta)
733 return -ENOMEM;
Johannes Berg4fd69312007-12-19 02:03:35 +0100734
735 sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
736
737 sta_apply_parameters(local, sta, params);
738
739 rate_control_rate_init(sta, local);
740
Johannes Berg73651ee2008-02-25 16:27:47 +0100741 rcu_read_lock();
742
743 err = sta_info_insert(sta);
744 if (err) {
Johannes Berg93e5deb2008-04-01 15:21:00 +0200745 /* STA has been freed */
Johannes Berg73651ee2008-02-25 16:27:47 +0100746 rcu_read_unlock();
747 return err;
748 }
749
750 if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN ||
751 sdata->vif.type == IEEE80211_IF_TYPE_AP)
752 ieee80211_send_layer2_update(sta);
753
754 rcu_read_unlock();
755
Johannes Berg4fd69312007-12-19 02:03:35 +0100756 return 0;
757}
758
759static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
760 u8 *mac)
761{
Johannes Berg14db74b2008-07-29 13:22:52 +0200762 struct ieee80211_local *local = wiphy_priv(wiphy);
763 struct ieee80211_sub_if_data *sdata;
Johannes Berg4fd69312007-12-19 02:03:35 +0100764 struct sta_info *sta;
765
Johannes Berg14db74b2008-07-29 13:22:52 +0200766 if (dev == local->mdev)
767 return -EOPNOTSUPP;
768
769 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
770
Johannes Berg4fd69312007-12-19 02:03:35 +0100771 if (mac) {
Johannes Berg98dd6a52008-04-10 15:36:09 +0200772 rcu_read_lock();
773
Johannes Berg4fd69312007-12-19 02:03:35 +0100774 /* XXX: get sta belonging to dev */
775 sta = sta_info_get(local, mac);
Johannes Berg98dd6a52008-04-10 15:36:09 +0200776 if (!sta) {
777 rcu_read_unlock();
Johannes Berg4fd69312007-12-19 02:03:35 +0100778 return -ENOENT;
Johannes Berg98dd6a52008-04-10 15:36:09 +0200779 }
Johannes Berg4fd69312007-12-19 02:03:35 +0100780
Johannes Bergd0709a62008-02-25 16:27:46 +0100781 sta_info_unlink(&sta);
Johannes Berg98dd6a52008-04-10 15:36:09 +0200782 rcu_read_unlock();
783
Johannes Berg4f6fab42008-03-31 19:23:02 +0200784 sta_info_destroy(sta);
Johannes Berg4fd69312007-12-19 02:03:35 +0100785 } else
Johannes Bergd0709a62008-02-25 16:27:46 +0100786 sta_info_flush(local, sdata);
Johannes Berg4fd69312007-12-19 02:03:35 +0100787
788 return 0;
789}
790
791static int ieee80211_change_station(struct wiphy *wiphy,
792 struct net_device *dev,
793 u8 *mac,
794 struct station_parameters *params)
795{
Johannes Berg14db74b2008-07-29 13:22:52 +0200796 struct ieee80211_local *local = wiphy_priv(wiphy);
Johannes Berg4fd69312007-12-19 02:03:35 +0100797 struct sta_info *sta;
798 struct ieee80211_sub_if_data *vlansdata;
799
Johannes Berg14db74b2008-07-29 13:22:52 +0200800 if (dev == local->mdev || params->vlan == local->mdev)
801 return -EOPNOTSUPP;
802
Johannes Berg98dd6a52008-04-10 15:36:09 +0200803 rcu_read_lock();
804
Johannes Berg4fd69312007-12-19 02:03:35 +0100805 /* XXX: get sta belonging to dev */
806 sta = sta_info_get(local, mac);
Johannes Berg98dd6a52008-04-10 15:36:09 +0200807 if (!sta) {
808 rcu_read_unlock();
Johannes Berg4fd69312007-12-19 02:03:35 +0100809 return -ENOENT;
Johannes Berg98dd6a52008-04-10 15:36:09 +0200810 }
Johannes Berg4fd69312007-12-19 02:03:35 +0100811
Johannes Bergd0709a62008-02-25 16:27:46 +0100812 if (params->vlan && params->vlan != sta->sdata->dev) {
Johannes Berg4fd69312007-12-19 02:03:35 +0100813 vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
814
Nicolas Kaiser679fda12008-05-20 18:42:54 +0200815 if (vlansdata->vif.type != IEEE80211_IF_TYPE_VLAN &&
Johannes Berg98dd6a52008-04-10 15:36:09 +0200816 vlansdata->vif.type != IEEE80211_IF_TYPE_AP) {
817 rcu_read_unlock();
Johannes Berg4fd69312007-12-19 02:03:35 +0100818 return -EINVAL;
Johannes Berg98dd6a52008-04-10 15:36:09 +0200819 }
Johannes Berg4fd69312007-12-19 02:03:35 +0100820
Johannes Berg14db74b2008-07-29 13:22:52 +0200821 sta->sdata = vlansdata;
Johannes Berg4fd69312007-12-19 02:03:35 +0100822 ieee80211_send_layer2_update(sta);
823 }
824
825 sta_apply_parameters(local, sta, params);
826
Johannes Berg98dd6a52008-04-10 15:36:09 +0200827 rcu_read_unlock();
828
Johannes Berg4fd69312007-12-19 02:03:35 +0100829 return 0;
830}
831
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100832#ifdef CONFIG_MAC80211_MESH
833static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
834 u8 *dst, u8 *next_hop)
835{
Johannes Berg14db74b2008-07-29 13:22:52 +0200836 struct ieee80211_local *local = wiphy_priv(wiphy);
837 struct ieee80211_sub_if_data *sdata;
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100838 struct mesh_path *mpath;
839 struct sta_info *sta;
840 int err;
841
Johannes Berg14db74b2008-07-29 13:22:52 +0200842 if (dev == local->mdev)
843 return -EOPNOTSUPP;
844
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100845 if (!netif_running(dev))
846 return -ENETDOWN;
847
Johannes Berg14db74b2008-07-29 13:22:52 +0200848 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
849
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100850 if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
851 return -ENOTSUPP;
852
Johannes Bergd0709a62008-02-25 16:27:46 +0100853 rcu_read_lock();
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100854 sta = sta_info_get(local, next_hop);
Johannes Bergd0709a62008-02-25 16:27:46 +0100855 if (!sta) {
856 rcu_read_unlock();
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100857 return -ENOENT;
Johannes Bergd0709a62008-02-25 16:27:46 +0100858 }
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100859
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200860 err = mesh_path_add(dst, sdata);
Johannes Bergd0709a62008-02-25 16:27:46 +0100861 if (err) {
862 rcu_read_unlock();
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100863 return err;
Johannes Bergd0709a62008-02-25 16:27:46 +0100864 }
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100865
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200866 mpath = mesh_path_lookup(dst, sdata);
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100867 if (!mpath) {
868 rcu_read_unlock();
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100869 return -ENXIO;
870 }
871 mesh_path_fix_nexthop(mpath, sta);
Johannes Bergd0709a62008-02-25 16:27:46 +0100872
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100873 rcu_read_unlock();
874 return 0;
875}
876
877static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
878 u8 *dst)
879{
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200880 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100881
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200882 if (dst)
883 return mesh_path_del(dst, sdata);
884
885 mesh_path_flush(sdata);
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100886 return 0;
887}
888
889static int ieee80211_change_mpath(struct wiphy *wiphy,
890 struct net_device *dev,
891 u8 *dst, u8 *next_hop)
892{
Johannes Berg14db74b2008-07-29 13:22:52 +0200893 struct ieee80211_local *local = wiphy_priv(wiphy);
894 struct ieee80211_sub_if_data *sdata;
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100895 struct mesh_path *mpath;
896 struct sta_info *sta;
897
Johannes Berg14db74b2008-07-29 13:22:52 +0200898 if (dev == local->mdev)
899 return -EOPNOTSUPP;
900
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100901 if (!netif_running(dev))
902 return -ENETDOWN;
903
Johannes Berg14db74b2008-07-29 13:22:52 +0200904 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
905
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100906 if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
907 return -ENOTSUPP;
908
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100909 rcu_read_lock();
Johannes Bergd0709a62008-02-25 16:27:46 +0100910
911 sta = sta_info_get(local, next_hop);
912 if (!sta) {
913 rcu_read_unlock();
914 return -ENOENT;
915 }
916
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200917 mpath = mesh_path_lookup(dst, sdata);
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100918 if (!mpath) {
919 rcu_read_unlock();
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100920 return -ENOENT;
921 }
922
923 mesh_path_fix_nexthop(mpath, sta);
Johannes Bergd0709a62008-02-25 16:27:46 +0100924
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100925 rcu_read_unlock();
926 return 0;
927}
928
929static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
930 struct mpath_info *pinfo)
931{
932 if (mpath->next_hop)
933 memcpy(next_hop, mpath->next_hop->addr, ETH_ALEN);
934 else
935 memset(next_hop, 0, ETH_ALEN);
936
937 pinfo->filled = MPATH_INFO_FRAME_QLEN |
938 MPATH_INFO_DSN |
939 MPATH_INFO_METRIC |
940 MPATH_INFO_EXPTIME |
941 MPATH_INFO_DISCOVERY_TIMEOUT |
942 MPATH_INFO_DISCOVERY_RETRIES |
943 MPATH_INFO_FLAGS;
944
945 pinfo->frame_qlen = mpath->frame_queue.qlen;
946 pinfo->dsn = mpath->dsn;
947 pinfo->metric = mpath->metric;
948 if (time_before(jiffies, mpath->exp_time))
949 pinfo->exptime = jiffies_to_msecs(mpath->exp_time - jiffies);
950 pinfo->discovery_timeout =
951 jiffies_to_msecs(mpath->discovery_timeout);
952 pinfo->discovery_retries = mpath->discovery_retries;
953 pinfo->flags = 0;
954 if (mpath->flags & MESH_PATH_ACTIVE)
955 pinfo->flags |= NL80211_MPATH_FLAG_ACTIVE;
956 if (mpath->flags & MESH_PATH_RESOLVING)
957 pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
958 if (mpath->flags & MESH_PATH_DSN_VALID)
959 pinfo->flags |= NL80211_MPATH_FLAG_DSN_VALID;
960 if (mpath->flags & MESH_PATH_FIXED)
961 pinfo->flags |= NL80211_MPATH_FLAG_FIXED;
962 if (mpath->flags & MESH_PATH_RESOLVING)
963 pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
964
965 pinfo->flags = mpath->flags;
966}
967
968static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
969 u8 *dst, u8 *next_hop, struct mpath_info *pinfo)
970
971{
Johannes Berg14db74b2008-07-29 13:22:52 +0200972 struct ieee80211_local *local = wiphy_priv(wiphy);
973 struct ieee80211_sub_if_data *sdata;
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100974 struct mesh_path *mpath;
975
Johannes Berg14db74b2008-07-29 13:22:52 +0200976 if (dev == local->mdev)
977 return -EOPNOTSUPP;
978
979 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
980
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100981 if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
982 return -ENOTSUPP;
983
984 rcu_read_lock();
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200985 mpath = mesh_path_lookup(dst, sdata);
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +0100986 if (!mpath) {
987 rcu_read_unlock();
988 return -ENOENT;
989 }
990 memcpy(dst, mpath->dst, ETH_ALEN);
991 mpath_set_pinfo(mpath, next_hop, pinfo);
992 rcu_read_unlock();
993 return 0;
994}
995
996static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
997 int idx, u8 *dst, u8 *next_hop,
998 struct mpath_info *pinfo)
999{
Johannes Berg14db74b2008-07-29 13:22:52 +02001000 struct ieee80211_local *local = wiphy_priv(wiphy);
1001 struct ieee80211_sub_if_data *sdata;
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +01001002 struct mesh_path *mpath;
1003
Johannes Berg14db74b2008-07-29 13:22:52 +02001004 if (dev == local->mdev)
1005 return -EOPNOTSUPP;
1006
1007 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
1008
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +01001009 if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
1010 return -ENOTSUPP;
1011
1012 rcu_read_lock();
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +12001013 mpath = mesh_path_lookup_by_idx(idx, sdata);
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +01001014 if (!mpath) {
1015 rcu_read_unlock();
1016 return -ENOENT;
1017 }
1018 memcpy(dst, mpath->dst, ETH_ALEN);
1019 mpath_set_pinfo(mpath, next_hop, pinfo);
1020 rcu_read_unlock();
1021 return 0;
1022}
1023#endif
1024
Jouni Malinen9f1ba902008-08-07 20:07:01 +03001025static int ieee80211_change_bss(struct wiphy *wiphy,
1026 struct net_device *dev,
1027 struct bss_parameters *params)
1028{
1029 struct ieee80211_local *local = wiphy_priv(wiphy);
1030 struct ieee80211_sub_if_data *sdata;
1031 u32 changed = 0;
1032
1033 if (dev == local->mdev)
1034 return -EOPNOTSUPP;
1035
1036 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
1037
1038 if (sdata->vif.type != IEEE80211_IF_TYPE_AP)
1039 return -EINVAL;
1040
1041 if (params->use_cts_prot >= 0) {
1042 sdata->bss_conf.use_cts_prot = params->use_cts_prot;
1043 changed |= BSS_CHANGED_ERP_CTS_PROT;
1044 }
1045 if (params->use_short_preamble >= 0) {
1046 sdata->bss_conf.use_short_preamble =
1047 params->use_short_preamble;
1048 changed |= BSS_CHANGED_ERP_PREAMBLE;
1049 }
1050 if (params->use_short_slot_time >= 0) {
1051 sdata->bss_conf.use_short_slot =
1052 params->use_short_slot_time;
1053 changed |= BSS_CHANGED_ERP_SLOT;
1054 }
1055
1056 ieee80211_bss_info_change_notify(sdata, changed);
1057
1058 return 0;
1059}
1060
Jiri Bencf0706e822007-05-05 11:45:53 -07001061struct cfg80211_ops mac80211_config_ops = {
1062 .add_virtual_intf = ieee80211_add_iface,
1063 .del_virtual_intf = ieee80211_del_iface,
Johannes Berg42613db2007-09-28 21:52:27 +02001064 .change_virtual_intf = ieee80211_change_iface,
Johannes Berge8cbb4c2007-12-19 02:03:30 +01001065 .add_key = ieee80211_add_key,
1066 .del_key = ieee80211_del_key,
Johannes Berg62da92f2007-12-19 02:03:31 +01001067 .get_key = ieee80211_get_key,
Johannes Berge8cbb4c2007-12-19 02:03:30 +01001068 .set_default_key = ieee80211_config_default_key,
Johannes Berg5dfdaf52007-12-19 02:03:33 +01001069 .add_beacon = ieee80211_add_beacon,
1070 .set_beacon = ieee80211_set_beacon,
1071 .del_beacon = ieee80211_del_beacon,
Johannes Berg4fd69312007-12-19 02:03:35 +01001072 .add_station = ieee80211_add_station,
1073 .del_station = ieee80211_del_station,
1074 .change_station = ieee80211_change_station,
Johannes Berg7bbdd2d2007-12-19 02:03:37 +01001075 .get_station = ieee80211_get_station,
Luis Carlos Coboc5dd9c22008-02-23 15:17:17 +01001076 .dump_station = ieee80211_dump_station,
1077#ifdef CONFIG_MAC80211_MESH
1078 .add_mpath = ieee80211_add_mpath,
1079 .del_mpath = ieee80211_del_mpath,
1080 .change_mpath = ieee80211_change_mpath,
1081 .get_mpath = ieee80211_get_mpath,
1082 .dump_mpath = ieee80211_dump_mpath,
1083#endif
Jouni Malinen9f1ba902008-08-07 20:07:01 +03001084 .change_bss = ieee80211_change_bss,
Jiri Bencf0706e822007-05-05 11:45:53 -07001085};