diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2007-12-18 20:03:33 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 18:09:38 -0500 |
commit | 5dfdaf58d61f06a458529430c24b1191ea4d1a27 (patch) | |
tree | bd3fac57f66e80bf2a31d253af19093f4020ba79 /net/mac80211/cfg.c | |
parent | 51fb61e76d952e6bc2fbdd9f0d38425fbab1cf31 (diff) |
mac80211: add beacon configuration via cfg80211
This patch implements the cfg80211 hooks for configuring beaconing
on an access point interface in mac80211. While doing so, it fixes
a number of races that could badly crash the machine when the
beacon is changed while being requested by the driver.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/cfg.c')
-rw-r--r-- | net/mac80211/cfg.c | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d02d9ef6b1ef..5a4c6edd9348 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <linux/nl80211.h> | 10 | #include <linux/nl80211.h> |
11 | #include <linux/rtnetlink.h> | 11 | #include <linux/rtnetlink.h> |
12 | #include <net/net_namespace.h> | 12 | #include <net/net_namespace.h> |
13 | #include <linux/rcupdate.h> | ||
13 | #include <net/cfg80211.h> | 14 | #include <net/cfg80211.h> |
14 | #include "ieee80211_i.h" | 15 | #include "ieee80211_i.h" |
15 | #include "cfg.h" | 16 | #include "cfg.h" |
@@ -294,6 +295,158 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, | |||
294 | return 0; | 295 | return 0; |
295 | } | 296 | } |
296 | 297 | ||
298 | /* | ||
299 | * This handles both adding a beacon and setting new beacon info | ||
300 | */ | ||
301 | static int ieee80211_config_beacon(struct ieee80211_sub_if_data *sdata, | ||
302 | struct beacon_parameters *params) | ||
303 | { | ||
304 | struct beacon_data *new, *old; | ||
305 | int new_head_len, new_tail_len; | ||
306 | int size; | ||
307 | int err = -EINVAL; | ||
308 | |||
309 | old = sdata->u.ap.beacon; | ||
310 | |||
311 | /* head must not be zero-length */ | ||
312 | if (params->head && !params->head_len) | ||
313 | return -EINVAL; | ||
314 | |||
315 | /* | ||
316 | * This is a kludge. beacon interval should really be part | ||
317 | * of the beacon information. | ||
318 | */ | ||
319 | if (params->interval) { | ||
320 | sdata->local->hw.conf.beacon_int = params->interval; | ||
321 | if (ieee80211_hw_config(sdata->local)) | ||
322 | return -EINVAL; | ||
323 | /* | ||
324 | * We updated some parameter so if below bails out | ||
325 | * it's not an error. | ||
326 | */ | ||
327 | err = 0; | ||
328 | } | ||
329 | |||
330 | /* Need to have a beacon head if we don't have one yet */ | ||
331 | if (!params->head && !old) | ||
332 | return err; | ||
333 | |||
334 | /* sorry, no way to start beaconing without dtim period */ | ||
335 | if (!params->dtim_period && !old) | ||
336 | return err; | ||
337 | |||
338 | /* new or old head? */ | ||
339 | if (params->head) | ||
340 | new_head_len = params->head_len; | ||
341 | else | ||
342 | new_head_len = old->head_len; | ||
343 | |||
344 | /* new or old tail? */ | ||
345 | if (params->tail || !old) | ||
346 | /* params->tail_len will be zero for !params->tail */ | ||
347 | new_tail_len = params->tail_len; | ||
348 | else | ||
349 | new_tail_len = old->tail_len; | ||
350 | |||
351 | size = sizeof(*new) + new_head_len + new_tail_len; | ||
352 | |||
353 | new = kzalloc(size, GFP_KERNEL); | ||
354 | if (!new) | ||
355 | return -ENOMEM; | ||
356 | |||
357 | /* start filling the new info now */ | ||
358 | |||
359 | /* new or old dtim period? */ | ||
360 | if (params->dtim_period) | ||
361 | new->dtim_period = params->dtim_period; | ||
362 | else | ||
363 | new->dtim_period = old->dtim_period; | ||
364 | |||
365 | /* | ||
366 | * pointers go into the block we allocated, | ||
367 | * memory is | beacon_data | head | tail | | ||
368 | */ | ||
369 | new->head = ((u8 *) new) + sizeof(*new); | ||
370 | new->tail = new->head + new_head_len; | ||
371 | new->head_len = new_head_len; | ||
372 | new->tail_len = new_tail_len; | ||
373 | |||
374 | /* copy in head */ | ||
375 | if (params->head) | ||
376 | memcpy(new->head, params->head, new_head_len); | ||
377 | else | ||
378 | memcpy(new->head, old->head, new_head_len); | ||
379 | |||
380 | /* copy in optional tail */ | ||
381 | if (params->tail) | ||
382 | memcpy(new->tail, params->tail, new_tail_len); | ||
383 | else | ||
384 | if (old) | ||
385 | memcpy(new->tail, old->tail, new_tail_len); | ||
386 | |||
387 | rcu_assign_pointer(sdata->u.ap.beacon, new); | ||
388 | |||
389 | synchronize_rcu(); | ||
390 | |||
391 | kfree(old); | ||
392 | |||
393 | return ieee80211_if_config_beacon(sdata->dev); | ||
394 | } | ||
395 | |||
396 | static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, | ||
397 | struct beacon_parameters *params) | ||
398 | { | ||
399 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
400 | struct beacon_data *old; | ||
401 | |||
402 | if (sdata->vif.type != IEEE80211_IF_TYPE_AP) | ||
403 | return -EINVAL; | ||
404 | |||
405 | old = sdata->u.ap.beacon; | ||
406 | |||
407 | if (old) | ||
408 | return -EALREADY; | ||
409 | |||
410 | return ieee80211_config_beacon(sdata, params); | ||
411 | } | ||
412 | |||
413 | static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, | ||
414 | struct beacon_parameters *params) | ||
415 | { | ||
416 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
417 | struct beacon_data *old; | ||
418 | |||
419 | if (sdata->vif.type != IEEE80211_IF_TYPE_AP) | ||
420 | return -EINVAL; | ||
421 | |||
422 | old = sdata->u.ap.beacon; | ||
423 | |||
424 | if (!old) | ||
425 | return -ENOENT; | ||
426 | |||
427 | return ieee80211_config_beacon(sdata, params); | ||
428 | } | ||
429 | |||
430 | static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) | ||
431 | { | ||
432 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
433 | struct beacon_data *old; | ||
434 | |||
435 | if (sdata->vif.type != IEEE80211_IF_TYPE_AP) | ||
436 | return -EINVAL; | ||
437 | |||
438 | old = sdata->u.ap.beacon; | ||
439 | |||
440 | if (!old) | ||
441 | return -ENOENT; | ||
442 | |||
443 | rcu_assign_pointer(sdata->u.ap.beacon, NULL); | ||
444 | synchronize_rcu(); | ||
445 | kfree(old); | ||
446 | |||
447 | return ieee80211_if_config_beacon(dev); | ||
448 | } | ||
449 | |||
297 | struct cfg80211_ops mac80211_config_ops = { | 450 | struct cfg80211_ops mac80211_config_ops = { |
298 | .add_virtual_intf = ieee80211_add_iface, | 451 | .add_virtual_intf = ieee80211_add_iface, |
299 | .del_virtual_intf = ieee80211_del_iface, | 452 | .del_virtual_intf = ieee80211_del_iface, |
@@ -302,5 +455,8 @@ struct cfg80211_ops mac80211_config_ops = { | |||
302 | .del_key = ieee80211_del_key, | 455 | .del_key = ieee80211_del_key, |
303 | .get_key = ieee80211_get_key, | 456 | .get_key = ieee80211_get_key, |
304 | .set_default_key = ieee80211_config_default_key, | 457 | .set_default_key = ieee80211_config_default_key, |
458 | .add_beacon = ieee80211_add_beacon, | ||
459 | .set_beacon = ieee80211_set_beacon, | ||
460 | .del_beacon = ieee80211_del_beacon, | ||
305 | .get_station = ieee80211_get_station, | 461 | .get_station = ieee80211_get_station, |
306 | }; | 462 | }; |