diff options
Diffstat (limited to 'drivers/net/wireless/mac80211_hwsim.c')
-rw-r--r-- | drivers/net/wireless/mac80211_hwsim.c | 177 |
1 files changed, 130 insertions, 47 deletions
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index ff9085502bea..b73e497fe770 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c | |||
@@ -48,6 +48,10 @@ static int channels = 1; | |||
48 | module_param(channels, int, 0444); | 48 | module_param(channels, int, 0444); |
49 | MODULE_PARM_DESC(channels, "Number of concurrent channels"); | 49 | MODULE_PARM_DESC(channels, "Number of concurrent channels"); |
50 | 50 | ||
51 | static bool paged_rx = false; | ||
52 | module_param(paged_rx, bool, 0644); | ||
53 | MODULE_PARM_DESC(paged_rx, "Use paged SKBs for RX instead of linear ones"); | ||
54 | |||
51 | /** | 55 | /** |
52 | * enum hwsim_regtest - the type of regulatory tests we offer | 56 | * enum hwsim_regtest - the type of regulatory tests we offer |
53 | * | 57 | * |
@@ -333,11 +337,11 @@ struct mac80211_hwsim_data { | |||
333 | int scan_chan_idx; | 337 | int scan_chan_idx; |
334 | 338 | ||
335 | struct ieee80211_channel *channel; | 339 | struct ieee80211_channel *channel; |
336 | unsigned long beacon_int; /* in jiffies unit */ | 340 | u64 beacon_int /* beacon interval in us */; |
337 | unsigned int rx_filter; | 341 | unsigned int rx_filter; |
338 | bool started, idle, scanning; | 342 | bool started, idle, scanning; |
339 | struct mutex mutex; | 343 | struct mutex mutex; |
340 | struct timer_list beacon_timer; | 344 | struct tasklet_hrtimer beacon_timer; |
341 | enum ps_mode { | 345 | enum ps_mode { |
342 | PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL | 346 | PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL |
343 | } ps; | 347 | } ps; |
@@ -357,7 +361,10 @@ struct mac80211_hwsim_data { | |||
357 | int power_level; | 361 | int power_level; |
358 | 362 | ||
359 | /* difference between this hw's clock and the real clock, in usecs */ | 363 | /* difference between this hw's clock and the real clock, in usecs */ |
360 | u64 tsf_offset; | 364 | s64 tsf_offset; |
365 | s64 bcn_delta; | ||
366 | /* absolute beacon transmission time. Used to cover up "tx" delay. */ | ||
367 | u64 abs_bcn_ts; | ||
361 | }; | 368 | }; |
362 | 369 | ||
363 | 370 | ||
@@ -405,15 +412,19 @@ static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb, | |||
405 | return NETDEV_TX_OK; | 412 | return NETDEV_TX_OK; |
406 | } | 413 | } |
407 | 414 | ||
415 | static inline u64 mac80211_hwsim_get_tsf_raw(void) | ||
416 | { | ||
417 | return ktime_to_us(ktime_get_real()); | ||
418 | } | ||
419 | |||
408 | static __le64 __mac80211_hwsim_get_tsf(struct mac80211_hwsim_data *data) | 420 | static __le64 __mac80211_hwsim_get_tsf(struct mac80211_hwsim_data *data) |
409 | { | 421 | { |
410 | struct timeval tv = ktime_to_timeval(ktime_get_real()); | 422 | u64 now = mac80211_hwsim_get_tsf_raw(); |
411 | u64 now = tv.tv_sec * USEC_PER_SEC + tv.tv_usec; | ||
412 | return cpu_to_le64(now + data->tsf_offset); | 423 | return cpu_to_le64(now + data->tsf_offset); |
413 | } | 424 | } |
414 | 425 | ||
415 | static u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw, | 426 | static u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw, |
416 | struct ieee80211_vif *vif) | 427 | struct ieee80211_vif *vif) |
417 | { | 428 | { |
418 | struct mac80211_hwsim_data *data = hw->priv; | 429 | struct mac80211_hwsim_data *data = hw->priv; |
419 | return le64_to_cpu(__mac80211_hwsim_get_tsf(data)); | 430 | return le64_to_cpu(__mac80211_hwsim_get_tsf(data)); |
@@ -423,9 +434,13 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw, | |||
423 | struct ieee80211_vif *vif, u64 tsf) | 434 | struct ieee80211_vif *vif, u64 tsf) |
424 | { | 435 | { |
425 | struct mac80211_hwsim_data *data = hw->priv; | 436 | struct mac80211_hwsim_data *data = hw->priv; |
426 | struct timeval tv = ktime_to_timeval(ktime_get_real()); | 437 | u64 now = mac80211_hwsim_get_tsf(hw, vif); |
427 | u64 now = tv.tv_sec * USEC_PER_SEC + tv.tv_usec; | 438 | u32 bcn_int = data->beacon_int; |
428 | data->tsf_offset = tsf - now; | 439 | s64 delta = tsf - now; |
440 | |||
441 | data->tsf_offset += delta; | ||
442 | /* adjust after beaconing with new timestamp at old TBTT */ | ||
443 | data->bcn_delta = do_div(delta, bcn_int); | ||
429 | } | 444 | } |
430 | 445 | ||
431 | static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, | 446 | static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, |
@@ -696,7 +711,7 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, | |||
696 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | 711 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; |
697 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | 712 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); |
698 | struct ieee80211_rx_status rx_status; | 713 | struct ieee80211_rx_status rx_status; |
699 | struct ieee80211_rate *txrate = ieee80211_get_tx_rate(hw, info); | 714 | u64 now; |
700 | 715 | ||
701 | memset(&rx_status, 0, sizeof(rx_status)); | 716 | memset(&rx_status, 0, sizeof(rx_status)); |
702 | rx_status.flag |= RX_FLAG_MACTIME_START; | 717 | rx_status.flag |= RX_FLAG_MACTIME_START; |
@@ -722,11 +737,23 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, | |||
722 | secpath_reset(skb); | 737 | secpath_reset(skb); |
723 | nf_reset(skb); | 738 | nf_reset(skb); |
724 | 739 | ||
740 | /* | ||
741 | * Get absolute mactime here so all HWs RX at the "same time", and | ||
742 | * absolute TX time for beacon mactime so the timestamp matches. | ||
743 | * Giving beacons a different mactime than non-beacons looks messy, but | ||
744 | * it helps the Toffset be exact and a ~10us mactime discrepancy | ||
745 | * probably doesn't really matter. | ||
746 | */ | ||
747 | if (ieee80211_is_beacon(hdr->frame_control) || | ||
748 | ieee80211_is_probe_resp(hdr->frame_control)) | ||
749 | now = data->abs_bcn_ts; | ||
750 | else | ||
751 | now = mac80211_hwsim_get_tsf_raw(); | ||
752 | |||
725 | /* Copy skb to all enabled radios that are on the current frequency */ | 753 | /* Copy skb to all enabled radios that are on the current frequency */ |
726 | spin_lock(&hwsim_radio_lock); | 754 | spin_lock(&hwsim_radio_lock); |
727 | list_for_each_entry(data2, &hwsim_radios, list) { | 755 | list_for_each_entry(data2, &hwsim_radios, list) { |
728 | struct sk_buff *nskb; | 756 | struct sk_buff *nskb; |
729 | struct ieee80211_mgmt *mgmt; | ||
730 | struct tx_iter_data tx_iter_data = { | 757 | struct tx_iter_data tx_iter_data = { |
731 | .receive = false, | 758 | .receive = false, |
732 | .channel = chan, | 759 | .channel = chan, |
@@ -755,24 +782,30 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, | |||
755 | * reserve some space for our vendor and the normal | 782 | * reserve some space for our vendor and the normal |
756 | * radiotap header, since we're copying anyway | 783 | * radiotap header, since we're copying anyway |
757 | */ | 784 | */ |
758 | nskb = skb_copy_expand(skb, 64, 0, GFP_ATOMIC); | 785 | if (skb->len < PAGE_SIZE && paged_rx) { |
759 | if (nskb == NULL) | 786 | struct page *page = alloc_page(GFP_ATOMIC); |
760 | continue; | 787 | |
788 | if (!page) | ||
789 | continue; | ||
790 | |||
791 | nskb = dev_alloc_skb(128); | ||
792 | if (!nskb) { | ||
793 | __free_page(page); | ||
794 | continue; | ||
795 | } | ||
796 | |||
797 | memcpy(page_address(page), skb->data, skb->len); | ||
798 | skb_add_rx_frag(nskb, 0, page, 0, skb->len, skb->len); | ||
799 | } else { | ||
800 | nskb = skb_copy(skb, GFP_ATOMIC); | ||
801 | if (!nskb) | ||
802 | continue; | ||
803 | } | ||
761 | 804 | ||
762 | if (mac80211_hwsim_addr_match(data2, hdr->addr1)) | 805 | if (mac80211_hwsim_addr_match(data2, hdr->addr1)) |
763 | ack = true; | 806 | ack = true; |
764 | 807 | ||
765 | /* set bcn timestamp relative to receiver mactime */ | 808 | rx_status.mactime = now + data2->tsf_offset; |
766 | rx_status.mactime = | ||
767 | le64_to_cpu(__mac80211_hwsim_get_tsf(data2)); | ||
768 | mgmt = (struct ieee80211_mgmt *) nskb->data; | ||
769 | if (ieee80211_is_beacon(mgmt->frame_control) || | ||
770 | ieee80211_is_probe_resp(mgmt->frame_control)) | ||
771 | mgmt->u.beacon.timestamp = cpu_to_le64( | ||
772 | rx_status.mactime + | ||
773 | (data->tsf_offset - data2->tsf_offset) + | ||
774 | 24 * 8 * 10 / txrate->bitrate); | ||
775 | |||
776 | #if 0 | 809 | #if 0 |
777 | /* | 810 | /* |
778 | * Don't enable this code by default as the OUI 00:00:00 | 811 | * Don't enable this code by default as the OUI 00:00:00 |
@@ -896,7 +929,7 @@ static void mac80211_hwsim_stop(struct ieee80211_hw *hw) | |||
896 | { | 929 | { |
897 | struct mac80211_hwsim_data *data = hw->priv; | 930 | struct mac80211_hwsim_data *data = hw->priv; |
898 | data->started = false; | 931 | data->started = false; |
899 | del_timer(&data->beacon_timer); | 932 | tasklet_hrtimer_cancel(&data->beacon_timer); |
900 | wiphy_debug(hw->wiphy, "%s\n", __func__); | 933 | wiphy_debug(hw->wiphy, "%s\n", __func__); |
901 | } | 934 | } |
902 | 935 | ||
@@ -962,7 +995,11 @@ static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, | |||
962 | static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, | 995 | static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, |
963 | struct ieee80211_vif *vif) | 996 | struct ieee80211_vif *vif) |
964 | { | 997 | { |
965 | struct ieee80211_hw *hw = arg; | 998 | struct mac80211_hwsim_data *data = arg; |
999 | struct ieee80211_hw *hw = data->hw; | ||
1000 | struct ieee80211_tx_info *info; | ||
1001 | struct ieee80211_rate *txrate; | ||
1002 | struct ieee80211_mgmt *mgmt; | ||
966 | struct sk_buff *skb; | 1003 | struct sk_buff *skb; |
967 | 1004 | ||
968 | hwsim_check_magic(vif); | 1005 | hwsim_check_magic(vif); |
@@ -975,26 +1012,48 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, | |||
975 | skb = ieee80211_beacon_get(hw, vif); | 1012 | skb = ieee80211_beacon_get(hw, vif); |
976 | if (skb == NULL) | 1013 | if (skb == NULL) |
977 | return; | 1014 | return; |
1015 | info = IEEE80211_SKB_CB(skb); | ||
1016 | txrate = ieee80211_get_tx_rate(hw, info); | ||
1017 | |||
1018 | mgmt = (struct ieee80211_mgmt *) skb->data; | ||
1019 | /* fake header transmission time */ | ||
1020 | data->abs_bcn_ts = mac80211_hwsim_get_tsf_raw(); | ||
1021 | mgmt->u.beacon.timestamp = cpu_to_le64(data->abs_bcn_ts + | ||
1022 | data->tsf_offset + | ||
1023 | 24 * 8 * 10 / txrate->bitrate); | ||
978 | 1024 | ||
979 | mac80211_hwsim_tx_frame(hw, skb, | 1025 | mac80211_hwsim_tx_frame(hw, skb, |
980 | rcu_dereference(vif->chanctx_conf)->def.chan); | 1026 | rcu_dereference(vif->chanctx_conf)->def.chan); |
981 | } | 1027 | } |
982 | 1028 | ||
983 | 1029 | static enum hrtimer_restart | |
984 | static void mac80211_hwsim_beacon(unsigned long arg) | 1030 | mac80211_hwsim_beacon(struct hrtimer *timer) |
985 | { | 1031 | { |
986 | struct ieee80211_hw *hw = (struct ieee80211_hw *) arg; | 1032 | struct mac80211_hwsim_data *data = |
987 | struct mac80211_hwsim_data *data = hw->priv; | 1033 | container_of(timer, struct mac80211_hwsim_data, |
1034 | beacon_timer.timer); | ||
1035 | struct ieee80211_hw *hw = data->hw; | ||
1036 | u64 bcn_int = data->beacon_int; | ||
1037 | ktime_t next_bcn; | ||
988 | 1038 | ||
989 | if (!data->started) | 1039 | if (!data->started) |
990 | return; | 1040 | goto out; |
991 | 1041 | ||
992 | ieee80211_iterate_active_interfaces_atomic( | 1042 | ieee80211_iterate_active_interfaces_atomic( |
993 | hw, IEEE80211_IFACE_ITER_NORMAL, | 1043 | hw, IEEE80211_IFACE_ITER_NORMAL, |
994 | mac80211_hwsim_beacon_tx, hw); | 1044 | mac80211_hwsim_beacon_tx, data); |
1045 | |||
1046 | /* beacon at new TBTT + beacon interval */ | ||
1047 | if (data->bcn_delta) { | ||
1048 | bcn_int -= data->bcn_delta; | ||
1049 | data->bcn_delta = 0; | ||
1050 | } | ||
995 | 1051 | ||
996 | data->beacon_timer.expires = jiffies + data->beacon_int; | 1052 | next_bcn = ktime_add(hrtimer_get_expires(timer), |
997 | add_timer(&data->beacon_timer); | 1053 | ns_to_ktime(bcn_int * 1000)); |
1054 | tasklet_hrtimer_start(&data->beacon_timer, next_bcn, HRTIMER_MODE_ABS); | ||
1055 | out: | ||
1056 | return HRTIMER_NORESTART; | ||
998 | } | 1057 | } |
999 | 1058 | ||
1000 | static const char *hwsim_chantypes[] = { | 1059 | static const char *hwsim_chantypes[] = { |
@@ -1032,9 +1091,16 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) | |||
1032 | 1091 | ||
1033 | data->power_level = conf->power_level; | 1092 | data->power_level = conf->power_level; |
1034 | if (!data->started || !data->beacon_int) | 1093 | if (!data->started || !data->beacon_int) |
1035 | del_timer(&data->beacon_timer); | 1094 | tasklet_hrtimer_cancel(&data->beacon_timer); |
1036 | else | 1095 | else if (!hrtimer_is_queued(&data->beacon_timer.timer)) { |
1037 | mod_timer(&data->beacon_timer, jiffies + data->beacon_int); | 1096 | u64 tsf = mac80211_hwsim_get_tsf(hw, NULL); |
1097 | u32 bcn_int = data->beacon_int; | ||
1098 | u64 until_tbtt = bcn_int - do_div(tsf, bcn_int); | ||
1099 | |||
1100 | tasklet_hrtimer_start(&data->beacon_timer, | ||
1101 | ns_to_ktime(until_tbtt * 1000), | ||
1102 | HRTIMER_MODE_REL); | ||
1103 | } | ||
1038 | 1104 | ||
1039 | return 0; | 1105 | return 0; |
1040 | } | 1106 | } |
@@ -1084,12 +1150,26 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, | |||
1084 | 1150 | ||
1085 | if (changed & BSS_CHANGED_BEACON_INT) { | 1151 | if (changed & BSS_CHANGED_BEACON_INT) { |
1086 | wiphy_debug(hw->wiphy, " BCNINT: %d\n", info->beacon_int); | 1152 | wiphy_debug(hw->wiphy, " BCNINT: %d\n", info->beacon_int); |
1087 | data->beacon_int = 1024 * info->beacon_int / 1000 * HZ / 1000; | 1153 | data->beacon_int = info->beacon_int * 1024; |
1088 | if (WARN_ON(!data->beacon_int)) | 1154 | } |
1089 | data->beacon_int = 1; | 1155 | |
1090 | if (data->started) | 1156 | if (changed & BSS_CHANGED_BEACON_ENABLED) { |
1091 | mod_timer(&data->beacon_timer, | 1157 | wiphy_debug(hw->wiphy, " BCN EN: %d\n", info->enable_beacon); |
1092 | jiffies + data->beacon_int); | 1158 | if (data->started && |
1159 | !hrtimer_is_queued(&data->beacon_timer.timer) && | ||
1160 | info->enable_beacon) { | ||
1161 | u64 tsf, until_tbtt; | ||
1162 | u32 bcn_int; | ||
1163 | if (WARN_ON(!data->beacon_int)) | ||
1164 | data->beacon_int = 1000 * 1024; | ||
1165 | tsf = mac80211_hwsim_get_tsf(hw, vif); | ||
1166 | bcn_int = data->beacon_int; | ||
1167 | until_tbtt = bcn_int - do_div(tsf, bcn_int); | ||
1168 | tasklet_hrtimer_start(&data->beacon_timer, | ||
1169 | ns_to_ktime(until_tbtt * 1000), | ||
1170 | HRTIMER_MODE_REL); | ||
1171 | } else if (!info->enable_beacon) | ||
1172 | tasklet_hrtimer_cancel(&data->beacon_timer); | ||
1093 | } | 1173 | } |
1094 | 1174 | ||
1095 | if (changed & BSS_CHANGED_ERP_CTS_PROT) { | 1175 | if (changed & BSS_CHANGED_ERP_CTS_PROT) { |
@@ -1292,7 +1372,9 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw, | |||
1292 | case IEEE80211_AMPDU_TX_START: | 1372 | case IEEE80211_AMPDU_TX_START: |
1293 | ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); | 1373 | ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); |
1294 | break; | 1374 | break; |
1295 | case IEEE80211_AMPDU_TX_STOP: | 1375 | case IEEE80211_AMPDU_TX_STOP_CONT: |
1376 | case IEEE80211_AMPDU_TX_STOP_FLUSH: | ||
1377 | case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: | ||
1296 | ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); | 1378 | ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); |
1297 | break; | 1379 | break; |
1298 | case IEEE80211_AMPDU_TX_OPERATIONAL: | 1380 | case IEEE80211_AMPDU_TX_OPERATIONAL: |
@@ -2370,8 +2452,9 @@ static int __init init_mac80211_hwsim(void) | |||
2370 | data->debugfs, data, | 2452 | data->debugfs, data, |
2371 | &hwsim_fops_group); | 2453 | &hwsim_fops_group); |
2372 | 2454 | ||
2373 | setup_timer(&data->beacon_timer, mac80211_hwsim_beacon, | 2455 | tasklet_hrtimer_init(&data->beacon_timer, |
2374 | (unsigned long) hw); | 2456 | mac80211_hwsim_beacon, |
2457 | CLOCK_REALTIME, HRTIMER_MODE_ABS); | ||
2375 | 2458 | ||
2376 | list_add_tail(&data->list, &hwsim_radios); | 2459 | list_add_tail(&data->list, &hwsim_radios); |
2377 | } | 2460 | } |