aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2010-05-18 00:09:11 -0400
committerDavid S. Miller <davem@davemloft.net>2010-05-18 00:09:11 -0400
commit820ae8a80eb59962aefbbd4908dfe144ec0f9edb (patch)
tree6c0f7356afff14e1c7d266de644810cd2de7caa9 /net
parent380fefb2ddabd4cd5f14dbe090481f0544e65078 (diff)
parent6fe70aae0d128339febfabc073ba4c4a03de4f45 (diff)
Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/driver-ops.h11
-rw-r--r--net/mac80211/driver-trace.h49
-rw-r--r--net/mac80211/ieee80211_i.h3
-rw-r--r--net/mac80211/mlme.c59
-rw-r--r--net/mac80211/rx.c6
-rw-r--r--net/mac80211/work.c27
6 files changed, 147 insertions, 8 deletions
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index ee8b63f92f71..4f2271316650 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -371,4 +371,15 @@ static inline void drv_flush(struct ieee80211_local *local, bool drop)
371 if (local->ops->flush) 371 if (local->ops->flush)
372 local->ops->flush(&local->hw, drop); 372 local->ops->flush(&local->hw, drop);
373} 373}
374
375static inline void drv_channel_switch(struct ieee80211_local *local,
376 struct ieee80211_channel_switch *ch_switch)
377{
378 might_sleep();
379
380 local->ops->channel_switch(&local->hw, ch_switch);
381
382 trace_drv_channel_switch(local, ch_switch);
383}
384
374#endif /* __MAC80211_DRIVER_OPS */ 385#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index ce734b58d07a..6a9b2342a9c2 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -774,6 +774,34 @@ TRACE_EVENT(drv_flush,
774 ) 774 )
775); 775);
776 776
777TRACE_EVENT(drv_channel_switch,
778 TP_PROTO(struct ieee80211_local *local,
779 struct ieee80211_channel_switch *ch_switch),
780
781 TP_ARGS(local, ch_switch),
782
783 TP_STRUCT__entry(
784 LOCAL_ENTRY
785 __field(u64, timestamp)
786 __field(bool, block_tx)
787 __field(u16, freq)
788 __field(u8, count)
789 ),
790
791 TP_fast_assign(
792 LOCAL_ASSIGN;
793 __entry->timestamp = ch_switch->timestamp;
794 __entry->block_tx = ch_switch->block_tx;
795 __entry->freq = ch_switch->channel->center_freq;
796 __entry->count = ch_switch->count;
797 ),
798
799 TP_printk(
800 LOCAL_PR_FMT " new freq:%u count:%d",
801 LOCAL_PR_ARG, __entry->freq, __entry->count
802 )
803);
804
777/* 805/*
778 * Tracing for API calls that drivers call. 806 * Tracing for API calls that drivers call.
779 */ 807 */
@@ -992,6 +1020,27 @@ TRACE_EVENT(api_sta_block_awake,
992 ) 1020 )
993); 1021);
994 1022
1023TRACE_EVENT(api_chswitch_done,
1024 TP_PROTO(struct ieee80211_sub_if_data *sdata, bool success),
1025
1026 TP_ARGS(sdata, success),
1027
1028 TP_STRUCT__entry(
1029 VIF_ENTRY
1030 __field(bool, success)
1031 ),
1032
1033 TP_fast_assign(
1034 VIF_ASSIGN;
1035 __entry->success = success;
1036 ),
1037
1038 TP_printk(
1039 VIF_PR_FMT " success=%d",
1040 VIF_PR_ARG, __entry->success
1041 )
1042);
1043
995/* 1044/*
996 * Tracing for internal functions 1045 * Tracing for internal functions
997 * (which may also be called in response to driver calls) 1046 * (which may also be called in response to driver calls)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 7ef7798d04cd..1a9e2da37a93 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -998,7 +998,8 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
998 unsigned long data, void *dummy); 998 unsigned long data, void *dummy);
999void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, 999void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
1000 struct ieee80211_channel_sw_ie *sw_elem, 1000 struct ieee80211_channel_sw_ie *sw_elem,
1001 struct ieee80211_bss *bss); 1001 struct ieee80211_bss *bss,
1002 u64 timestamp);
1002void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata); 1003void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata);
1003void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata); 1004void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
1004 1005
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 3093e46273c3..c6c1f49cc456 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -342,7 +342,11 @@ static void ieee80211_chswitch_work(struct work_struct *work)
342 goto out; 342 goto out;
343 343
344 sdata->local->oper_channel = sdata->local->csa_channel; 344 sdata->local->oper_channel = sdata->local->csa_channel;
345 ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL); 345 if (!sdata->local->ops->channel_switch) {
346 /* call "hw_config" only if doing sw channel switch */
347 ieee80211_hw_config(sdata->local,
348 IEEE80211_CONF_CHANGE_CHANNEL);
349 }
346 350
347 /* XXX: shouldn't really modify cfg80211-owned data! */ 351 /* XXX: shouldn't really modify cfg80211-owned data! */
348 ifmgd->associated->channel = sdata->local->oper_channel; 352 ifmgd->associated->channel = sdata->local->oper_channel;
@@ -354,6 +358,29 @@ static void ieee80211_chswitch_work(struct work_struct *work)
354 mutex_unlock(&ifmgd->mtx); 358 mutex_unlock(&ifmgd->mtx);
355} 359}
356 360
361void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
362{
363 struct ieee80211_sub_if_data *sdata;
364 struct ieee80211_if_managed *ifmgd;
365
366 sdata = vif_to_sdata(vif);
367 ifmgd = &sdata->u.mgd;
368
369 trace_api_chswitch_done(sdata, success);
370 if (!success) {
371 /*
372 * If the channel switch was not successful, stay
373 * around on the old channel. We currently lack
374 * good handling of this situation, possibly we
375 * should just drop the association.
376 */
377 sdata->local->csa_channel = sdata->local->oper_channel;
378 }
379
380 ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
381}
382EXPORT_SYMBOL(ieee80211_chswitch_done);
383
357static void ieee80211_chswitch_timer(unsigned long data) 384static void ieee80211_chswitch_timer(unsigned long data)
358{ 385{
359 struct ieee80211_sub_if_data *sdata = 386 struct ieee80211_sub_if_data *sdata =
@@ -370,7 +397,8 @@ static void ieee80211_chswitch_timer(unsigned long data)
370 397
371void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, 398void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
372 struct ieee80211_channel_sw_ie *sw_elem, 399 struct ieee80211_channel_sw_ie *sw_elem,
373 struct ieee80211_bss *bss) 400 struct ieee80211_bss *bss,
401 u64 timestamp)
374{ 402{
375 struct cfg80211_bss *cbss = 403 struct cfg80211_bss *cbss =
376 container_of((void *)bss, struct cfg80211_bss, priv); 404 container_of((void *)bss, struct cfg80211_bss, priv);
@@ -398,10 +426,29 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
398 426
399 sdata->local->csa_channel = new_ch; 427 sdata->local->csa_channel = new_ch;
400 428
429 if (sdata->local->ops->channel_switch) {
430 /* use driver's channel switch callback */
431 struct ieee80211_channel_switch ch_switch;
432 memset(&ch_switch, 0, sizeof(ch_switch));
433 ch_switch.timestamp = timestamp;
434 if (sw_elem->mode) {
435 ch_switch.block_tx = true;
436 ieee80211_stop_queues_by_reason(&sdata->local->hw,
437 IEEE80211_QUEUE_STOP_REASON_CSA);
438 }
439 ch_switch.channel = new_ch;
440 ch_switch.count = sw_elem->count;
441 ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
442 drv_channel_switch(sdata->local, &ch_switch);
443 return;
444 }
445
446 /* channel switch handled in software */
401 if (sw_elem->count <= 1) { 447 if (sw_elem->count <= 1) {
402 ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); 448 ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
403 } else { 449 } else {
404 ieee80211_stop_queues_by_reason(&sdata->local->hw, 450 if (sw_elem->mode)
451 ieee80211_stop_queues_by_reason(&sdata->local->hw,
405 IEEE80211_QUEUE_STOP_REASON_CSA); 452 IEEE80211_QUEUE_STOP_REASON_CSA);
406 ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; 453 ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
407 mod_timer(&ifmgd->chswitch_timer, 454 mod_timer(&ifmgd->chswitch_timer,
@@ -1317,7 +1364,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
1317 ETH_ALEN) == 0)) { 1364 ETH_ALEN) == 0)) {
1318 struct ieee80211_channel_sw_ie *sw_elem = 1365 struct ieee80211_channel_sw_ie *sw_elem =
1319 (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem; 1366 (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem;
1320 ieee80211_sta_process_chanswitch(sdata, sw_elem, bss); 1367 ieee80211_sta_process_chanswitch(sdata, sw_elem,
1368 bss, rx_status->mactime);
1321 } 1369 }
1322} 1370}
1323 1371
@@ -1649,7 +1697,8 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
1649 1697
1650 ieee80211_sta_process_chanswitch(sdata, 1698 ieee80211_sta_process_chanswitch(sdata,
1651 &mgmt->u.action.u.chan_switch.sw_elem, 1699 &mgmt->u.action.u.chan_switch.sw_elem,
1652 (void *)ifmgd->associated->priv); 1700 (void *)ifmgd->associated->priv,
1701 rx_status->mactime);
1653 break; 1702 break;
1654 } 1703 }
1655 mutex_unlock(&ifmgd->mtx); 1704 mutex_unlock(&ifmgd->mtx);
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 9a08f2c446c6..6e2a7bcd8cb8 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1253,6 +1253,12 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
1253 if (skb_linearize(rx->skb)) 1253 if (skb_linearize(rx->skb))
1254 return RX_DROP_UNUSABLE; 1254 return RX_DROP_UNUSABLE;
1255 1255
1256 /*
1257 * skb_linearize() might change the skb->data and
1258 * previously cached variables (in this case, hdr) need to
1259 * be refreshed with the new data.
1260 */
1261 hdr = (struct ieee80211_hdr *)rx->skb->data;
1256 seq = (sc & IEEE80211_SCTL_SEQ) >> 4; 1262 seq = (sc & IEEE80211_SCTL_SEQ) >> 4;
1257 1263
1258 if (frag == 0) { 1264 if (frag == 0) {
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index 3dd07600199d..be3d4a698692 100644
--- a/net/mac80211/work.c
+++ b/net/mac80211/work.c
@@ -33,6 +33,7 @@
33#define IEEE80211_MAX_PROBE_TRIES 5 33#define IEEE80211_MAX_PROBE_TRIES 5
34 34
35enum work_action { 35enum work_action {
36 WORK_ACT_MISMATCH,
36 WORK_ACT_NONE, 37 WORK_ACT_NONE,
37 WORK_ACT_TIMEOUT, 38 WORK_ACT_TIMEOUT,
38 WORK_ACT_DONE, 39 WORK_ACT_DONE,
@@ -585,7 +586,7 @@ ieee80211_rx_mgmt_auth(struct ieee80211_work *wk,
585 u16 auth_alg, auth_transaction, status_code; 586 u16 auth_alg, auth_transaction, status_code;
586 587
587 if (wk->type != IEEE80211_WORK_AUTH) 588 if (wk->type != IEEE80211_WORK_AUTH)
588 return WORK_ACT_NONE; 589 return WORK_ACT_MISMATCH;
589 590
590 if (len < 24 + 6) 591 if (len < 24 + 6)
591 return WORK_ACT_NONE; 592 return WORK_ACT_NONE;
@@ -636,6 +637,9 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_work *wk,
636 struct ieee802_11_elems elems; 637 struct ieee802_11_elems elems;
637 u8 *pos; 638 u8 *pos;
638 639
640 if (wk->type != IEEE80211_WORK_ASSOC)
641 return WORK_ACT_MISMATCH;
642
639 /* 643 /*
640 * AssocResp and ReassocResp have identical structure, so process both 644 * AssocResp and ReassocResp have identical structure, so process both
641 * of them in this function. 645 * of them in this function.
@@ -691,6 +695,12 @@ ieee80211_rx_mgmt_probe_resp(struct ieee80211_work *wk,
691 695
692 ASSERT_WORK_MTX(local); 696 ASSERT_WORK_MTX(local);
693 697
698 if (wk->type != IEEE80211_WORK_DIRECT_PROBE)
699 return WORK_ACT_MISMATCH;
700
701 if (len < 24 + 12)
702 return WORK_ACT_NONE;
703
694 baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt; 704 baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
695 if (baselen > len) 705 if (baselen > len)
696 return WORK_ACT_NONE; 706 return WORK_ACT_NONE;
@@ -705,7 +715,7 @@ static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local,
705 struct ieee80211_rx_status *rx_status; 715 struct ieee80211_rx_status *rx_status;
706 struct ieee80211_mgmt *mgmt; 716 struct ieee80211_mgmt *mgmt;
707 struct ieee80211_work *wk; 717 struct ieee80211_work *wk;
708 enum work_action rma = WORK_ACT_NONE; 718 enum work_action rma;
709 u16 fc; 719 u16 fc;
710 720
711 rx_status = (struct ieee80211_rx_status *) skb->cb; 721 rx_status = (struct ieee80211_rx_status *) skb->cb;
@@ -752,7 +762,17 @@ static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local,
752 break; 762 break;
753 default: 763 default:
754 WARN_ON(1); 764 WARN_ON(1);
765 rma = WORK_ACT_NONE;
755 } 766 }
767
768 /*
769 * We've either received an unexpected frame, or we have
770 * multiple work items and need to match the frame to the
771 * right one.
772 */
773 if (rma == WORK_ACT_MISMATCH)
774 continue;
775
756 /* 776 /*
757 * We've processed this frame for that work, so it can't 777 * We've processed this frame for that work, so it can't
758 * belong to another work struct. 778 * belong to another work struct.
@@ -762,6 +782,9 @@ static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local,
762 } 782 }
763 783
764 switch (rma) { 784 switch (rma) {
785 case WORK_ACT_MISMATCH:
786 /* ignore this unmatched frame */
787 break;
765 case WORK_ACT_NONE: 788 case WORK_ACT_NONE:
766 break; 789 break;
767 case WORK_ACT_DONE: 790 case WORK_ACT_DONE: