#define IEEE80211_CHAN_NO_HT40 \
        (IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
 
+#define IEEE80211_DFS_MIN_CAC_TIME_MS          60000
+#define IEEE80211_DFS_MIN_NOP_TIME_MS          (30 * 60 * 1000)
+
 /**
  * struct ieee80211_channel - channel definition
  *
  *     to enable this, this is useful only on 5 GHz band.
  * @orig_mag: internal use
  * @orig_mpwr: internal use
+ * @dfs_state: current state of this channel. Only relevant if radar is required
+ *     on this channel.
+ * @dfs_state_entered: timestamp (jiffies) when the dfs state was entered.
  */
 struct ieee80211_channel {
        enum ieee80211_band band;
        bool beacon_found;
        u32 orig_flags;
        int orig_mag, orig_mpwr;
+       enum nl80211_dfs_state dfs_state;
+       unsigned long dfs_state_entered;
 };
 
 /**
  * @p2p_opp_ps: P2P opportunistic PS
  * @acl: ACL configuration used by the drivers which has support for
  *     MAC address based access control
+ * @radar_required: set if radar detection is required
  */
 struct cfg80211_ap_settings {
        struct cfg80211_chan_def chandef;
        u8 p2p_ctwindow;
        bool p2p_opp_ps;
        const struct cfg80211_acl_data *acl;
+       bool radar_required;
 };
 
 /**
  *     this new list replaces the existing one. Driver has to clear its ACL
  *     when number of MAC addresses entries is passed as 0. Drivers which
  *     advertise the support for MAC based ACL have to implement this callback.
+ *
+ * @start_radar_detection: Start radar detection in the driver.
  */
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
 
        int     (*set_mac_acl)(struct wiphy *wiphy, struct net_device *dev,
                               const struct cfg80211_acl_data *params);
+
+       int     (*start_radar_detection)(struct wiphy *wiphy,
+                                        struct net_device *dev,
+                                        struct cfg80211_chan_def *chandef);
 };
 
 /*
  *     beacons, 0 when not valid
  * @address: The address for this device, valid only if @netdev is %NULL
  * @p2p_started: true if this is a P2P Device that has been started
+ * @cac_started: true if DFS channel availability check has been started
+ * @cac_start_time: timestamp (jiffies) when the dfs state was entered.
  */
 struct wireless_dev {
        struct wiphy *wiphy;
 
        u32 ap_unexpected_nlportid;
 
+       bool cac_started;
+       unsigned long cac_start_time;
+
 #ifdef CONFIG_CFG80211_WEXT
        /* wext data */
        struct {
                              enum nl80211_cqm_rssi_threshold_event rssi_event,
                              gfp_t gfp);
 
+/**
+ * cfg80211_radar_event - radar detection event
+ * @wiphy: the wiphy
+ * @chandef: chandef for the current channel
+ * @gfp: context flags
+ *
+ * This function is called when a radar is detected on the current chanenl.
+ */
+void cfg80211_radar_event(struct wiphy *wiphy,
+                         struct cfg80211_chan_def *chandef, gfp_t gfp);
+
+/**
+ * cfg80211_cac_event - Channel availability check (CAC) event
+ * @netdev: network device
+ * @event: type of event
+ * @gfp: context flags
+ *
+ * This function is called when a Channel availability check (CAC) is finished
+ * or aborted. This must be called to notify the completion of a CAC process,
+ * also by full-MAC drivers.
+ */
+void cfg80211_cac_event(struct net_device *netdev,
+                       enum nl80211_radar_event event, gfp_t gfp);
+
+
 /**
  * cfg80211_cqm_pktloss_notify - notify userspace about packetloss to peer
  * @dev: network device
 
  *     command is used in AP/P2P GO mode. Driver has to make sure to clear its
  *     ACL list during %NL80211_CMD_STOP_AP.
  *
+ * @NL80211_CMD_RADAR_DETECT: Start a Channel availability check (CAC). Once
+ *     a radar is detected or the channel availability scan (CAC) has finished
+ *     or was aborted, or a radar was detected, usermode will be notified with
+ *     this event. This command is also used to notify userspace about radars
+ *     while operating on this channel.
+ *     %NL80211_ATTR_RADAR_EVENT is used to inform about the type of the
+ *     event.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
 
        NL80211_CMD_SET_MAC_ACL,
 
+       NL80211_CMD_RADAR_DETECT,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
  *     number of MAC addresses that a device can support for MAC
  *     ACL.
  *
+ * @NL80211_ATTR_RADAR_EVENT: Type of radar event for notification to userspace,
+ *     contains a value of enum nl80211_radar_event (u32).
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
 
        NL80211_ATTR_MAC_ACL_MAX,
 
+       NL80211_ATTR_RADAR_EVENT,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
  *     on this channel in current regulatory domain.
  * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm
  *     (100 * dBm).
+ * @NL80211_FREQUENCY_ATTR_DFS_STATE: current state for DFS
+ *     (enum nl80211_dfs_state)
+ * @NL80211_FREQUENCY_ATTR_DFS_TIME: time in miliseconds for how long
+ *     this channel is in this DFS state.
  * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
  *     currently defined
  * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
        NL80211_FREQUENCY_ATTR_NO_IBSS,
        NL80211_FREQUENCY_ATTR_RADAR,
        NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
+       NL80211_FREQUENCY_ATTR_DFS_STATE,
+       NL80211_FREQUENCY_ATTR_DFS_TIME,
 
        /* keep last */
        __NL80211_FREQUENCY_ATTR_AFTER_LAST,
        NL80211_ACL_POLICY_DENY_UNLESS_LISTED,
 };
 
+/**
+ * enum nl80211_radar_event - type of radar event for DFS operation
+ *
+ * Type of event to be used with NL80211_ATTR_RADAR_EVENT to inform userspace
+ * about detected radars or success of the channel available check (CAC)
+ *
+ * @NL80211_RADAR_DETECTED: A radar pattern has been detected. The channel is
+ *     now unusable.
+ * @NL80211_RADAR_CAC_FINISHED: Channel Availability Check has been finished,
+ *     the channel is now available.
+ * @NL80211_RADAR_CAC_ABORTED: Channel Availability Check has been aborted, no
+ *     change to the channel status.
+ * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is
+ *     over, channel becomes usable.
+ */
+enum nl80211_radar_event {
+       NL80211_RADAR_DETECTED,
+       NL80211_RADAR_CAC_FINISHED,
+       NL80211_RADAR_CAC_ABORTED,
+       NL80211_RADAR_NOP_FINISHED,
+};
+
+/**
+ * enum nl80211_dfs_state - DFS states for channels
+ *
+ * Channel states used by the DFS code.
+ *
+ * @IEEE80211_DFS_USABLE: The channel can be used, but channel availability
+ *     check (CAC) must be performed before using it for AP or IBSS.
+ * @IEEE80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
+ *     is therefore marked as not available.
+ * @IEEE80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
+ */
+
+enum nl80211_dfs_state {
+       NL80211_DFS_USABLE,
+       NL80211_DFS_UNAVAILABLE,
+       NL80211_DFS_AVAILABLE,
+};
+
 #endif /* __LINUX_NL80211_H */
 
        }
 }
 
+static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
+{
+       int width;
+
+       switch (c->width) {
+       case NL80211_CHAN_WIDTH_20:
+       case NL80211_CHAN_WIDTH_20_NOHT:
+               width = 20;
+               break;
+       case NL80211_CHAN_WIDTH_40:
+               width = 40;
+               break;
+       case NL80211_CHAN_WIDTH_80P80:
+       case NL80211_CHAN_WIDTH_80:
+               width = 80;
+               break;
+       case NL80211_CHAN_WIDTH_160:
+               width = 160;
+               break;
+       default:
+               WARN_ON_ONCE(1);
+               return -1;
+       }
+       return width;
+}
+
 const struct cfg80211_chan_def *
 cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
                            const struct cfg80211_chan_def *c2)
 }
 EXPORT_SYMBOL(cfg80211_chandef_compatible);
 
+static void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq,
+                                        u32 bandwidth,
+                                        enum nl80211_dfs_state dfs_state)
+{
+       struct ieee80211_channel *c;
+       u32 freq;
+
+       for (freq = center_freq - bandwidth/2 + 10;
+            freq <= center_freq + bandwidth/2 - 10;
+            freq += 20) {
+               c = ieee80211_get_channel(wiphy, freq);
+               if (!c || !(c->flags & IEEE80211_CHAN_RADAR))
+                       continue;
+
+               c->dfs_state = dfs_state;
+               c->dfs_state_entered = jiffies;
+       }
+}
+
+void cfg80211_set_dfs_state(struct wiphy *wiphy,
+                           const struct cfg80211_chan_def *chandef,
+                           enum nl80211_dfs_state dfs_state)
+{
+       int width;
+
+       if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+               return;
+
+       width = cfg80211_chandef_get_width(chandef);
+       if (width < 0)
+               return;
+
+       cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq1,
+                                    width, dfs_state);
+
+       if (!chandef->center_freq2)
+               return;
+       cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq2,
+                                    width, dfs_state);
+}
+
+static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
+                                           u32 center_freq,
+                                           u32 bandwidth)
+{
+       struct ieee80211_channel *c;
+       u32 freq;
+
+       for (freq = center_freq - bandwidth/2 + 10;
+            freq <= center_freq + bandwidth/2 - 10;
+            freq += 20) {
+               c = ieee80211_get_channel(wiphy, freq);
+               if (!c)
+                       return -EINVAL;
+
+               if (c->flags & IEEE80211_CHAN_RADAR)
+                       return 1;
+       }
+       return 0;
+}
+
+
+int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
+                                 const struct cfg80211_chan_def *chandef)
+{
+       int width;
+       int r;
+
+       if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+               return -EINVAL;
+
+       width = cfg80211_chandef_get_width(chandef);
+       if (width < 0)
+               return -EINVAL;
+
+       r = cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq1,
+                                           width);
+       if (r)
+               return r;
+
+       if (!chandef->center_freq2)
+               return 0;
+
+       return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2,
+                                              width);
+}
+
 static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
                                        u32 center_freq, u32 bandwidth,
                                        u32 prohibited_flags)
             freq <= center_freq + bandwidth/2 - 10;
             freq += 20) {
                c = ieee80211_get_channel(wiphy, freq);
-               if (!c || c->flags & prohibited_flags)
+               if (!c)
+                       return false;
+
+               /* check for radar flags */
+               if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) &&
+                   (c->dfs_state != NL80211_DFS_AVAILABLE))
+                       return false;
+
+               /* check for the other flags */
+               if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR)
                        return false;
        }
 
                break;
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_P2P_GO:
-               if (wdev->beacon_interval) {
+               if (wdev->cac_started) {
+                       *chan = wdev->channel;
+                       *chanmode = CHAN_MODE_SHARED;
+               } else if (wdev->beacon_interval) {
                        *chan = wdev->channel;
                        *chanmode = CHAN_MODE_SHARED;
                }
 
        INIT_LIST_HEAD(&rdev->bss_list);
        INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
        INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results);
+       INIT_DELAYED_WORK(&rdev->dfs_update_channels_wk,
+                         cfg80211_dfs_channels_update_work);
 #ifdef CONFIG_CFG80211_WEXT
        rdev->wiphy.wext = &cfg80211_wext_handler;
 #endif
        flush_work(&rdev->scan_done_wk);
        cancel_work_sync(&rdev->conn_work);
        flush_work(&rdev->event_work);
+       cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
 
        if (rdev->wowlan && rdev->ops->set_wakeup)
                rdev_set_wakeup(rdev, false);
 
 
        struct cfg80211_wowlan *wowlan;
 
+       struct delayed_work dfs_update_channels_wk;
+
        /* must be last because of the way we do wiphy_priv(),
         * and it should at least be aligned to NETDEV_ALIGN */
        struct wiphy wiphy __aligned(NETDEV_ALIGN);
                                 enum cfg80211_chan_mode chanmode,
                                 u8 radar_detect);
 
+/**
+ * cfg80211_chandef_dfs_required - checks if radar detection is required
+ * @wiphy: the wiphy to validate against
+ * @chandef: the channel definition to check
+ * Return: 1 if radar detection is required, 0 if it is not, < 0 on error
+ */
+int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
+                                 const struct cfg80211_chan_def *c);
+
+void cfg80211_set_dfs_state(struct wiphy *wiphy,
+                           const struct cfg80211_chan_def *chandef,
+                           enum nl80211_dfs_state dfs_state);
+
+void cfg80211_dfs_channels_update_work(struct work_struct *work);
+
+
 static inline int
 cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
                              struct wireless_dev *wdev,
                                            chan, chanmode, 0);
 }
 
+static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
+{
+       unsigned long end = jiffies;
+
+       if (end >= start)
+               return jiffies_to_msecs(end - start);
+
+       return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
+}
+
 void
 cfg80211_get_chan_state(struct wireless_dev *wdev,
                        struct ieee80211_channel **chan,
 
        nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
 }
 EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
+
+void cfg80211_dfs_channels_update_work(struct work_struct *work)
+{
+       struct delayed_work *delayed_work;
+       struct cfg80211_registered_device *rdev;
+       struct cfg80211_chan_def chandef;
+       struct ieee80211_supported_band *sband;
+       struct ieee80211_channel *c;
+       struct wiphy *wiphy;
+       bool check_again = false;
+       unsigned long timeout, next_time = 0;
+       int bandid, i;
+
+       delayed_work = container_of(work, struct delayed_work, work);
+       rdev = container_of(delayed_work, struct cfg80211_registered_device,
+                           dfs_update_channels_wk);
+       wiphy = &rdev->wiphy;
+
+       mutex_lock(&cfg80211_mutex);
+       for (bandid = 0; bandid < IEEE80211_NUM_BANDS; bandid++) {
+               sband = wiphy->bands[bandid];
+               if (!sband)
+                       continue;
+
+               for (i = 0; i < sband->n_channels; i++) {
+                       c = &sband->channels[i];
+
+                       if (c->dfs_state != NL80211_DFS_UNAVAILABLE)
+                               continue;
+
+                       timeout = c->dfs_state_entered +
+                                 IEEE80211_DFS_MIN_NOP_TIME_MS;
+
+                       if (time_after_eq(jiffies, timeout)) {
+                               c->dfs_state = NL80211_DFS_USABLE;
+                               cfg80211_chandef_create(&chandef, c,
+                                                       NL80211_CHAN_NO_HT);
+
+                               nl80211_radar_notify(rdev, &chandef,
+                                                    NL80211_RADAR_NOP_FINISHED,
+                                                    NULL, GFP_ATOMIC);
+                               continue;
+                       }
+
+                       if (!check_again)
+                               next_time = timeout - jiffies;
+                       else
+                               next_time = min(next_time, timeout - jiffies);
+                       check_again = true;
+               }
+       }
+       mutex_unlock(&cfg80211_mutex);
+
+       /* reschedule if there are other channels waiting to be cleared again */
+       if (check_again)
+               queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk,
+                                  next_time);
+}
+
+
+void cfg80211_radar_event(struct wiphy *wiphy,
+                         struct cfg80211_chan_def *chandef,
+                         gfp_t gfp)
+{
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       unsigned long timeout;
+
+       trace_cfg80211_radar_event(wiphy, chandef);
+
+       /* only set the chandef supplied channel to unavailable, in
+        * case the radar is detected on only one of multiple channels
+        * spanned by the chandef.
+        */
+       cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE);
+
+       timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_NOP_TIME_MS);
+       queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk,
+                          timeout);
+
+       nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp);
+}
+EXPORT_SYMBOL(cfg80211_radar_event);
+
+void cfg80211_cac_event(struct net_device *netdev,
+                       enum nl80211_radar_event event, gfp_t gfp)
+{
+       struct wireless_dev *wdev = netdev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+       struct cfg80211_chan_def chandef;
+       unsigned long timeout;
+
+       trace_cfg80211_cac_event(netdev, event);
+
+       if (WARN_ON(!wdev->cac_started))
+               return;
+
+       if (WARN_ON(!wdev->channel))
+               return;
+
+       cfg80211_chandef_create(&chandef, wdev->channel, NL80211_CHAN_NO_HT);
+
+       switch (event) {
+       case NL80211_RADAR_CAC_FINISHED:
+               timeout = wdev->cac_start_time +
+                         msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
+               WARN_ON(!time_after_eq(jiffies, timeout));
+               cfg80211_set_dfs_state(wiphy, &chandef, NL80211_DFS_AVAILABLE);
+               break;
+       case NL80211_RADAR_CAC_ABORTED:
+               break;
+       default:
+               WARN_ON(1);
+               return;
+       }
+       wdev->cac_started = false;
+
+       nl80211_radar_notify(rdev, &chandef, event, netdev, gfp);
+}
+EXPORT_SYMBOL(cfg80211_cac_event);
 
        if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
            nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
                goto nla_put_failure;
-       if ((chan->flags & IEEE80211_CHAN_RADAR) &&
-           nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
-               goto nla_put_failure;
+       if (chan->flags & IEEE80211_CHAN_RADAR) {
+               u32 time = elapsed_jiffies_msecs(chan->dfs_state_entered);
+               if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
+                       goto nla_put_failure;
+               if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_STATE,
+                               chan->dfs_state))
+                       goto nla_put_failure;
+               if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME, time))
+                       goto nla_put_failure;
+       }
 
        if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
                        DBM_TO_MBM(chan->max_power)))
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_ap_settings params;
        int err;
+       u8 radar_detect_width = 0;
 
        if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
            dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
        if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef))
                return -EINVAL;
 
+       err = cfg80211_chandef_dfs_required(wdev->wiphy, ¶ms.chandef);
+       if (err < 0)
+               return err;
+       if (err) {
+               radar_detect_width = BIT(params.chandef.width);
+               params.radar_required = true;
+       }
+
        mutex_lock(&rdev->devlist_mtx);
-       err = cfg80211_can_use_chan(rdev, wdev, params.chandef.chan,
-                                   CHAN_MODE_SHARED);
+       err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
+                                          params.chandef.chan,
+                                          CHAN_MODE_SHARED,
+                                          radar_detect_width);
        mutex_unlock(&rdev->devlist_mtx);
 
        if (err)
        return err;
 }
 
+static int nl80211_start_radar_detection(struct sk_buff *skb,
+                                        struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_chan_def chandef;
+       int err;
+
+       err = nl80211_parse_chandef(rdev, info, &chandef);
+       if (err)
+               return err;
+
+       if (wdev->cac_started)
+               return -EBUSY;
+
+       err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef);
+       if (err < 0)
+               return err;
+
+       if (err == 0)
+               return -EINVAL;
+
+       if (chandef.chan->dfs_state != NL80211_DFS_USABLE)
+               return -EINVAL;
+
+       if (!rdev->ops->start_radar_detection)
+               return -EOPNOTSUPP;
+
+       mutex_lock(&rdev->devlist_mtx);
+       err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
+                                          chandef.chan, CHAN_MODE_SHARED,
+                                          BIT(chandef.width));
+       if (err)
+               goto err_locked;
+
+       err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef);
+       if (!err) {
+               wdev->channel = chandef.chan;
+               wdev->cac_started = true;
+               wdev->cac_start_time = jiffies;
+       }
+err_locked:
+       mutex_unlock(&rdev->devlist_mtx);
+
+       return err;
+}
+
 static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
                            u32 seq, int flags,
                            struct cfg80211_registered_device *rdev,
                .internal_flags = NL80211_FLAG_NEED_NETDEV |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_RADAR_DETECT,
+               .doit = nl80211_start_radar_detection,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 };
 
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
        nlmsg_free(msg);
 }
 
+void
+nl80211_radar_notify(struct cfg80211_registered_device *rdev,
+                    struct cfg80211_chan_def *chandef,
+                    enum nl80211_radar_event event,
+                    struct net_device *netdev, gfp_t gfp)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_RADAR_DETECT);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
+               goto nla_put_failure;
+
+       /* NOP and radar events don't need a netdev parameter */
+       if (netdev) {
+               struct wireless_dev *wdev = netdev->ieee80211_ptr;
+
+               if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+                   nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
+                       goto nla_put_failure;
+       }
+
+       if (nla_put_u32(msg, NL80211_ATTR_RADAR_EVENT, event))
+               goto nla_put_failure;
+
+       if (nl80211_send_chandef(msg, chandef))
+               goto nla_put_failure;
+
+       if (genlmsg_end(msg, hdr) < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_mlme_mcgrp.id, gfp);
+       return;
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       nlmsg_free(msg);
+}
+
 void
 nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
                                struct net_device *netdev, const u8 *peer,
 
                             struct net_device *netdev,
                             enum nl80211_cqm_rssi_threshold_event rssi_event,
                             gfp_t gfp);
+
+void
+nl80211_radar_notify(struct cfg80211_registered_device *rdev,
+                    struct cfg80211_chan_def *chandef,
+                    enum nl80211_radar_event event,
+                    struct net_device *netdev, gfp_t gfp);
+
 void
 nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
                                struct net_device *netdev, const u8 *peer,
 
                return;
        }
 
+       chan->dfs_state = NL80211_DFS_USABLE;
+       chan->dfs_state_entered = jiffies;
+
        chan->beacon_found = false;
        chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags);
        chan->max_antenna_gain =
 
        }
 }
 
-static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
-{
-       unsigned long end = jiffies;
-
-       if (end >= start)
-               return jiffies_to_msecs(end - start);
-
-       return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
-}
-
 static char *
 ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
              struct cfg80211_internal_bss *bss, char *current_ev,
 
                  WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
 );
 
+TRACE_EVENT(cfg80211_chandef_dfs_required,
+       TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
+       TP_ARGS(wiphy, chandef),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               CHAN_DEF_ENTRY
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               CHAN_DEF_ASSIGN(chandef);
+       ),
+       TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
+                 WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
+);
+
 TRACE_EVENT(cfg80211_ch_switch_notify,
        TP_PROTO(struct net_device *netdev,
                 struct cfg80211_chan_def *chandef),
                  NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
 );
 
+TRACE_EVENT(cfg80211_radar_event,
+       TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
+       TP_ARGS(wiphy, chandef),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               CHAN_DEF_ENTRY
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               CHAN_DEF_ASSIGN(chandef);
+       ),
+       TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
+                 WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
+);
+
+TRACE_EVENT(cfg80211_cac_event,
+       TP_PROTO(struct net_device *netdev, enum nl80211_radar_event evt),
+       TP_ARGS(netdev, evt),
+       TP_STRUCT__entry(
+               NETDEV_ENTRY
+               __field(enum nl80211_radar_event, evt)
+       ),
+       TP_fast_assign(
+               NETDEV_ASSIGN;
+               __entry->evt = evt;
+       ),
+       TP_printk(NETDEV_PR_FMT ",  event: %d",
+                 NETDEV_PR_ARG, __entry->evt)
+);
+
 DECLARE_EVENT_CLASS(cfg80211_rx_evt,
        TP_PROTO(struct net_device *netdev, const u8 *addr),
        TP_ARGS(netdev, addr),