diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/iface.c | 4 | ||||
-rw-r--r-- | net/mac80211/main.c | 22 | ||||
-rw-r--r-- | net/mac80211/mesh.h | 7 | ||||
-rw-r--r-- | net/mac80211/mesh_pathtbl.c | 204 | ||||
-rw-r--r-- | net/mac80211/scan.c | 5 | ||||
-rw-r--r-- | net/rfkill/Kconfig | 9 | ||||
-rw-r--r-- | net/rfkill/Makefile | 1 | ||||
-rw-r--r-- | net/rfkill/rfkill-gpio.c | 227 | ||||
-rw-r--r-- | net/wireless/core.h | 5 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 12 | ||||
-rw-r--r-- | net/wireless/sme.c | 19 | ||||
-rw-r--r-- | net/wireless/util.c | 2 |
12 files changed, 417 insertions, 100 deletions
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 7dfbe71dc637..49d4f869e0bc 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -384,11 +384,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, | |||
384 | int i; | 384 | int i; |
385 | enum nl80211_channel_type orig_ct; | 385 | enum nl80211_channel_type orig_ct; |
386 | 386 | ||
387 | clear_bit(SDATA_STATE_RUNNING, &sdata->state); | ||
388 | |||
387 | if (local->scan_sdata == sdata) | 389 | if (local->scan_sdata == sdata) |
388 | ieee80211_scan_cancel(local); | 390 | ieee80211_scan_cancel(local); |
389 | 391 | ||
390 | clear_bit(SDATA_STATE_RUNNING, &sdata->state); | ||
391 | |||
392 | /* | 392 | /* |
393 | * Stop TX on this interface first. | 393 | * Stop TX on this interface first. |
394 | */ | 394 | */ |
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 0d7b08db8e56..866f269183cf 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -752,11 +752,25 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
752 | hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); | 752 | hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); |
753 | hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR); | 753 | hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR); |
754 | 754 | ||
755 | /* mac80211 doesn't support more than 1 channel */ | 755 | /* |
756 | for (i = 0; i < hw->wiphy->n_iface_combinations; i++) | 756 | * mac80211 doesn't support more than 1 channel, and also not more |
757 | if (hw->wiphy->iface_combinations[i].num_different_channels > 1) | 757 | * than one IBSS interface |
758 | */ | ||
759 | for (i = 0; i < hw->wiphy->n_iface_combinations; i++) { | ||
760 | const struct ieee80211_iface_combination *c; | ||
761 | int j; | ||
762 | |||
763 | c = &hw->wiphy->iface_combinations[i]; | ||
764 | |||
765 | if (c->num_different_channels > 1) | ||
758 | return -EINVAL; | 766 | return -EINVAL; |
759 | 767 | ||
768 | for (j = 0; j < c->n_limits; j++) | ||
769 | if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) && | ||
770 | c->limits[j].max > 1) | ||
771 | return -EINVAL; | ||
772 | } | ||
773 | |||
760 | #ifndef CONFIG_MAC80211_MESH | 774 | #ifndef CONFIG_MAC80211_MESH |
761 | /* mesh depends on Kconfig, but drivers should set it if they want */ | 775 | /* mesh depends on Kconfig, but drivers should set it if they want */ |
762 | local->hw.wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MESH_POINT); | 776 | local->hw.wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MESH_POINT); |
@@ -1076,6 +1090,8 @@ static void __exit ieee80211_exit(void) | |||
1076 | ieee80211s_stop(); | 1090 | ieee80211s_stop(); |
1077 | 1091 | ||
1078 | ieee80211_iface_exit(); | 1092 | ieee80211_iface_exit(); |
1093 | |||
1094 | rcu_barrier(); | ||
1079 | } | 1095 | } |
1080 | 1096 | ||
1081 | 1097 | ||
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index e7c5fddb4804..249e733362e7 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h | |||
@@ -120,6 +120,7 @@ struct mesh_path { | |||
120 | * buckets | 120 | * buckets |
121 | * @mean_chain_len: maximum average length for the hash buckets' list, if it is | 121 | * @mean_chain_len: maximum average length for the hash buckets' list, if it is |
122 | * reached, the table will grow | 122 | * reached, the table will grow |
123 | * rcu_head: RCU head to free the table | ||
123 | */ | 124 | */ |
124 | struct mesh_table { | 125 | struct mesh_table { |
125 | /* Number of buckets will be 2^N */ | 126 | /* Number of buckets will be 2^N */ |
@@ -132,6 +133,8 @@ struct mesh_table { | |||
132 | int (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl); | 133 | int (*copy_node) (struct hlist_node *p, struct mesh_table *newtbl); |
133 | int size_order; | 134 | int size_order; |
134 | int mean_chain_len; | 135 | int mean_chain_len; |
136 | |||
137 | struct rcu_head rcu_head; | ||
135 | }; | 138 | }; |
136 | 139 | ||
137 | /* Recent multicast cache */ | 140 | /* Recent multicast cache */ |
@@ -286,10 +289,6 @@ static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata) | |||
286 | return sdata->u.mesh.mesh_pp_id == IEEE80211_PATH_PROTOCOL_HWMP; | 289 | return sdata->u.mesh.mesh_pp_id == IEEE80211_PATH_PROTOCOL_HWMP; |
287 | } | 290 | } |
288 | 291 | ||
289 | #define for_each_mesh_entry(x, p, node, i) \ | ||
290 | for (i = 0; i <= x->hash_mask; i++) \ | ||
291 | hlist_for_each_entry_rcu(node, p, &x->hash_buckets[i], list) | ||
292 | |||
293 | void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local); | 292 | void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local); |
294 | 293 | ||
295 | void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata); | 294 | void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata); |
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 83ce48e31913..0d2faacc3e87 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c | |||
@@ -36,8 +36,8 @@ struct mpath_node { | |||
36 | struct mesh_path *mpath; | 36 | struct mesh_path *mpath; |
37 | }; | 37 | }; |
38 | 38 | ||
39 | static struct mesh_table *mesh_paths; | 39 | static struct mesh_table __rcu *mesh_paths; |
40 | static struct mesh_table *mpp_paths; /* Store paths for MPP&MAP */ | 40 | static struct mesh_table __rcu *mpp_paths; /* Store paths for MPP&MAP */ |
41 | 41 | ||
42 | int mesh_paths_generation; | 42 | int mesh_paths_generation; |
43 | 43 | ||
@@ -48,17 +48,40 @@ int mesh_paths_generation; | |||
48 | static DEFINE_RWLOCK(pathtbl_resize_lock); | 48 | static DEFINE_RWLOCK(pathtbl_resize_lock); |
49 | 49 | ||
50 | 50 | ||
51 | static inline struct mesh_table *resize_dereference_mesh_paths(void) | ||
52 | { | ||
53 | return rcu_dereference_protected(mesh_paths, | ||
54 | lockdep_is_held(&pathtbl_resize_lock)); | ||
55 | } | ||
56 | |||
57 | static inline struct mesh_table *resize_dereference_mpp_paths(void) | ||
58 | { | ||
59 | return rcu_dereference_protected(mpp_paths, | ||
60 | lockdep_is_held(&pathtbl_resize_lock)); | ||
61 | } | ||
62 | |||
63 | /* | ||
64 | * CAREFUL -- "tbl" must not be an expression, | ||
65 | * in particular not an rcu_dereference(), since | ||
66 | * it's used twice. So it is illegal to do | ||
67 | * for_each_mesh_entry(rcu_dereference(...), ...) | ||
68 | */ | ||
69 | #define for_each_mesh_entry(tbl, p, node, i) \ | ||
70 | for (i = 0; i <= tbl->hash_mask; i++) \ | ||
71 | hlist_for_each_entry_rcu(node, p, &tbl->hash_buckets[i], list) | ||
72 | |||
73 | |||
51 | static struct mesh_table *mesh_table_alloc(int size_order) | 74 | static struct mesh_table *mesh_table_alloc(int size_order) |
52 | { | 75 | { |
53 | int i; | 76 | int i; |
54 | struct mesh_table *newtbl; | 77 | struct mesh_table *newtbl; |
55 | 78 | ||
56 | newtbl = kmalloc(sizeof(struct mesh_table), GFP_KERNEL); | 79 | newtbl = kmalloc(sizeof(struct mesh_table), GFP_ATOMIC); |
57 | if (!newtbl) | 80 | if (!newtbl) |
58 | return NULL; | 81 | return NULL; |
59 | 82 | ||
60 | newtbl->hash_buckets = kzalloc(sizeof(struct hlist_head) * | 83 | newtbl->hash_buckets = kzalloc(sizeof(struct hlist_head) * |
61 | (1 << size_order), GFP_KERNEL); | 84 | (1 << size_order), GFP_ATOMIC); |
62 | 85 | ||
63 | if (!newtbl->hash_buckets) { | 86 | if (!newtbl->hash_buckets) { |
64 | kfree(newtbl); | 87 | kfree(newtbl); |
@@ -66,7 +89,7 @@ static struct mesh_table *mesh_table_alloc(int size_order) | |||
66 | } | 89 | } |
67 | 90 | ||
68 | newtbl->hashwlock = kmalloc(sizeof(spinlock_t) * | 91 | newtbl->hashwlock = kmalloc(sizeof(spinlock_t) * |
69 | (1 << size_order), GFP_KERNEL); | 92 | (1 << size_order), GFP_ATOMIC); |
70 | if (!newtbl->hashwlock) { | 93 | if (!newtbl->hashwlock) { |
71 | kfree(newtbl->hash_buckets); | 94 | kfree(newtbl->hash_buckets); |
72 | kfree(newtbl); | 95 | kfree(newtbl); |
@@ -258,12 +281,13 @@ struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata) | |||
258 | */ | 281 | */ |
259 | struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data *sdata) | 282 | struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data *sdata) |
260 | { | 283 | { |
284 | struct mesh_table *tbl = rcu_dereference(mesh_paths); | ||
261 | struct mpath_node *node; | 285 | struct mpath_node *node; |
262 | struct hlist_node *p; | 286 | struct hlist_node *p; |
263 | int i; | 287 | int i; |
264 | int j = 0; | 288 | int j = 0; |
265 | 289 | ||
266 | for_each_mesh_entry(mesh_paths, p, node, i) { | 290 | for_each_mesh_entry(tbl, p, node, i) { |
267 | if (sdata && node->mpath->sdata != sdata) | 291 | if (sdata && node->mpath->sdata != sdata) |
268 | continue; | 292 | continue; |
269 | if (j++ == idx) { | 293 | if (j++ == idx) { |
@@ -293,6 +317,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) | |||
293 | { | 317 | { |
294 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | 318 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; |
295 | struct ieee80211_local *local = sdata->local; | 319 | struct ieee80211_local *local = sdata->local; |
320 | struct mesh_table *tbl; | ||
296 | struct mesh_path *mpath, *new_mpath; | 321 | struct mesh_path *mpath, *new_mpath; |
297 | struct mpath_node *node, *new_node; | 322 | struct mpath_node *node, *new_node; |
298 | struct hlist_head *bucket; | 323 | struct hlist_head *bucket; |
@@ -332,10 +357,12 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) | |||
332 | spin_lock_init(&new_mpath->state_lock); | 357 | spin_lock_init(&new_mpath->state_lock); |
333 | init_timer(&new_mpath->timer); | 358 | init_timer(&new_mpath->timer); |
334 | 359 | ||
335 | hash_idx = mesh_table_hash(dst, sdata, mesh_paths); | 360 | tbl = resize_dereference_mesh_paths(); |
336 | bucket = &mesh_paths->hash_buckets[hash_idx]; | 361 | |
362 | hash_idx = mesh_table_hash(dst, sdata, tbl); | ||
363 | bucket = &tbl->hash_buckets[hash_idx]; | ||
337 | 364 | ||
338 | spin_lock_bh(&mesh_paths->hashwlock[hash_idx]); | 365 | spin_lock_bh(&tbl->hashwlock[hash_idx]); |
339 | 366 | ||
340 | err = -EEXIST; | 367 | err = -EEXIST; |
341 | hlist_for_each_entry(node, n, bucket, list) { | 368 | hlist_for_each_entry(node, n, bucket, list) { |
@@ -345,13 +372,13 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) | |||
345 | } | 372 | } |
346 | 373 | ||
347 | hlist_add_head_rcu(&new_node->list, bucket); | 374 | hlist_add_head_rcu(&new_node->list, bucket); |
348 | if (atomic_inc_return(&mesh_paths->entries) >= | 375 | if (atomic_inc_return(&tbl->entries) >= |
349 | mesh_paths->mean_chain_len * (mesh_paths->hash_mask + 1)) | 376 | tbl->mean_chain_len * (tbl->hash_mask + 1)) |
350 | grow = 1; | 377 | grow = 1; |
351 | 378 | ||
352 | mesh_paths_generation++; | 379 | mesh_paths_generation++; |
353 | 380 | ||
354 | spin_unlock_bh(&mesh_paths->hashwlock[hash_idx]); | 381 | spin_unlock_bh(&tbl->hashwlock[hash_idx]); |
355 | read_unlock_bh(&pathtbl_resize_lock); | 382 | read_unlock_bh(&pathtbl_resize_lock); |
356 | if (grow) { | 383 | if (grow) { |
357 | set_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags); | 384 | set_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags); |
@@ -360,7 +387,7 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata) | |||
360 | return 0; | 387 | return 0; |
361 | 388 | ||
362 | err_exists: | 389 | err_exists: |
363 | spin_unlock_bh(&mesh_paths->hashwlock[hash_idx]); | 390 | spin_unlock_bh(&tbl->hashwlock[hash_idx]); |
364 | read_unlock_bh(&pathtbl_resize_lock); | 391 | read_unlock_bh(&pathtbl_resize_lock); |
365 | kfree(new_node); | 392 | kfree(new_node); |
366 | err_node_alloc: | 393 | err_node_alloc: |
@@ -370,58 +397,59 @@ err_path_alloc: | |||
370 | return err; | 397 | return err; |
371 | } | 398 | } |
372 | 399 | ||
400 | static void mesh_table_free_rcu(struct rcu_head *rcu) | ||
401 | { | ||
402 | struct mesh_table *tbl = container_of(rcu, struct mesh_table, rcu_head); | ||
403 | |||
404 | mesh_table_free(tbl, false); | ||
405 | } | ||
406 | |||
373 | void mesh_mpath_table_grow(void) | 407 | void mesh_mpath_table_grow(void) |
374 | { | 408 | { |
375 | struct mesh_table *oldtbl, *newtbl; | 409 | struct mesh_table *oldtbl, *newtbl; |
376 | 410 | ||
377 | rcu_read_lock(); | ||
378 | newtbl = mesh_table_alloc(rcu_dereference(mesh_paths)->size_order + 1); | ||
379 | if (!newtbl) | ||
380 | return; | ||
381 | write_lock_bh(&pathtbl_resize_lock); | 411 | write_lock_bh(&pathtbl_resize_lock); |
382 | oldtbl = mesh_paths; | 412 | oldtbl = resize_dereference_mesh_paths(); |
383 | if (mesh_table_grow(mesh_paths, newtbl) < 0) { | 413 | newtbl = mesh_table_alloc(oldtbl->size_order + 1); |
384 | rcu_read_unlock(); | 414 | if (!newtbl) |
415 | goto out; | ||
416 | if (mesh_table_grow(oldtbl, newtbl) < 0) { | ||
385 | __mesh_table_free(newtbl); | 417 | __mesh_table_free(newtbl); |
386 | write_unlock_bh(&pathtbl_resize_lock); | 418 | goto out; |
387 | return; | ||
388 | } | 419 | } |
389 | rcu_read_unlock(); | ||
390 | rcu_assign_pointer(mesh_paths, newtbl); | 420 | rcu_assign_pointer(mesh_paths, newtbl); |
391 | write_unlock_bh(&pathtbl_resize_lock); | ||
392 | 421 | ||
393 | synchronize_rcu(); | 422 | call_rcu(&oldtbl->rcu_head, mesh_table_free_rcu); |
394 | mesh_table_free(oldtbl, false); | 423 | |
424 | out: | ||
425 | write_unlock_bh(&pathtbl_resize_lock); | ||
395 | } | 426 | } |
396 | 427 | ||
397 | void mesh_mpp_table_grow(void) | 428 | void mesh_mpp_table_grow(void) |
398 | { | 429 | { |
399 | struct mesh_table *oldtbl, *newtbl; | 430 | struct mesh_table *oldtbl, *newtbl; |
400 | 431 | ||
401 | rcu_read_lock(); | ||
402 | newtbl = mesh_table_alloc(rcu_dereference(mpp_paths)->size_order + 1); | ||
403 | if (!newtbl) | ||
404 | return; | ||
405 | write_lock_bh(&pathtbl_resize_lock); | 432 | write_lock_bh(&pathtbl_resize_lock); |
406 | oldtbl = mpp_paths; | 433 | oldtbl = resize_dereference_mpp_paths(); |
407 | if (mesh_table_grow(mpp_paths, newtbl) < 0) { | 434 | newtbl = mesh_table_alloc(oldtbl->size_order + 1); |
408 | rcu_read_unlock(); | 435 | if (!newtbl) |
436 | goto out; | ||
437 | if (mesh_table_grow(oldtbl, newtbl) < 0) { | ||
409 | __mesh_table_free(newtbl); | 438 | __mesh_table_free(newtbl); |
410 | write_unlock_bh(&pathtbl_resize_lock); | 439 | goto out; |
411 | return; | ||
412 | } | 440 | } |
413 | rcu_read_unlock(); | ||
414 | rcu_assign_pointer(mpp_paths, newtbl); | 441 | rcu_assign_pointer(mpp_paths, newtbl); |
415 | write_unlock_bh(&pathtbl_resize_lock); | 442 | call_rcu(&oldtbl->rcu_head, mesh_table_free_rcu); |
416 | 443 | ||
417 | synchronize_rcu(); | 444 | out: |
418 | mesh_table_free(oldtbl, false); | 445 | write_unlock_bh(&pathtbl_resize_lock); |
419 | } | 446 | } |
420 | 447 | ||
421 | int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) | 448 | int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) |
422 | { | 449 | { |
423 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; | 450 | struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; |
424 | struct ieee80211_local *local = sdata->local; | 451 | struct ieee80211_local *local = sdata->local; |
452 | struct mesh_table *tbl; | ||
425 | struct mesh_path *mpath, *new_mpath; | 453 | struct mesh_path *mpath, *new_mpath; |
426 | struct mpath_node *node, *new_node; | 454 | struct mpath_node *node, *new_node; |
427 | struct hlist_head *bucket; | 455 | struct hlist_head *bucket; |
@@ -456,10 +484,12 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) | |||
456 | new_mpath->exp_time = jiffies; | 484 | new_mpath->exp_time = jiffies; |
457 | spin_lock_init(&new_mpath->state_lock); | 485 | spin_lock_init(&new_mpath->state_lock); |
458 | 486 | ||
459 | hash_idx = mesh_table_hash(dst, sdata, mpp_paths); | 487 | tbl = resize_dereference_mpp_paths(); |
460 | bucket = &mpp_paths->hash_buckets[hash_idx]; | ||
461 | 488 | ||
462 | spin_lock_bh(&mpp_paths->hashwlock[hash_idx]); | 489 | hash_idx = mesh_table_hash(dst, sdata, tbl); |
490 | bucket = &tbl->hash_buckets[hash_idx]; | ||
491 | |||
492 | spin_lock_bh(&tbl->hashwlock[hash_idx]); | ||
463 | 493 | ||
464 | err = -EEXIST; | 494 | err = -EEXIST; |
465 | hlist_for_each_entry(node, n, bucket, list) { | 495 | hlist_for_each_entry(node, n, bucket, list) { |
@@ -469,11 +499,11 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) | |||
469 | } | 499 | } |
470 | 500 | ||
471 | hlist_add_head_rcu(&new_node->list, bucket); | 501 | hlist_add_head_rcu(&new_node->list, bucket); |
472 | if (atomic_inc_return(&mpp_paths->entries) >= | 502 | if (atomic_inc_return(&tbl->entries) >= |
473 | mpp_paths->mean_chain_len * (mpp_paths->hash_mask + 1)) | 503 | tbl->mean_chain_len * (tbl->hash_mask + 1)) |
474 | grow = 1; | 504 | grow = 1; |
475 | 505 | ||
476 | spin_unlock_bh(&mpp_paths->hashwlock[hash_idx]); | 506 | spin_unlock_bh(&tbl->hashwlock[hash_idx]); |
477 | read_unlock_bh(&pathtbl_resize_lock); | 507 | read_unlock_bh(&pathtbl_resize_lock); |
478 | if (grow) { | 508 | if (grow) { |
479 | set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags); | 509 | set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags); |
@@ -482,7 +512,7 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata) | |||
482 | return 0; | 512 | return 0; |
483 | 513 | ||
484 | err_exists: | 514 | err_exists: |
485 | spin_unlock_bh(&mpp_paths->hashwlock[hash_idx]); | 515 | spin_unlock_bh(&tbl->hashwlock[hash_idx]); |
486 | read_unlock_bh(&pathtbl_resize_lock); | 516 | read_unlock_bh(&pathtbl_resize_lock); |
487 | kfree(new_node); | 517 | kfree(new_node); |
488 | err_node_alloc: | 518 | err_node_alloc: |
@@ -502,6 +532,7 @@ err_path_alloc: | |||
502 | */ | 532 | */ |
503 | void mesh_plink_broken(struct sta_info *sta) | 533 | void mesh_plink_broken(struct sta_info *sta) |
504 | { | 534 | { |
535 | struct mesh_table *tbl; | ||
505 | static const u8 bcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; | 536 | static const u8 bcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; |
506 | struct mesh_path *mpath; | 537 | struct mesh_path *mpath; |
507 | struct mpath_node *node; | 538 | struct mpath_node *node; |
@@ -510,10 +541,11 @@ void mesh_plink_broken(struct sta_info *sta) | |||
510 | int i; | 541 | int i; |
511 | 542 | ||
512 | rcu_read_lock(); | 543 | rcu_read_lock(); |
513 | for_each_mesh_entry(mesh_paths, p, node, i) { | 544 | tbl = rcu_dereference(mesh_paths); |
545 | for_each_mesh_entry(tbl, p, node, i) { | ||
514 | mpath = node->mpath; | 546 | mpath = node->mpath; |
515 | spin_lock_bh(&mpath->state_lock); | 547 | spin_lock_bh(&mpath->state_lock); |
516 | if (mpath->next_hop == sta && | 548 | if (rcu_dereference(mpath->next_hop) == sta && |
517 | mpath->flags & MESH_PATH_ACTIVE && | 549 | mpath->flags & MESH_PATH_ACTIVE && |
518 | !(mpath->flags & MESH_PATH_FIXED)) { | 550 | !(mpath->flags & MESH_PATH_FIXED)) { |
519 | mpath->flags &= ~MESH_PATH_ACTIVE; | 551 | mpath->flags &= ~MESH_PATH_ACTIVE; |
@@ -542,30 +574,38 @@ void mesh_plink_broken(struct sta_info *sta) | |||
542 | */ | 574 | */ |
543 | void mesh_path_flush_by_nexthop(struct sta_info *sta) | 575 | void mesh_path_flush_by_nexthop(struct sta_info *sta) |
544 | { | 576 | { |
577 | struct mesh_table *tbl; | ||
545 | struct mesh_path *mpath; | 578 | struct mesh_path *mpath; |
546 | struct mpath_node *node; | 579 | struct mpath_node *node; |
547 | struct hlist_node *p; | 580 | struct hlist_node *p; |
548 | int i; | 581 | int i; |
549 | 582 | ||
550 | for_each_mesh_entry(mesh_paths, p, node, i) { | 583 | rcu_read_lock(); |
584 | tbl = rcu_dereference(mesh_paths); | ||
585 | for_each_mesh_entry(tbl, p, node, i) { | ||
551 | mpath = node->mpath; | 586 | mpath = node->mpath; |
552 | if (mpath->next_hop == sta) | 587 | if (rcu_dereference(mpath->next_hop) == sta) |
553 | mesh_path_del(mpath->dst, mpath->sdata); | 588 | mesh_path_del(mpath->dst, mpath->sdata); |
554 | } | 589 | } |
590 | rcu_read_unlock(); | ||
555 | } | 591 | } |
556 | 592 | ||
557 | void mesh_path_flush(struct ieee80211_sub_if_data *sdata) | 593 | void mesh_path_flush(struct ieee80211_sub_if_data *sdata) |
558 | { | 594 | { |
595 | struct mesh_table *tbl; | ||
559 | struct mesh_path *mpath; | 596 | struct mesh_path *mpath; |
560 | struct mpath_node *node; | 597 | struct mpath_node *node; |
561 | struct hlist_node *p; | 598 | struct hlist_node *p; |
562 | int i; | 599 | int i; |
563 | 600 | ||
564 | for_each_mesh_entry(mesh_paths, p, node, i) { | 601 | rcu_read_lock(); |
602 | tbl = rcu_dereference(mesh_paths); | ||
603 | for_each_mesh_entry(tbl, p, node, i) { | ||
565 | mpath = node->mpath; | 604 | mpath = node->mpath; |
566 | if (mpath->sdata == sdata) | 605 | if (mpath->sdata == sdata) |
567 | mesh_path_del(mpath->dst, mpath->sdata); | 606 | mesh_path_del(mpath->dst, mpath->sdata); |
568 | } | 607 | } |
608 | rcu_read_unlock(); | ||
569 | } | 609 | } |
570 | 610 | ||
571 | static void mesh_path_node_reclaim(struct rcu_head *rp) | 611 | static void mesh_path_node_reclaim(struct rcu_head *rp) |
@@ -589,6 +629,7 @@ static void mesh_path_node_reclaim(struct rcu_head *rp) | |||
589 | */ | 629 | */ |
590 | int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) | 630 | int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) |
591 | { | 631 | { |
632 | struct mesh_table *tbl; | ||
592 | struct mesh_path *mpath; | 633 | struct mesh_path *mpath; |
593 | struct mpath_node *node; | 634 | struct mpath_node *node; |
594 | struct hlist_head *bucket; | 635 | struct hlist_head *bucket; |
@@ -597,19 +638,20 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) | |||
597 | int err = 0; | 638 | int err = 0; |
598 | 639 | ||
599 | read_lock_bh(&pathtbl_resize_lock); | 640 | read_lock_bh(&pathtbl_resize_lock); |
600 | hash_idx = mesh_table_hash(addr, sdata, mesh_paths); | 641 | tbl = resize_dereference_mesh_paths(); |
601 | bucket = &mesh_paths->hash_buckets[hash_idx]; | 642 | hash_idx = mesh_table_hash(addr, sdata, tbl); |
643 | bucket = &tbl->hash_buckets[hash_idx]; | ||
602 | 644 | ||
603 | spin_lock_bh(&mesh_paths->hashwlock[hash_idx]); | 645 | spin_lock_bh(&tbl->hashwlock[hash_idx]); |
604 | hlist_for_each_entry(node, n, bucket, list) { | 646 | hlist_for_each_entry(node, n, bucket, list) { |
605 | mpath = node->mpath; | 647 | mpath = node->mpath; |
606 | if (mpath->sdata == sdata && | 648 | if (mpath->sdata == sdata && |
607 | memcmp(addr, mpath->dst, ETH_ALEN) == 0) { | 649 | memcmp(addr, mpath->dst, ETH_ALEN) == 0) { |
608 | spin_lock_bh(&mpath->state_lock); | 650 | spin_lock_bh(&mpath->state_lock); |
609 | mpath->flags |= MESH_PATH_RESOLVING; | 651 | mpath->flags |= MESH_PATH_RESOLVING; |
610 | hlist_del_rcu(&node->list); | 652 | hlist_del_rcu(&node->list); |
611 | call_rcu(&node->rcu, mesh_path_node_reclaim); | 653 | call_rcu(&node->rcu, mesh_path_node_reclaim); |
612 | atomic_dec(&mesh_paths->entries); | 654 | atomic_dec(&tbl->entries); |
613 | spin_unlock_bh(&mpath->state_lock); | 655 | spin_unlock_bh(&mpath->state_lock); |
614 | goto enddel; | 656 | goto enddel; |
615 | } | 657 | } |
@@ -618,7 +660,7 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata) | |||
618 | err = -ENXIO; | 660 | err = -ENXIO; |
619 | enddel: | 661 | enddel: |
620 | mesh_paths_generation++; | 662 | mesh_paths_generation++; |
621 | spin_unlock_bh(&mesh_paths->hashwlock[hash_idx]); | 663 | spin_unlock_bh(&tbl->hashwlock[hash_idx]); |
622 | read_unlock_bh(&pathtbl_resize_lock); | 664 | read_unlock_bh(&pathtbl_resize_lock); |
623 | return err; | 665 | return err; |
624 | } | 666 | } |
@@ -719,8 +761,10 @@ static void mesh_path_node_free(struct hlist_node *p, bool free_leafs) | |||
719 | struct mpath_node *node = hlist_entry(p, struct mpath_node, list); | 761 | struct mpath_node *node = hlist_entry(p, struct mpath_node, list); |
720 | mpath = node->mpath; | 762 | mpath = node->mpath; |
721 | hlist_del_rcu(p); | 763 | hlist_del_rcu(p); |
722 | if (free_leafs) | 764 | if (free_leafs) { |
765 | del_timer_sync(&mpath->timer); | ||
723 | kfree(mpath); | 766 | kfree(mpath); |
767 | } | ||
724 | kfree(node); | 768 | kfree(node); |
725 | } | 769 | } |
726 | 770 | ||
@@ -745,52 +789,60 @@ static int mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl) | |||
745 | 789 | ||
746 | int mesh_pathtbl_init(void) | 790 | int mesh_pathtbl_init(void) |
747 | { | 791 | { |
748 | mesh_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); | 792 | struct mesh_table *tbl_path, *tbl_mpp; |
749 | if (!mesh_paths) | 793 | |
794 | tbl_path = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); | ||
795 | if (!tbl_path) | ||
750 | return -ENOMEM; | 796 | return -ENOMEM; |
751 | mesh_paths->free_node = &mesh_path_node_free; | 797 | tbl_path->free_node = &mesh_path_node_free; |
752 | mesh_paths->copy_node = &mesh_path_node_copy; | 798 | tbl_path->copy_node = &mesh_path_node_copy; |
753 | mesh_paths->mean_chain_len = MEAN_CHAIN_LEN; | 799 | tbl_path->mean_chain_len = MEAN_CHAIN_LEN; |
754 | 800 | ||
755 | mpp_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); | 801 | tbl_mpp = mesh_table_alloc(INIT_PATHS_SIZE_ORDER); |
756 | if (!mpp_paths) { | 802 | if (!tbl_mpp) { |
757 | mesh_table_free(mesh_paths, true); | 803 | mesh_table_free(tbl_path, true); |
758 | return -ENOMEM; | 804 | return -ENOMEM; |
759 | } | 805 | } |
760 | mpp_paths->free_node = &mesh_path_node_free; | 806 | tbl_mpp->free_node = &mesh_path_node_free; |
761 | mpp_paths->copy_node = &mesh_path_node_copy; | 807 | tbl_mpp->copy_node = &mesh_path_node_copy; |
762 | mpp_paths->mean_chain_len = MEAN_CHAIN_LEN; | 808 | tbl_mpp->mean_chain_len = MEAN_CHAIN_LEN; |
809 | |||
810 | /* Need no locking since this is during init */ | ||
811 | RCU_INIT_POINTER(mesh_paths, tbl_path); | ||
812 | RCU_INIT_POINTER(mpp_paths, tbl_mpp); | ||
763 | 813 | ||
764 | return 0; | 814 | return 0; |
765 | } | 815 | } |
766 | 816 | ||
767 | void mesh_path_expire(struct ieee80211_sub_if_data *sdata) | 817 | void mesh_path_expire(struct ieee80211_sub_if_data *sdata) |
768 | { | 818 | { |
819 | struct mesh_table *tbl; | ||
769 | struct mesh_path *mpath; | 820 | struct mesh_path *mpath; |
770 | struct mpath_node *node; | 821 | struct mpath_node *node; |
771 | struct hlist_node *p; | 822 | struct hlist_node *p; |
772 | int i; | 823 | int i; |
773 | 824 | ||
774 | read_lock_bh(&pathtbl_resize_lock); | 825 | rcu_read_lock(); |
775 | for_each_mesh_entry(mesh_paths, p, node, i) { | 826 | tbl = rcu_dereference(mesh_paths); |
827 | for_each_mesh_entry(tbl, p, node, i) { | ||
776 | if (node->mpath->sdata != sdata) | 828 | if (node->mpath->sdata != sdata) |
777 | continue; | 829 | continue; |
778 | mpath = node->mpath; | 830 | mpath = node->mpath; |
779 | spin_lock_bh(&mpath->state_lock); | 831 | spin_lock_bh(&mpath->state_lock); |
780 | if ((!(mpath->flags & MESH_PATH_RESOLVING)) && | 832 | if ((!(mpath->flags & MESH_PATH_RESOLVING)) && |
781 | (!(mpath->flags & MESH_PATH_FIXED)) && | 833 | (!(mpath->flags & MESH_PATH_FIXED)) && |
782 | time_after(jiffies, | 834 | time_after(jiffies, mpath->exp_time + MESH_PATH_EXPIRE)) { |
783 | mpath->exp_time + MESH_PATH_EXPIRE)) { | ||
784 | spin_unlock_bh(&mpath->state_lock); | 835 | spin_unlock_bh(&mpath->state_lock); |
785 | mesh_path_del(mpath->dst, mpath->sdata); | 836 | mesh_path_del(mpath->dst, mpath->sdata); |
786 | } else | 837 | } else |
787 | spin_unlock_bh(&mpath->state_lock); | 838 | spin_unlock_bh(&mpath->state_lock); |
788 | } | 839 | } |
789 | read_unlock_bh(&pathtbl_resize_lock); | 840 | rcu_read_unlock(); |
790 | } | 841 | } |
791 | 842 | ||
792 | void mesh_pathtbl_unregister(void) | 843 | void mesh_pathtbl_unregister(void) |
793 | { | 844 | { |
794 | mesh_table_free(mesh_paths, true); | 845 | /* no need for locking during exit path */ |
795 | mesh_table_free(mpp_paths, true); | 846 | mesh_table_free(rcu_dereference_raw(mesh_paths), true); |
847 | mesh_table_free(rcu_dereference_raw(mpp_paths), true); | ||
796 | } | 848 | } |
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index d20046b5d8f4..27af6723cb5e 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -719,6 +719,11 @@ void ieee80211_scan_work(struct work_struct *work) | |||
719 | * without scheduling a new work | 719 | * without scheduling a new work |
720 | */ | 720 | */ |
721 | do { | 721 | do { |
722 | if (!ieee80211_sdata_running(sdata)) { | ||
723 | aborted = true; | ||
724 | goto out_complete; | ||
725 | } | ||
726 | |||
722 | switch (local->next_scan_state) { | 727 | switch (local->next_scan_state) { |
723 | case SCAN_DECISION: | 728 | case SCAN_DECISION: |
724 | /* if no more bands/channels left, complete scan */ | 729 | /* if no more bands/channels left, complete scan */ |
diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig index 48464ca13b24..78efe895b663 100644 --- a/net/rfkill/Kconfig +++ b/net/rfkill/Kconfig | |||
@@ -33,3 +33,12 @@ config RFKILL_REGULATOR | |||
33 | 33 | ||
34 | To compile this driver as a module, choose M here: the module will | 34 | To compile this driver as a module, choose M here: the module will |
35 | be called rfkill-regulator. | 35 | be called rfkill-regulator. |
36 | |||
37 | config RFKILL_GPIO | ||
38 | tristate "GPIO RFKILL driver" | ||
39 | depends on RFKILL && GPIOLIB && HAVE_CLK | ||
40 | default n | ||
41 | help | ||
42 | If you say yes here you get support of a generic gpio RFKILL | ||
43 | driver. The platform should fill in the appropriate fields in the | ||
44 | rfkill_gpio_platform_data structure and pass that to the driver. | ||
diff --git a/net/rfkill/Makefile b/net/rfkill/Makefile index d9a5a58ffd8c..311768783f4a 100644 --- a/net/rfkill/Makefile +++ b/net/rfkill/Makefile | |||
@@ -6,3 +6,4 @@ rfkill-y += core.o | |||
6 | rfkill-$(CONFIG_RFKILL_INPUT) += input.o | 6 | rfkill-$(CONFIG_RFKILL_INPUT) += input.o |
7 | obj-$(CONFIG_RFKILL) += rfkill.o | 7 | obj-$(CONFIG_RFKILL) += rfkill.o |
8 | obj-$(CONFIG_RFKILL_REGULATOR) += rfkill-regulator.o | 8 | obj-$(CONFIG_RFKILL_REGULATOR) += rfkill-regulator.o |
9 | obj-$(CONFIG_RFKILL_GPIO) += rfkill-gpio.o | ||
diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c new file mode 100644 index 000000000000..256c5ddd2d72 --- /dev/null +++ b/net/rfkill/rfkill-gpio.c | |||
@@ -0,0 +1,227 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011, NVIDIA Corporation. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along | ||
15 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
17 | */ | ||
18 | |||
19 | #include <linux/gpio.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/rfkill.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/clk.h> | ||
26 | #include <linux/slab.h> | ||
27 | |||
28 | #include <linux/rfkill-gpio.h> | ||
29 | |||
30 | enum rfkill_gpio_clk_state { | ||
31 | UNSPECIFIED = 0, | ||
32 | PWR_ENABLED, | ||
33 | PWR_DISABLED | ||
34 | }; | ||
35 | |||
36 | #define PWR_CLK_SET(_RF, _EN) \ | ||
37 | ((_RF)->pwr_clk_enabled = (!(_EN) ? PWR_ENABLED : PWR_DISABLED)) | ||
38 | #define PWR_CLK_ENABLED(_RF) ((_RF)->pwr_clk_enabled == PWR_ENABLED) | ||
39 | #define PWR_CLK_DISABLED(_RF) ((_RF)->pwr_clk_enabled != PWR_ENABLED) | ||
40 | |||
41 | struct rfkill_gpio_data { | ||
42 | struct rfkill_gpio_platform_data *pdata; | ||
43 | struct rfkill *rfkill_dev; | ||
44 | char *reset_name; | ||
45 | char *shutdown_name; | ||
46 | enum rfkill_gpio_clk_state pwr_clk_enabled; | ||
47 | struct clk *pwr_clk; | ||
48 | }; | ||
49 | |||
50 | static int rfkill_gpio_set_power(void *data, bool blocked) | ||
51 | { | ||
52 | struct rfkill_gpio_data *rfkill = data; | ||
53 | |||
54 | if (blocked) { | ||
55 | if (gpio_is_valid(rfkill->pdata->shutdown_gpio)) | ||
56 | gpio_direction_output(rfkill->pdata->shutdown_gpio, 0); | ||
57 | if (gpio_is_valid(rfkill->pdata->reset_gpio)) | ||
58 | gpio_direction_output(rfkill->pdata->reset_gpio, 0); | ||
59 | if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill)) | ||
60 | clk_disable(rfkill->pwr_clk); | ||
61 | } else { | ||
62 | if (rfkill->pwr_clk && PWR_CLK_DISABLED(rfkill)) | ||
63 | clk_enable(rfkill->pwr_clk); | ||
64 | if (gpio_is_valid(rfkill->pdata->reset_gpio)) | ||
65 | gpio_direction_output(rfkill->pdata->reset_gpio, 1); | ||
66 | if (gpio_is_valid(rfkill->pdata->shutdown_gpio)) | ||
67 | gpio_direction_output(rfkill->pdata->shutdown_gpio, 1); | ||
68 | } | ||
69 | |||
70 | if (rfkill->pwr_clk) | ||
71 | PWR_CLK_SET(rfkill, blocked); | ||
72 | |||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | static const struct rfkill_ops rfkill_gpio_ops = { | ||
77 | .set_block = rfkill_gpio_set_power, | ||
78 | }; | ||
79 | |||
80 | static int rfkill_gpio_probe(struct platform_device *pdev) | ||
81 | { | ||
82 | struct rfkill_gpio_data *rfkill; | ||
83 | struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data; | ||
84 | int ret = 0; | ||
85 | int len = 0; | ||
86 | |||
87 | if (!pdata) { | ||
88 | pr_warn("%s: No platform data specified\n", __func__); | ||
89 | return -EINVAL; | ||
90 | } | ||
91 | |||
92 | /* make sure at-least one of the GPIO is defined and that | ||
93 | * a name is specified for this instance */ | ||
94 | if (!pdata->name || (!gpio_is_valid(pdata->reset_gpio) && | ||
95 | !gpio_is_valid(pdata->shutdown_gpio))) { | ||
96 | pr_warn("%s: invalid platform data\n", __func__); | ||
97 | return -EINVAL; | ||
98 | } | ||
99 | |||
100 | rfkill = kzalloc(sizeof(*rfkill), GFP_KERNEL); | ||
101 | if (!rfkill) | ||
102 | return -ENOMEM; | ||
103 | |||
104 | rfkill->pdata = pdata; | ||
105 | |||
106 | len = strlen(pdata->name); | ||
107 | rfkill->reset_name = kzalloc(len + 7, GFP_KERNEL); | ||
108 | if (!rfkill->reset_name) { | ||
109 | ret = -ENOMEM; | ||
110 | goto fail_alloc; | ||
111 | } | ||
112 | |||
113 | rfkill->shutdown_name = kzalloc(len + 10, GFP_KERNEL); | ||
114 | if (!rfkill->shutdown_name) { | ||
115 | ret = -ENOMEM; | ||
116 | goto fail_reset_name; | ||
117 | } | ||
118 | |||
119 | snprintf(rfkill->reset_name, len + 6 , "%s_reset", pdata->name); | ||
120 | snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", pdata->name); | ||
121 | |||
122 | if (pdata->power_clk_name) { | ||
123 | rfkill->pwr_clk = clk_get(&pdev->dev, pdata->power_clk_name); | ||
124 | if (IS_ERR(rfkill->pwr_clk)) { | ||
125 | pr_warn("%s: can't find pwr_clk.\n", __func__); | ||
126 | goto fail_shutdown_name; | ||
127 | } | ||
128 | } | ||
129 | |||
130 | if (gpio_is_valid(pdata->reset_gpio)) { | ||
131 | ret = gpio_request(pdata->reset_gpio, rfkill->reset_name); | ||
132 | if (ret) { | ||
133 | pr_warn("%s: failed to get reset gpio.\n", __func__); | ||
134 | goto fail_clock; | ||
135 | } | ||
136 | } | ||
137 | |||
138 | if (gpio_is_valid(pdata->shutdown_gpio)) { | ||
139 | ret = gpio_request(pdata->shutdown_gpio, rfkill->shutdown_name); | ||
140 | if (ret) { | ||
141 | pr_warn("%s: failed to get shutdown gpio.\n", __func__); | ||
142 | goto fail_reset; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | rfkill->rfkill_dev = rfkill_alloc(pdata->name, &pdev->dev, pdata->type, | ||
147 | &rfkill_gpio_ops, rfkill); | ||
148 | if (!rfkill->rfkill_dev) | ||
149 | goto fail_shutdown; | ||
150 | |||
151 | ret = rfkill_register(rfkill->rfkill_dev); | ||
152 | if (ret < 0) | ||
153 | goto fail_rfkill; | ||
154 | |||
155 | platform_set_drvdata(pdev, rfkill); | ||
156 | |||
157 | dev_info(&pdev->dev, "%s device registered.\n", pdata->name); | ||
158 | |||
159 | return 0; | ||
160 | |||
161 | fail_rfkill: | ||
162 | rfkill_destroy(rfkill->rfkill_dev); | ||
163 | fail_shutdown: | ||
164 | if (gpio_is_valid(pdata->shutdown_gpio)) | ||
165 | gpio_free(pdata->shutdown_gpio); | ||
166 | fail_reset: | ||
167 | if (gpio_is_valid(pdata->reset_gpio)) | ||
168 | gpio_free(pdata->reset_gpio); | ||
169 | fail_clock: | ||
170 | if (rfkill->pwr_clk) | ||
171 | clk_put(rfkill->pwr_clk); | ||
172 | fail_shutdown_name: | ||
173 | kfree(rfkill->shutdown_name); | ||
174 | fail_reset_name: | ||
175 | kfree(rfkill->reset_name); | ||
176 | fail_alloc: | ||
177 | kfree(rfkill); | ||
178 | |||
179 | return ret; | ||
180 | } | ||
181 | |||
182 | static int rfkill_gpio_remove(struct platform_device *pdev) | ||
183 | { | ||
184 | struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev); | ||
185 | |||
186 | rfkill_unregister(rfkill->rfkill_dev); | ||
187 | rfkill_destroy(rfkill->rfkill_dev); | ||
188 | if (gpio_is_valid(rfkill->pdata->shutdown_gpio)) | ||
189 | gpio_free(rfkill->pdata->shutdown_gpio); | ||
190 | if (gpio_is_valid(rfkill->pdata->reset_gpio)) | ||
191 | gpio_free(rfkill->pdata->reset_gpio); | ||
192 | if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill)) | ||
193 | clk_disable(rfkill->pwr_clk); | ||
194 | if (rfkill->pwr_clk) | ||
195 | clk_put(rfkill->pwr_clk); | ||
196 | kfree(rfkill->shutdown_name); | ||
197 | kfree(rfkill->reset_name); | ||
198 | kfree(rfkill); | ||
199 | |||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | static struct platform_driver rfkill_gpio_driver = { | ||
204 | .probe = rfkill_gpio_probe, | ||
205 | .remove = __devexit_p(rfkill_gpio_remove), | ||
206 | .driver = { | ||
207 | .name = "rfkill_gpio", | ||
208 | .owner = THIS_MODULE, | ||
209 | }, | ||
210 | }; | ||
211 | |||
212 | static int __init rfkill_gpio_init(void) | ||
213 | { | ||
214 | return platform_driver_register(&rfkill_gpio_driver); | ||
215 | } | ||
216 | |||
217 | static void __exit rfkill_gpio_exit(void) | ||
218 | { | ||
219 | platform_driver_unregister(&rfkill_gpio_driver); | ||
220 | } | ||
221 | |||
222 | module_init(rfkill_gpio_init); | ||
223 | module_exit(rfkill_gpio_exit); | ||
224 | |||
225 | MODULE_DESCRIPTION("gpio rfkill"); | ||
226 | MODULE_AUTHOR("NVIDIA"); | ||
227 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/wireless/core.h b/net/wireless/core.h index bf0fb40e3c8b..3dce1f167eba 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -245,6 +245,7 @@ struct cfg80211_event { | |||
245 | u16 status; | 245 | u16 status; |
246 | } cr; | 246 | } cr; |
247 | struct { | 247 | struct { |
248 | struct ieee80211_channel *channel; | ||
248 | u8 bssid[ETH_ALEN]; | 249 | u8 bssid[ETH_ALEN]; |
249 | const u8 *req_ie; | 250 | const u8 *req_ie; |
250 | const u8 *resp_ie; | 251 | const u8 *resp_ie; |
@@ -392,7 +393,9 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, | |||
392 | int cfg80211_disconnect(struct cfg80211_registered_device *rdev, | 393 | int cfg80211_disconnect(struct cfg80211_registered_device *rdev, |
393 | struct net_device *dev, u16 reason, | 394 | struct net_device *dev, u16 reason, |
394 | bool wextev); | 395 | bool wextev); |
395 | void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, | 396 | void __cfg80211_roamed(struct wireless_dev *wdev, |
397 | struct ieee80211_channel *channel, | ||
398 | const u8 *bssid, | ||
396 | const u8 *req_ie, size_t req_ie_len, | 399 | const u8 *req_ie, size_t req_ie_len, |
397 | const u8 *resp_ie, size_t resp_ie_len); | 400 | const u8 *resp_ie, size_t resp_ie_len); |
398 | int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, | 401 | int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 2222ce08ee91..ec83f413a7ed 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -3294,8 +3294,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) | |||
3294 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 3294 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
3295 | struct net_device *dev = info->user_ptr[1]; | 3295 | struct net_device *dev = info->user_ptr[1]; |
3296 | struct cfg80211_scan_request *request; | 3296 | struct cfg80211_scan_request *request; |
3297 | struct cfg80211_ssid *ssid; | ||
3298 | struct ieee80211_channel *channel; | ||
3299 | struct nlattr *attr; | 3297 | struct nlattr *attr; |
3300 | struct wiphy *wiphy; | 3298 | struct wiphy *wiphy; |
3301 | int err, tmp, n_ssids = 0, n_channels, i; | 3299 | int err, tmp, n_ssids = 0, n_channels, i; |
@@ -3342,8 +3340,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) | |||
3342 | return -EINVAL; | 3340 | return -EINVAL; |
3343 | 3341 | ||
3344 | request = kzalloc(sizeof(*request) | 3342 | request = kzalloc(sizeof(*request) |
3345 | + sizeof(*ssid) * n_ssids | 3343 | + sizeof(*request->ssids) * n_ssids |
3346 | + sizeof(channel) * n_channels | 3344 | + sizeof(*request->channels) * n_channels |
3347 | + ie_len, GFP_KERNEL); | 3345 | + ie_len, GFP_KERNEL); |
3348 | if (!request) | 3346 | if (!request) |
3349 | return -ENOMEM; | 3347 | return -ENOMEM; |
@@ -3449,8 +3447,6 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, | |||
3449 | struct cfg80211_sched_scan_request *request; | 3447 | struct cfg80211_sched_scan_request *request; |
3450 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 3448 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
3451 | struct net_device *dev = info->user_ptr[1]; | 3449 | struct net_device *dev = info->user_ptr[1]; |
3452 | struct cfg80211_ssid *ssid; | ||
3453 | struct ieee80211_channel *channel; | ||
3454 | struct nlattr *attr; | 3450 | struct nlattr *attr; |
3455 | struct wiphy *wiphy; | 3451 | struct wiphy *wiphy; |
3456 | int err, tmp, n_ssids = 0, n_channels, i; | 3452 | int err, tmp, n_ssids = 0, n_channels, i; |
@@ -3507,8 +3503,8 @@ static int nl80211_start_sched_scan(struct sk_buff *skb, | |||
3507 | return -EINVAL; | 3503 | return -EINVAL; |
3508 | 3504 | ||
3509 | request = kzalloc(sizeof(*request) | 3505 | request = kzalloc(sizeof(*request) |
3510 | + sizeof(*ssid) * n_ssids | 3506 | + sizeof(*request->ssids) * n_ssids |
3511 | + sizeof(channel) * n_channels | 3507 | + sizeof(*request->channels) * n_channels |
3512 | + ie_len, GFP_KERNEL); | 3508 | + ie_len, GFP_KERNEL); |
3513 | if (!request) | 3509 | if (!request) |
3514 | return -ENOMEM; | 3510 | return -ENOMEM; |
diff --git a/net/wireless/sme.c b/net/wireless/sme.c index e17b0bee6bdc..b7b6ff8be553 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c | |||
@@ -250,7 +250,8 @@ static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev) | |||
250 | if (wdev->conn->params.privacy) | 250 | if (wdev->conn->params.privacy) |
251 | capa |= WLAN_CAPABILITY_PRIVACY; | 251 | capa |= WLAN_CAPABILITY_PRIVACY; |
252 | 252 | ||
253 | bss = cfg80211_get_bss(wdev->wiphy, NULL, wdev->conn->params.bssid, | 253 | bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel, |
254 | wdev->conn->params.bssid, | ||
254 | wdev->conn->params.ssid, | 255 | wdev->conn->params.ssid, |
255 | wdev->conn->params.ssid_len, | 256 | wdev->conn->params.ssid_len, |
256 | WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, | 257 | WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, |
@@ -470,7 +471,10 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
470 | } | 471 | } |
471 | 472 | ||
472 | if (!bss) | 473 | if (!bss) |
473 | bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, | 474 | bss = cfg80211_get_bss(wdev->wiphy, |
475 | wdev->conn ? wdev->conn->params.channel : | ||
476 | NULL, | ||
477 | bssid, | ||
474 | wdev->ssid, wdev->ssid_len, | 478 | wdev->ssid, wdev->ssid_len, |
475 | WLAN_CAPABILITY_ESS, | 479 | WLAN_CAPABILITY_ESS, |
476 | WLAN_CAPABILITY_ESS); | 480 | WLAN_CAPABILITY_ESS); |
@@ -538,7 +542,9 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
538 | } | 542 | } |
539 | EXPORT_SYMBOL(cfg80211_connect_result); | 543 | EXPORT_SYMBOL(cfg80211_connect_result); |
540 | 544 | ||
541 | void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, | 545 | void __cfg80211_roamed(struct wireless_dev *wdev, |
546 | struct ieee80211_channel *channel, | ||
547 | const u8 *bssid, | ||
542 | const u8 *req_ie, size_t req_ie_len, | 548 | const u8 *req_ie, size_t req_ie_len, |
543 | const u8 *resp_ie, size_t resp_ie_len) | 549 | const u8 *resp_ie, size_t resp_ie_len) |
544 | { | 550 | { |
@@ -565,7 +571,7 @@ void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, | |||
565 | cfg80211_put_bss(&wdev->current_bss->pub); | 571 | cfg80211_put_bss(&wdev->current_bss->pub); |
566 | wdev->current_bss = NULL; | 572 | wdev->current_bss = NULL; |
567 | 573 | ||
568 | bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, | 574 | bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, |
569 | wdev->ssid, wdev->ssid_len, | 575 | wdev->ssid, wdev->ssid_len, |
570 | WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); | 576 | WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); |
571 | 577 | ||
@@ -603,7 +609,9 @@ void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, | |||
603 | #endif | 609 | #endif |
604 | } | 610 | } |
605 | 611 | ||
606 | void cfg80211_roamed(struct net_device *dev, const u8 *bssid, | 612 | void cfg80211_roamed(struct net_device *dev, |
613 | struct ieee80211_channel *channel, | ||
614 | const u8 *bssid, | ||
607 | const u8 *req_ie, size_t req_ie_len, | 615 | const u8 *req_ie, size_t req_ie_len, |
608 | const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) | 616 | const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) |
609 | { | 617 | { |
@@ -619,6 +627,7 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid, | |||
619 | return; | 627 | return; |
620 | 628 | ||
621 | ev->type = EVENT_ROAMED; | 629 | ev->type = EVENT_ROAMED; |
630 | ev->rm.channel = channel; | ||
622 | memcpy(ev->rm.bssid, bssid, ETH_ALEN); | 631 | memcpy(ev->rm.bssid, bssid, ETH_ALEN); |
623 | ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); | 632 | ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); |
624 | ev->rm.req_ie_len = req_ie_len; | 633 | ev->rm.req_ie_len = req_ie_len; |
diff --git a/net/wireless/util.c b/net/wireless/util.c index f0536d44d43c..4d7b83fbc32f 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -746,7 +746,7 @@ static void cfg80211_process_wdev_events(struct wireless_dev *wdev) | |||
746 | NULL); | 746 | NULL); |
747 | break; | 747 | break; |
748 | case EVENT_ROAMED: | 748 | case EVENT_ROAMED: |
749 | __cfg80211_roamed(wdev, ev->rm.bssid, | 749 | __cfg80211_roamed(wdev, ev->rm.channel, ev->rm.bssid, |
750 | ev->rm.req_ie, ev->rm.req_ie_len, | 750 | ev->rm.req_ie, ev->rm.req_ie_len, |
751 | ev->rm.resp_ie, ev->rm.resp_ie_len); | 751 | ev->rm.resp_ie, ev->rm.resp_ie_len); |
752 | break; | 752 | break; |