If a dedicated (off-channel) radar detection hardware (chain) is available in the hardware/driver, allow this to be used by calling the NL80211_CMD_RADAR_DETECT command with a new flag attribute requesting off-channel radar detection is used. Offchannel CAC (channel availability check) avoids the CAC downtime when switching to a radar channel or when turning on the AP. Drivers advertise support for this using the new feature flag NL80211_EXT_FEATURE_RADAR_OFFCHAN.
Unified · Split
| @@ -4072,6 +4072,15 @@ struct mgmt_frame_regs { · 4071 unmodified lines | ||
| 4072 | 4072 | * @set_fils_aad: Set FILS AAD data to the AP driver so that the driver can use |
| 4073 | 4073 | * those to decrypt (Re)Association Request and encrypt (Re)Association |
| 4074 | 4074 | * Response frame. |
| 4075 | + * | |
| 4076 | + * @set_radar_offchan: Configure dedicated offchannel chain available for | |
| 4077 | + * radar/CAC detection on some hw. This chain can't be used to transmit | |
| 4078 | + * or receive frames and it is bounded to a running wdev. | |
| 4079 | + * Offchannel radar/CAC detection allows to avoid the CAC downtime | |
| 4080 | + * switching to a different channel during CAC detection on the selected | |
| 4081 | + * radar channel. | |
| 4082 | + * The caller is expected to set chandef pointer to NULL in order to | |
| 4083 | + * disable offchannel CAC/radar detection. | |
| 4075 | 4084 | */ |
| 4076 | 4085 | struct cfg80211_ops { |
| 4077 | 4086 | int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); |
| @@ -4404,6 +4413,8 @@ struct cfg80211_ops { · 326 unmodified lines | ||
| 4404 | 4413 | struct cfg80211_color_change_settings *params); |
| 4405 | 4414 | int (*set_fils_aad)(struct wiphy *wiphy, struct net_device *dev, |
| 4406 | 4415 | struct cfg80211_fils_aad *fils_aad); |
| 4416 | + int (*set_radar_offchan)(struct wiphy *wiphy, | |
| 4417 | + struct cfg80211_chan_def *chandef); | |
| 4407 | 4418 | }; |
| 4408 | 4419 | |
| 4409 | 4420 | /* |
| @@ -7633,6 +7644,20 @@ void cfg80211_cac_event(struct net_device *netdev, · 3223 unmodified lines | ||
| 7633 | 7644 | const struct cfg80211_chan_def *chandef, |
| 7634 | 7645 | enum nl80211_radar_event event, gfp_t gfp); |
| 7635 | 7646 | |
| 7647 | +/** | |
| 7648 | + * cfg80211_offchan_cac_event - Channel Availability Check (CAC) offchan event | |
| 7649 | + * @wiphy: the wiphy | |
| 7650 | + * @chandef: chandef for the current channel | |
| 7651 | + * @event: type of event | |
| 7652 | + * | |
| 7653 | + * This function is called when a Channel Availability Check (CAC) is finished, | |
| 7654 | + * started or aborted by a offchannel dedicated chain. | |
| 7655 | + * | |
| 7656 | + * Note that this acquires the wiphy lock. | |
| 7657 | + */ | |
| 7658 | +void cfg80211_offchan_cac_event(struct wiphy *wiphy, | |
| 7659 | + const struct cfg80211_chan_def *chandef, | |
| 7660 | + enum nl80211_radar_event event); | |
| 7636 | 7661 | |
| 7637 | 7662 | /** |
| 7638 | 7663 | * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying |
| @@ -2639,6 +2639,13 @@ enum nl80211_commands { · 2638 unmodified lines | ||
| 2639 | 2639 | * Mandatory parameter for the transmitting interface to enable MBSSID. |
| 2640 | 2640 | * Optional for the non-transmitting interfaces. |
| 2641 | 2641 | * |
| 2642 | + * @NL80211_ATTR_RADAR_OFFCHAN: Configure dedicated offchannel chain available for | |
| 2643 | + * radar/CAC detection on some hw. This chain can't be used to transmit | |
| 2644 | + * or receive frames and it is bounded to a running wdev. | |
| 2645 | + * Offchannel radar/CAC detection allows to avoid the CAC downtime | |
| 2646 | + * switching on a different channel during CAC detection on the selected | |
| 2647 | + * radar channel. | |
| 2648 | + * | |
| 2642 | 2649 | * @NUM_NL80211_ATTR: total number of nl80211_attrs available |
| 2643 | 2650 | * @NL80211_ATTR_MAX: highest attribute number currently defined |
| 2644 | 2651 | * @__NL80211_ATTR_AFTER_LAST: internal use |
| @@ -3145,6 +3152,8 @@ enum nl80211_attrs { · 500 unmodified lines | ||
| 3145 | 3152 | NL80211_ATTR_MBSSID_CONFIG, |
| 3146 | 3153 | NL80211_ATTR_MBSSID_ELEMS, |
| 3147 | 3154 | |
| 3155 | + NL80211_ATTR_RADAR_OFFCHAN, | |
| 3156 | + | |
| 3148 | 3157 | /* add attributes here, update the policy in nl80211.c */ |
| 3149 | 3158 | |
| 3150 | 3159 | __NL80211_ATTR_AFTER_LAST, |
| @@ -6051,6 +6060,9 @@ enum nl80211_feature_flags { · 2900 unmodified lines | ||
| 6051 | 6060 | * frames. Userspace has to share FILS AAD details to the driver by using |
| 6052 | 6061 | * @NL80211_CMD_SET_FILS_AAD. |
| 6053 | 6062 | * |
| 6063 | + * @NL80211_EXT_FEATURE_RADAR_OFFCHAN: Device supports offchannel radar/CAC | |
| 6064 | + * detection. | |
| 6065 | + * | |
| 6054 | 6066 | * @NUM_NL80211_EXT_FEATURES: number of extended features. |
| 6055 | 6067 | * @MAX_NL80211_EXT_FEATURES: highest extended feature index. |
| 6056 | 6068 | */ |
| @@ -6117,6 +6129,7 @@ enum nl80211_ext_feature_index { · 60 unmodified lines | ||
| 6117 | 6129 | NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE, |
| 6118 | 6130 | NL80211_EXT_FEATURE_BSS_COLOR, |
| 6119 | 6131 | NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD, |
| 6132 | + NL80211_EXT_FEATURE_RADAR_OFFCHAN, | |
| 6120 | 6133 | |
| 6121 | 6134 | /* add new features before the definition below */ |
| 6122 | 6135 | NUM_NL80211_EXT_FEATURES, |
| @@ -545,6 +545,7 @@ use_default_name: · 544 unmodified lines | ||
| 545 | 545 | INIT_WORK(&rdev->rfkill_block, cfg80211_rfkill_block_work); |
| 546 | 546 | INIT_WORK(&rdev->conn_work, cfg80211_conn_work); |
| 547 | 547 | INIT_WORK(&rdev->event_work, cfg80211_event_work); |
| 548 | + INIT_DELAYED_WORK(&rdev->offchan_cac_work, cfg80211_offchan_cac_work); | |
| 548 | 549 | |
| 549 | 550 | init_waitqueue_head(&rdev->dev_wait); |
| 550 | 551 | |
| @@ -1207,6 +1208,8 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev, · 656 unmodified lines | ||
| 1207 | 1208 | |
| 1208 | 1209 | cfg80211_pmsr_wdev_down(wdev); |
| 1209 | 1210 | |
| 1211 | + cfg80211_stop_offchan_radar_detection(wdev); | |
| 1212 | + | |
| 1210 | 1213 | switch (wdev->iftype) { |
| 1211 | 1214 | case NL80211_IFTYPE_ADHOC: |
| 1212 | 1215 | __cfg80211_leave_ibss(rdev, dev, true); |
| @@ -84,6 +84,10 @@ struct cfg80211_registered_device { · 83 unmodified lines | ||
| 84 | 84 | |
| 85 | 85 | struct delayed_work dfs_update_channels_wk; |
| 86 | 86 | |
| 87 | + struct wireless_dev *offchan_radar_wdev; | |
| 88 | + struct cfg80211_chan_def offchan_radar_chandef; | |
| 89 | + struct delayed_work offchan_cac_work; | |
| 90 | + | |
| 87 | 91 | /* netlink port which started critical protocol (0 means not started) */ |
| 88 | 92 | u32 crit_proto_nlportid; |
| 89 | 93 | |
| @@ -491,6 +495,15 @@ cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy, · 401 unmodified lines | ||
| 491 | 495 | |
| 492 | 496 | void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev); |
| 493 | 497 | |
| 498 | +int | |
| 499 | +cfg80211_start_offchan_radar_detection(struct cfg80211_registered_device *rdev, | |
| 500 | + struct wireless_dev *wdev, | |
| 501 | + struct cfg80211_chan_def *chandef); | |
| 502 | + | |
| 503 | +void cfg80211_stop_offchan_radar_detection(struct wireless_dev *wdev); | |
| 504 | + | |
| 505 | +void cfg80211_offchan_cac_work(struct work_struct *work); | |
| 506 | + | |
| 494 | 507 | bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy, |
| 495 | 508 | struct ieee80211_channel *chan); |
| 496 | 509 | |
| @@ -970,3 +970,116 @@ void cfg80211_cac_event(struct net_device *netdev, · 969 unmodified lines | ||
| 970 | 970 | nl80211_radar_notify(rdev, chandef, event, netdev, gfp); |
| 971 | 971 | } |
| 972 | 972 | EXPORT_SYMBOL(cfg80211_cac_event); |
| 973 | + | |
| 974 | +void cfg80211_offchan_cac_work(struct work_struct *work) | |
| 975 | +{ | |
| 976 | + struct delayed_work *delayed_work = to_delayed_work(work); | |
| 977 | + struct cfg80211_registered_device *rdev; | |
| 978 | + | |
| 979 | + rdev = container_of(delayed_work, struct cfg80211_registered_device, | |
| 980 | + offchan_cac_work); | |
| 981 | + cfg80211_offchan_cac_event(&rdev->wiphy, &rdev->offchan_radar_chandef, | |
| 982 | + NL80211_RADAR_CAC_FINISHED); | |
| 983 | +} | |
| 984 | + | |
| 985 | +static void | |
| 986 | +__cfg80211_offchan_cac_event(struct cfg80211_registered_device *rdev, | |
| 987 | + struct wireless_dev *wdev, | |
| 988 | + const struct cfg80211_chan_def *chandef, | |
| 989 | + enum nl80211_radar_event event) | |
| 990 | +{ | |
| 991 | + struct wiphy *wiphy = &rdev->wiphy; | |
| 992 | + struct net_device *netdev; | |
| 993 | + | |
| 994 | + lockdep_assert_wiphy(&rdev->wiphy); | |
| 995 | + | |
| 996 | + if (event != NL80211_RADAR_CAC_STARTED && !rdev->offchan_radar_wdev) | |
| 997 | + return; | |
| 998 | + | |
| 999 | + switch (event) { | |
| 1000 | + case NL80211_RADAR_CAC_FINISHED: | |
| 1001 | + cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE); | |
| 1002 | + memcpy(&rdev->cac_done_chandef, chandef, sizeof(*chandef)); | |
| 1003 | + queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk); | |
| 1004 | + cfg80211_sched_dfs_chan_update(rdev); | |
| 1005 | + wdev = rdev->offchan_radar_wdev; | |
| 1006 | + rdev->offchan_radar_wdev = NULL; | |
| 1007 | + break; | |
| 1008 | + case NL80211_RADAR_CAC_ABORTED: | |
| 1009 | + cancel_delayed_work(&rdev->offchan_cac_work); | |
| 1010 | + wdev = rdev->offchan_radar_wdev; | |
| 1011 | + rdev->offchan_radar_wdev = NULL; | |
| 1012 | + break; | |
| 1013 | + case NL80211_RADAR_CAC_STARTED: | |
| 1014 | + WARN_ON(!wdev); | |
| 1015 | + rdev->offchan_radar_wdev = wdev; | |
| 1016 | + break; | |
| 1017 | + default: | |
| 1018 | + return; | |
| 1019 | + } | |
| 1020 | + | |
| 1021 | + netdev = wdev ? wdev->netdev : NULL; | |
| 1022 | + nl80211_radar_notify(rdev, chandef, event, netdev, GFP_KERNEL); | |
| 1023 | +} | |
| 1024 | + | |
| 1025 | +void cfg80211_offchan_cac_event(struct wiphy *wiphy, | |
| 1026 | + const struct cfg80211_chan_def *chandef, | |
| 1027 | + enum nl80211_radar_event event) | |
| 1028 | +{ | |
| 1029 | + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | |
| 1030 | + | |
| 1031 | + wiphy_lock(wiphy); | |
| 1032 | + __cfg80211_offchan_cac_event(rdev, NULL, chandef, event); | |
| 1033 | + wiphy_unlock(wiphy); | |
| 1034 | +} | |
| 1035 | +EXPORT_SYMBOL(cfg80211_offchan_cac_event); | |
| 1036 | + | |
| 1037 | +int | |
| 1038 | +cfg80211_start_offchan_radar_detection(struct cfg80211_registered_device *rdev, | |
| 1039 | + struct wireless_dev *wdev, | |
| 1040 | + struct cfg80211_chan_def *chandef) | |
| 1041 | +{ | |
| 1042 | + unsigned int cac_time_ms; | |
| 1043 | + int err; | |
| 1044 | + | |
| 1045 | + lockdep_assert_wiphy(&rdev->wiphy); | |
| 1046 | + | |
| 1047 | + if (!wiphy_ext_feature_isset(&rdev->wiphy, | |
| 1048 | + NL80211_EXT_FEATURE_RADAR_OFFCHAN)) | |
| 1049 | + return -EOPNOTSUPP; | |
| 1050 | + | |
| 1051 | + if (rdev->offchan_radar_wdev) | |
| 1052 | + return -EBUSY; | |
| 1053 | + | |
| 1054 | + err = rdev_set_radar_offchan(rdev, chandef); | |
| 1055 | + if (err) | |
| 1056 | + return err; | |
| 1057 | + | |
| 1058 | + cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, chandef); | |
| 1059 | + if (!cac_time_ms) | |
| 1060 | + cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; | |
| 1061 | + | |
| 1062 | + rdev->offchan_radar_chandef = *chandef; | |
| 1063 | + __cfg80211_offchan_cac_event(rdev, wdev, chandef, | |
| 1064 | + NL80211_RADAR_CAC_STARTED); | |
| 1065 | + queue_delayed_work(cfg80211_wq, &rdev->offchan_cac_work, | |
| 1066 | + msecs_to_jiffies(cac_time_ms)); | |
| 1067 | + | |
| 1068 | + return 0; | |
| 1069 | +} | |
| 1070 | + | |
| 1071 | +void cfg80211_stop_offchan_radar_detection(struct wireless_dev *wdev) | |
| 1072 | +{ | |
| 1073 | + struct wiphy *wiphy = wdev->wiphy; | |
| 1074 | + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | |
| 1075 | + | |
| 1076 | + lockdep_assert_wiphy(wiphy); | |
| 1077 | + | |
| 1078 | + if (wdev != rdev->offchan_radar_wdev) | |
| 1079 | + return; | |
| 1080 | + | |
| 1081 | + rdev_set_radar_offchan(rdev, NULL); | |
| 1082 | + | |
| 1083 | + __cfg80211_offchan_cac_event(rdev, NULL, NULL, | |
| 1084 | + NL80211_RADAR_CAC_ABORTED); | |
| 1085 | +} | |
| @@ -776,6 +776,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { · 775 unmodified lines | ||
| 776 | 776 | [NL80211_ATTR_MBSSID_CONFIG] = |
| 777 | 777 | NLA_POLICY_NESTED(nl80211_mbssid_config_policy), |
| 778 | 778 | [NL80211_ATTR_MBSSID_ELEMS] = { .type = NLA_NESTED }, |
| 779 | + [NL80211_ATTR_RADAR_OFFCHAN] = { .type = NLA_FLAG }, | |
| 779 | 780 | }; |
| 780 | 781 | |
| 781 | 782 | /* policy for the key attributes */ |
| @@ -9284,12 +9285,6 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, · 8502 unmodified lines | ||
| 9284 | 9285 | if (err) |
| 9285 | 9286 | return err; |
| 9286 | 9287 | |
| 9287 | − if (netif_carrier_ok(dev)) | |
| 9288 | − return -EBUSY; | |
| 9289 | − | |
| 9290 | − if (wdev->cac_started) | |
| 9291 | − return -EBUSY; | |
| 9292 | − | |
| 9293 | 9288 | err = cfg80211_chandef_dfs_required(wiphy, &chandef, wdev->iftype); |
| 9294 | 9289 | if (err < 0) |
| 9295 | 9290 | return err; |
| @@ -9300,6 +9295,16 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, · 4 unmodified lines | ||
| 9300 | 9295 | if (!cfg80211_chandef_dfs_usable(wiphy, &chandef)) |
| 9301 | 9296 | return -EINVAL; |
| 9302 | 9297 | |
| 9298 | + if (nla_get_flag(info->attrs[NL80211_ATTR_RADAR_OFFCHAN])) | |
| 9299 | + return cfg80211_start_offchan_radar_detection(rdev, wdev, | |
| 9300 | + &chandef); | |
| 9301 | + | |
| 9302 | + if (netif_carrier_ok(dev)) | |
| 9303 | + return -EBUSY; | |
| 9304 | + | |
| 9305 | + if (wdev->cac_started) | |
| 9306 | + return -EBUSY; | |
| 9307 | + | |
| 9303 | 9308 | /* CAC start is offloaded to HW and can't be started manually */ |
| 9304 | 9309 | if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD)) |
| 9305 | 9310 | return -EOPNOTSUPP; |
| @@ -1395,4 +1395,21 @@ rdev_set_fils_aad(struct cfg80211_registered_device *rdev, · 1394 unmodified lines | ||
| 1395 | 1395 | return ret; |
| 1396 | 1396 | } |
| 1397 | 1397 | |
| 1398 | +static inline int | |
| 1399 | +rdev_set_radar_offchan(struct cfg80211_registered_device *rdev, | |
| 1400 | + struct cfg80211_chan_def *chandef) | |
| 1401 | +{ | |
| 1402 | + struct wiphy *wiphy = &rdev->wiphy; | |
| 1403 | + int ret; | |
| 1404 | + | |
| 1405 | + if (!rdev->ops->set_radar_offchan) | |
| 1406 | + return -EOPNOTSUPP; | |
| 1407 | + | |
| 1408 | + trace_rdev_set_radar_offchan(wiphy, chandef); | |
| 1409 | + ret = rdev->ops->set_radar_offchan(wiphy, chandef); | |
| 1410 | + trace_rdev_return_int(wiphy, ret); | |
| 1411 | + | |
| 1412 | + return ret; | |
| 1413 | +} | |
| 1414 | + | |
| 1398 | 1415 | #endif /* __CFG80211_RDEV_OPS */ |
| @@ -3674,6 +3674,25 @@ TRACE_EVENT(cfg80211_bss_color_notify, · 3673 unmodified lines | ||
| 3674 | 3674 | __entry->color_bitmap) |
| 3675 | 3675 | ); |
| 3676 | 3676 | |
| 3677 | +TRACE_EVENT(rdev_set_radar_offchan, | |
| 3678 | + TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef), | |
| 3679 | + | |
| 3680 | + TP_ARGS(wiphy, chandef), | |
| 3681 | + | |
| 3682 | + TP_STRUCT__entry( | |
| 3683 | + WIPHY_ENTRY | |
| 3684 | + CHAN_DEF_ENTRY | |
| 3685 | + ), | |
| 3686 | + | |
| 3687 | + TP_fast_assign( | |
| 3688 | + WIPHY_ASSIGN; | |
| 3689 | + CHAN_DEF_ASSIGN(chandef) | |
| 3690 | + ), | |
| 3691 | + | |
| 3692 | + TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, | |
| 3693 | + WIPHY_PR_ARG, CHAN_DEF_PR_ARG) | |
| 3694 | +); | |
| 3695 | + | |
| 3677 | 3696 | #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ |
| 3678 | 3697 | |
| 3679 | 3698 | #undef TRACE_INCLUDE_PATH |