diff mbox

[1/4] cfg80211: regulatory, introduce DFS CAC time

Message ID 1392231266-28479-1-git-send-email-janusz.dziedzic@tieto.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Janusz.Dziedzic@tieto.com Feb. 12, 2014, 6:54 p.m. UTC
Introduce DFS CAC time as a regd param, configured
per REG_RULE and set per channel in cfg80211.
DFS CAC time is close connected with regulatory
database configuration. Instead of using hardcoded
values, get DFS CAC time form regulatory database.
Pass DFS CAC time to user mode (mainly for iw reg get,
iw list, iw info). Allow setting DFS CAC time via CRDA.

Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
---
 include/net/cfg80211.h       |    2 ++
 include/net/regulatory.h     |   21 +++++++++++++--------
 include/uapi/linux/nl80211.h |    6 ++++++
 net/wireless/nl80211.c       |   13 ++++++++++++-
 net/wireless/reg.c           |   39 +++++++++++++++++++++++++++++++--------
 5 files changed, 64 insertions(+), 17 deletions(-)

Comments

Luis R. Rodriguez Feb. 19, 2014, 12:31 a.m. UTC | #1
On Wed, Feb 12, 2014 at 10:54 AM, Janusz Dziedzic
<janusz.dziedzic@tieto.com> wrote:
> Introduce DFS CAC time as a regd param, configured
> per REG_RULE and set per channel in cfg80211.
> DFS CAC time is close connected with regulatory
> database configuration. Instead of using hardcoded
> values, get DFS CAC time form regulatory database.
> Pass DFS CAC time to user mode (mainly for iw reg get,
> iw list, iw info). Allow setting DFS CAC time via CRDA.
>
> Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>

Reviewed-by: Luis R. Rodriguez <mcgrof@do-not-panic.com>

Please also send a respective patch follow up (v2) that also addresses
the intersection of the cac time on the userspace equivalent of the
intersection.

  Luis
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Johannes Berg Feb. 19, 2014, 11:03 a.m. UTC | #2
On Wed, 2014-02-12 at 19:54 +0100, Janusz Dziedzic wrote:

> -#define REG_RULE(start, end, bw, gain, eirp, reg_flags) \
> -{							\
> -	.freq_range.start_freq_khz = MHZ_TO_KHZ(start),	\
> -	.freq_range.end_freq_khz = MHZ_TO_KHZ(end),	\
> -	.freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw),	\
> -	.power_rule.max_antenna_gain = DBI_TO_MBI(gain),\
> -	.power_rule.max_eirp = DBM_TO_MBM(eirp),	\
> -	.flags = reg_flags,				\
> +#define REG_RULE_EXT(start, end, bw, gain, eirp, dfs_cac, reg_flags)	\
> +{									\
> +	.freq_range.start_freq_khz = MHZ_TO_KHZ(start),			\
> +	.freq_range.end_freq_khz = MHZ_TO_KHZ(end),			\
> +	.freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw),			\
> +	.power_rule.max_antenna_gain = DBI_TO_MBI(gain),		\
> +	.power_rule.max_eirp = DBM_TO_MBM(eirp),			\
> +	.flags = reg_flags,						\
> +	.dfs_cac_ms = dfs_cac,						\
>  }
>  
> +#define REG_RULE(start, end, bw, gain, eirp, reg_flags) \
> +	REG_RULE_EXT(start, end, bw, gain, eirp, 0, reg_flags)

I don't see any users of this, is that really useful?

johannes

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Janusz.Dziedzic@tieto.com Feb. 19, 2014, 11:27 a.m. UTC | #3
On 19 February 2014 12:03, Johannes Berg <johannes@sipsolutions.net> wrote:
> On Wed, 2014-02-12 at 19:54 +0100, Janusz Dziedzic wrote:
>
>> -#define REG_RULE(start, end, bw, gain, eirp, reg_flags) \
>> -{                                                    \
>> -     .freq_range.start_freq_khz = MHZ_TO_KHZ(start), \
>> -     .freq_range.end_freq_khz = MHZ_TO_KHZ(end),     \
>> -     .freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw), \
>> -     .power_rule.max_antenna_gain = DBI_TO_MBI(gain),\
>> -     .power_rule.max_eirp = DBM_TO_MBM(eirp),        \
>> -     .flags = reg_flags,                             \
>> +#define REG_RULE_EXT(start, end, bw, gain, eirp, dfs_cac, reg_flags) \
>> +{                                                                    \
>> +     .freq_range.start_freq_khz = MHZ_TO_KHZ(start),                 \
>> +     .freq_range.end_freq_khz = MHZ_TO_KHZ(end),                     \
>> +     .freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw),                 \
>> +     .power_rule.max_antenna_gain = DBI_TO_MBI(gain),                \
>> +     .power_rule.max_eirp = DBM_TO_MBM(eirp),                        \
>> +     .flags = reg_flags,                                             \
>> +     .dfs_cac_ms = dfs_cac,                                          \
>>  }
>>
>> +#define REG_RULE(start, end, bw, gain, eirp, reg_flags) \
>> +     REG_RULE_EXT(start, end, bw, gain, eirp, 0, reg_flags)
>
> I don't see any users of this, is that really useful?
>

genregdb.awk using this when internal regdb is used.

BR
Janusz
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Johannes Berg Feb. 19, 2014, 11:28 a.m. UTC | #4
On Wed, 2014-02-19 at 12:27 +0100, Janusz Dziedzic wrote:
> On 19 February 2014 12:03, Johannes Berg <johannes@sipsolutions.net> wrote:
> > On Wed, 2014-02-12 at 19:54 +0100, Janusz Dziedzic wrote:
> >
> >> -#define REG_RULE(start, end, bw, gain, eirp, reg_flags) \
> >> -{                                                    \
> >> -     .freq_range.start_freq_khz = MHZ_TO_KHZ(start), \
> >> -     .freq_range.end_freq_khz = MHZ_TO_KHZ(end),     \
> >> -     .freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw), \
> >> -     .power_rule.max_antenna_gain = DBI_TO_MBI(gain),\
> >> -     .power_rule.max_eirp = DBM_TO_MBM(eirp),        \
> >> -     .flags = reg_flags,                             \
> >> +#define REG_RULE_EXT(start, end, bw, gain, eirp, dfs_cac, reg_flags) \
> >> +{                                                                    \
> >> +     .freq_range.start_freq_khz = MHZ_TO_KHZ(start),                 \
> >> +     .freq_range.end_freq_khz = MHZ_TO_KHZ(end),                     \
> >> +     .freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw),                 \
> >> +     .power_rule.max_antenna_gain = DBI_TO_MBI(gain),                \
> >> +     .power_rule.max_eirp = DBM_TO_MBM(eirp),                        \
> >> +     .flags = reg_flags,                                             \
> >> +     .dfs_cac_ms = dfs_cac,                                          \
> >>  }
> >>
> >> +#define REG_RULE(start, end, bw, gain, eirp, reg_flags) \
> >> +     REG_RULE_EXT(start, end, bw, gain, eirp, 0, reg_flags)
> >
> > I don't see any users of this, is that really useful?
> >
> 
> genregdb.awk using this when internal regdb is used.

Ah ok, thanks.

johannes

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Luis R. Rodriguez Feb. 19, 2014, 10:11 p.m. UTC | #5
On Wed, Feb 19, 2014 at 3:28 AM, Johannes Berg
<johannes@sipsolutions.net> wrote:
> Ah ok, thanks.

Let me know if you merge this so I can apply and push the respective
patch for CRDA. Not sure if you saw but as proposed this means this
bumps the wireless-regdb format. Janusz, I see you submitted a
respective patch for iw and CRDA but not one for wireless-regdb, we'll
need one there in order to parse the new entries in db.txt.

John, this should mean we will need two releases for wireless-regdb
moving on so that older versions of CRDA can read the new rules under
the old format. Another option may be to have wireless-regdb spit out
the file in two formats and we modify the new CRDA to jump to the new
format section if it supports it. Otherwise I'm afraid we get stuck
with the latest rules for old kernels forever, or we make a branch on
wireless-regdb for the old format.

Thoughts folks?

  Luis
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Janusz.Dziedzic@tieto.com Feb. 20, 2014, 7:12 a.m. UTC | #6
On 19 February 2014 23:11, Luis R. Rodriguez <mcgrof@do-not-panic.com> wrote:
> On Wed, Feb 19, 2014 at 3:28 AM, Johannes Berg
> <johannes@sipsolutions.net> wrote:
>> Ah ok, thanks.
>
> Let me know if you merge this so I can apply and push the respective
> patch for CRDA. Not sure if you saw but as proposed this means this
> bumps the wireless-regdb format. Janusz, I see you submitted a
> respective patch for iw and CRDA but not one for wireless-regdb, we'll
> need one there in order to parse the new entries in db.txt.
>
[PATCH] wireless-regdb: add DFS CAC time parameter

This one parse CAC time in wireless-regdb. I also change VERSION while
binary format was changed.
Didn't touch db.txt while will work as today (CAC = 0 - default CAC
time defined in mac80211).

> John, this should mean we will need two releases for wireless-regdb
> moving on so that older versions of CRDA can read the new rules under
> the old format. Another option may be to have wireless-regdb spit out
> the file in two formats and we modify the new CRDA to jump to the new
> format section if it supports it. Otherwise I'm afraid we get stuck
> with the latest rules for old kernels forever, or we make a branch on
> wireless-regdb for the old format.
>
We can allow VERSION=19 and VERSION=20, and handle this in CRDA
dynamically. This should be quite easy.

> Thoughts folks?
>
>   Luis
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Johannes Berg Feb. 21, 2014, 8:52 a.m. UTC | #7
On Wed, 2014-02-12 at 19:54 +0100, Janusz Dziedzic wrote:

> +++ b/include/net/regulatory.h
> @@ -155,6 +155,7 @@ struct ieee80211_reg_rule {
>  	struct ieee80211_freq_range freq_range;
>  	struct ieee80211_power_rule power_rule;
>  	u32 flags;
> +	u32 dfs_cac_ms;
>  };

Does that really have to be per channel? That's a significant investment
into bss size since we have a lot of channel structs.

> +	if (rule1->dfs_cac_ms > rule2->dfs_cac_ms)
> +		intersected_rule->dfs_cac_ms = rule1->dfs_cac_ms;
> +	else
> +		intersected_rule->dfs_cac_ms = rule2->dfs_cac_ms;

please just use max() or max_t() if needed (shouldn't be)

> @@ -2245,9 +2258,10 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
>  	const struct ieee80211_reg_rule *reg_rule = NULL;
>  	const struct ieee80211_freq_range *freq_range = NULL;
>  	const struct ieee80211_power_rule *power_rule = NULL;
> -	char bw[32];
> +	const int size = 32;
> +	char bw[size], cac_time[size];

I don't like that, size is const but it seems likely that sparse/smatch
may complain. I've already changed some of the below code to just use
sizeof(bw), please do that here as well.

johannes

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Janusz.Dziedzic@tieto.com Feb. 21, 2014, 9:47 a.m. UTC | #8
On 21 February 2014 09:52, Johannes Berg <johannes@sipsolutions.net> wrote:
> On Wed, 2014-02-12 at 19:54 +0100, Janusz Dziedzic wrote:
>
>> +++ b/include/net/regulatory.h
>> @@ -155,6 +155,7 @@ struct ieee80211_reg_rule {
>>       struct ieee80211_freq_range freq_range;
>>       struct ieee80211_power_rule power_rule;
>>       u32 flags;
>> +     u32 dfs_cac_ms;
>>  };
>
> Does that really have to be per channel? That's a significant investment
> into bss size since we have a lot of channel structs.
>
This seems easiest way to handle ETSI VHT80/40 case for channels with
different CAC time (eg):
VHT80:
- 116 (60s)
- 120 (600s)
- 124 (600s)
- 128 (600s)

VHT40/HT40:
- 132 (600s)
- 136 (60s)

>> +     if (rule1->dfs_cac_ms > rule2->dfs_cac_ms)
>> +             intersected_rule->dfs_cac_ms = rule1->dfs_cac_ms;
>> +     else
>> +             intersected_rule->dfs_cac_ms = rule2->dfs_cac_ms;
>
> please just use max() or max_t() if needed (shouldn't be)
>
OK

>> @@ -2245,9 +2258,10 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
>>       const struct ieee80211_reg_rule *reg_rule = NULL;
>>       const struct ieee80211_freq_range *freq_range = NULL;
>>       const struct ieee80211_power_rule *power_rule = NULL;
>> -     char bw[32];
>> +     const int size = 32;
>> +     char bw[size], cac_time[size];
>
> I don't like that, size is const but it seems likely that sparse/smatch
> may complain. I've already changed some of the below code to just use
> sizeof(bw), please do that here as well.
>
OK

BR
Janusz
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Johannes Berg Feb. 21, 2014, 9:55 a.m. UTC | #9
On Fri, 2014-02-21 at 10:47 +0100, Janusz Dziedzic wrote:
> On 21 February 2014 09:52, Johannes Berg <johannes@sipsolutions.net> wrote:
> > On Wed, 2014-02-12 at 19:54 +0100, Janusz Dziedzic wrote:
> >
> >> +++ b/include/net/regulatory.h
> >> @@ -155,6 +155,7 @@ struct ieee80211_reg_rule {
> >>       struct ieee80211_freq_range freq_range;
> >>       struct ieee80211_power_rule power_rule;
> >>       u32 flags;
> >> +     u32 dfs_cac_ms;
> >>  };
> >
> > Does that really have to be per channel? That's a significant investment
> > into bss size since we have a lot of channel structs.
> >
> This seems easiest way to handle ETSI VHT80/40 case for channels with
> different CAC time (eg):
> VHT80:
> - 116 (60s)
> - 120 (600s)
> - 124 (600s)
> - 128 (600s)
> 
> VHT40/HT40:
> - 132 (600s)
> - 136 (60s)

Huh, but you don't distinguish between channel widths in this whole
patchset?

johannes

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Janusz.Dziedzic@tieto.com Feb. 21, 2014, 10:09 a.m. UTC | #10
On 21 February 2014 10:55, Johannes Berg <johannes@sipsolutions.net> wrote:
> On Fri, 2014-02-21 at 10:47 +0100, Janusz Dziedzic wrote:
>> On 21 February 2014 09:52, Johannes Berg <johannes@sipsolutions.net> wrote:
>> > On Wed, 2014-02-12 at 19:54 +0100, Janusz Dziedzic wrote:
>> >
>> >> +++ b/include/net/regulatory.h
>> >> @@ -155,6 +155,7 @@ struct ieee80211_reg_rule {
>> >>       struct ieee80211_freq_range freq_range;
>> >>       struct ieee80211_power_rule power_rule;
>> >>       u32 flags;
>> >> +     u32 dfs_cac_ms;
>> >>  };
>> >
>> > Does that really have to be per channel? That's a significant investment
>> > into bss size since we have a lot of channel structs.
>> >
>> This seems easiest way to handle ETSI VHT80/40 case for channels with
>> different CAC time (eg):
>> VHT80:
>> - 116 (60s)
>> - 120 (600s)
>> - 124 (600s)
>> - 128 (600s)
>>
>> VHT40/HT40:
>> - 132 (600s)
>> - 136 (60s)
>
> Huh, but you don't distinguish between channel widths in this whole
> patchset?
>
In patch  cfg80211: DFS get CAC time from regulatory I check whole
chandef and get max channel cac value.

BR
Janusz
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Johannes Berg Feb. 21, 2014, 10:11 a.m. UTC | #11
On Fri, 2014-02-21 at 11:09 +0100, Janusz Dziedzic wrote:
> On 21 February 2014 10:55, Johannes Berg <johannes@sipsolutions.net> wrote:
> > On Fri, 2014-02-21 at 10:47 +0100, Janusz Dziedzic wrote:
> >> On 21 February 2014 09:52, Johannes Berg <johannes@sipsolutions.net> wrote:
> >> > On Wed, 2014-02-12 at 19:54 +0100, Janusz Dziedzic wrote:
> >> >
> >> >> +++ b/include/net/regulatory.h
> >> >> @@ -155,6 +155,7 @@ struct ieee80211_reg_rule {
> >> >>       struct ieee80211_freq_range freq_range;
> >> >>       struct ieee80211_power_rule power_rule;
> >> >>       u32 flags;
> >> >> +     u32 dfs_cac_ms;
> >> >>  };
> >> >
> >> > Does that really have to be per channel? That's a significant investment
> >> > into bss size since we have a lot of channel structs.
> >> >
> >> This seems easiest way to handle ETSI VHT80/40 case for channels with
> >> different CAC time (eg):
> >> VHT80:
> >> - 116 (60s)
> >> - 120 (600s)
> >> - 124 (600s)
> >> - 128 (600s)
> >>
> >> VHT40/HT40:
> >> - 132 (600s)
> >> - 136 (60s)
> >
> > Huh, but you don't distinguish between channel widths in this whole
> > patchset?
> >
> In patch  cfg80211: DFS get CAC time from regulatory I check whole
> chandef and get max channel cac value.

Oh, right, I misunderstood your other email.

johannes

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Luis R. Rodriguez Feb. 22, 2014, 12:31 a.m. UTC | #12
On Wed, Feb 19, 2014 at 11:12 PM, Janusz Dziedzic
<janusz.dziedzic@tieto.com> wrote:
> We can allow VERSION=19 and VERSION=20, and handle this in CRDA
> dynamically. This should be quite easy.

My point was what about older versions of CRDA that get a new
wireless-regdb tossed onto the system?

  Luis
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 9f90554..e1ea2ed 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -151,6 +151,7 @@  enum ieee80211_channel_flags {
  * @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.
+ * @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels.
  */
 struct ieee80211_channel {
 	enum ieee80211_band band;
@@ -165,6 +166,7 @@  struct ieee80211_channel {
 	int orig_mag, orig_mpwr;
 	enum nl80211_dfs_state dfs_state;
 	unsigned long dfs_state_entered;
+	unsigned int dfs_cac_ms;
 };
 
 /**
diff --git a/include/net/regulatory.h b/include/net/regulatory.h
index b07cdc9..75fc1f5 100644
--- a/include/net/regulatory.h
+++ b/include/net/regulatory.h
@@ -155,6 +155,7 @@  struct ieee80211_reg_rule {
 	struct ieee80211_freq_range freq_range;
 	struct ieee80211_power_rule power_rule;
 	u32 flags;
+	u32 dfs_cac_ms;
 };
 
 struct ieee80211_regdomain {
@@ -172,14 +173,18 @@  struct ieee80211_regdomain {
 #define DBM_TO_MBM(gain) ((gain) * 100)
 #define MBM_TO_DBM(gain) ((gain) / 100)
 
-#define REG_RULE(start, end, bw, gain, eirp, reg_flags) \
-{							\
-	.freq_range.start_freq_khz = MHZ_TO_KHZ(start),	\
-	.freq_range.end_freq_khz = MHZ_TO_KHZ(end),	\
-	.freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw),	\
-	.power_rule.max_antenna_gain = DBI_TO_MBI(gain),\
-	.power_rule.max_eirp = DBM_TO_MBM(eirp),	\
-	.flags = reg_flags,				\
+#define REG_RULE_EXT(start, end, bw, gain, eirp, dfs_cac, reg_flags)	\
+{									\
+	.freq_range.start_freq_khz = MHZ_TO_KHZ(start),			\
+	.freq_range.end_freq_khz = MHZ_TO_KHZ(end),			\
+	.freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw),			\
+	.power_rule.max_antenna_gain = DBI_TO_MBI(gain),		\
+	.power_rule.max_eirp = DBM_TO_MBM(eirp),			\
+	.flags = reg_flags,						\
+	.dfs_cac_ms = dfs_cac,						\
 }
 
+#define REG_RULE(start, end, bw, gain, eirp, reg_flags) \
+	REG_RULE_EXT(start, end, bw, gain, eirp, 0, reg_flags)
+
 #endif
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index ba1f762..353d61c6 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2329,6 +2329,7 @@  enum nl80211_band_attr {
  * @NL80211_FREQUENCY_ATTR_NO_160MHZ: any 160 MHz (but not 80+80) channel
  *	using this channel as the primary or any of the secondary channels
  *	isn't possible
+ * @NL80211_FREQUENCY_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
  * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
  *	currently defined
  * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
@@ -2347,6 +2348,7 @@  enum nl80211_frequency_attr {
 	NL80211_FREQUENCY_ATTR_NO_HT40_PLUS,
 	NL80211_FREQUENCY_ATTR_NO_80MHZ,
 	NL80211_FREQUENCY_ATTR_NO_160MHZ,
+	NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
 
 	/* keep last */
 	__NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -2446,6 +2448,8 @@  enum nl80211_reg_type {
  * 	If you don't have one then don't send this.
  * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for
  * 	a given frequency range. The value is in mBm (100 * dBm).
+ * @NL80211_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+ *	If not present or 0 default CAC time will be used.
  * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number
  *	currently defined
  * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use
@@ -2461,6 +2465,8 @@  enum nl80211_reg_rule_attr {
 	NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
 	NL80211_ATTR_POWER_RULE_MAX_EIRP,
 
+	NL80211_ATTR_DFS_CAC_TIME,
+
 	/* keep last */
 	__NL80211_REG_RULE_ATTR_AFTER_LAST,
 	NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 1797864..0bf69da 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -592,6 +592,10 @@  static int nl80211_msg_put_channel(struct sk_buff *msg,
 			if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME,
 					time))
 				goto nla_put_failure;
+			if (nla_put_u32(msg,
+					NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
+					chan->dfs_cac_ms))
+				goto nla_put_failure;
 		}
 	}
 
@@ -4612,6 +4616,7 @@  static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] =
 	[NL80211_ATTR_FREQ_RANGE_MAX_BW]	= { .type = NLA_U32 },
 	[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]	= { .type = NLA_U32 },
 	[NL80211_ATTR_POWER_RULE_MAX_EIRP]	= { .type = NLA_U32 },
+	[NL80211_ATTR_DFS_CAC_TIME]		= { .type = NLA_U32 },
 };
 
 static int parse_reg_rule(struct nlattr *tb[],
@@ -4646,6 +4651,10 @@  static int parse_reg_rule(struct nlattr *tb[],
 		power_rule->max_antenna_gain =
 			nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
 
+	if (tb[NL80211_ATTR_DFS_CAC_TIME])
+		reg_rule->dfs_cac_ms =
+			nla_get_u32(tb[NL80211_ATTR_DFS_CAC_TIME]);
+
 	return 0;
 }
 
@@ -5133,7 +5142,9 @@  static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
 		    nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
 				power_rule->max_antenna_gain) ||
 		    nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
-				power_rule->max_eirp))
+				power_rule->max_eirp) ||
+		    nla_put_u32(msg, NL80211_ATTR_DFS_CAC_TIME,
+				reg_rule->dfs_cac_ms))
 			goto nla_put_failure_rcu;
 
 		nla_nest_end(msg, nl_reg_rule);
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 27c5253..c80f20e 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -755,6 +755,11 @@  static int reg_rules_intersect(const struct ieee80211_regdomain *rd1,
 
 	intersected_rule->flags = rule1->flags | rule2->flags;
 
+	if (rule1->dfs_cac_ms > rule2->dfs_cac_ms)
+		intersected_rule->dfs_cac_ms = rule1->dfs_cac_ms;
+	else
+		intersected_rule->dfs_cac_ms = rule2->dfs_cac_ms;
+
 	if (!is_valid_reg_rule(intersected_rule))
 		return -EINVAL;
 
@@ -1067,6 +1072,14 @@  static void handle_channel(struct wiphy *wiphy,
 		min_t(int, chan->orig_mag,
 		      MBI_TO_DBI(power_rule->max_antenna_gain));
 	chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp);
+
+	if (chan->flags & IEEE80211_CHAN_RADAR) {
+		if (reg_rule->dfs_cac_ms)
+			chan->dfs_cac_ms = reg_rule->dfs_cac_ms;
+		else
+			chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+	}
+
 	if (chan->orig_mpwr) {
 		/*
 		 * Devices that use REGULATORY_COUNTRY_IE_FOLLOW_POWER
@@ -2245,9 +2258,10 @@  static void print_rd_rules(const struct ieee80211_regdomain *rd)
 	const struct ieee80211_reg_rule *reg_rule = NULL;
 	const struct ieee80211_freq_range *freq_range = NULL;
 	const struct ieee80211_power_rule *power_rule = NULL;
-	char bw[32];
+	const int size = 32;
+	char bw[size], cac_time[size];
 
-	pr_info("  (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp)\n");
+	pr_info("  (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp), (dfs_cac_time)\n");
 
 	for (i = 0; i < rd->n_reg_rules; i++) {
 		reg_rule = &rd->reg_rules[i];
@@ -2255,29 +2269,38 @@  static void print_rd_rules(const struct ieee80211_regdomain *rd)
 		power_rule = &reg_rule->power_rule;
 
 		if (!freq_range->max_bandwidth_khz)
-			snprintf(bw, 32, "%d KHz, AUTO",
+			snprintf(bw, size, "%d KHz, AUTO",
 				 reg_get_max_bandwidth(rd, reg_rule));
 		else
-			snprintf(bw, 32, "%d KHz",
+			snprintf(bw, size, "%d KHz",
 				 freq_range->max_bandwidth_khz);
 
+		if (reg_rule->flags & NL80211_RRF_DFS)
+			scnprintf(cac_time, size, "%u ms",
+				  reg_rule->dfs_cac_ms);
+		else
+			scnprintf(cac_time, size, "N/A");
+
+
 		/*
 		 * There may not be documentation for max antenna gain
 		 * in certain regions
 		 */
 		if (power_rule->max_antenna_gain)
-			pr_info("  (%d KHz - %d KHz @ %s), (%d mBi, %d mBm)\n",
+			pr_info("  (%d KHz - %d KHz @ %s), (%d mBi, %d mBm), (%s)\n",
 				freq_range->start_freq_khz,
 				freq_range->end_freq_khz,
 				bw,
 				power_rule->max_antenna_gain,
-				power_rule->max_eirp);
+				power_rule->max_eirp,
+				cac_time);
 		else
-			pr_info("  (%d KHz - %d KHz @ %s), (N/A, %d mBm)\n",
+			pr_info("  (%d KHz - %d KHz @ %s), (N/A, %d mBm), (%s)\n",
 				freq_range->start_freq_khz,
 				freq_range->end_freq_khz,
 				bw,
-				power_rule->max_eirp);
+				power_rule->max_eirp,
+				cac_time);
 	}
 }