2011-02-09 12:02:01

by Zefir Kurtisi

[permalink] [raw]
Subject: [RFC] DFS userspace handler

Hello DFS folks,

this is a patch series to prove the concept of DFS handling done in userspace.

It moves the radar pattern detector introduced in [1] and documented in [2]
from mac80211 up to hostapd.

The basic concept is:
1) pattern detection
* pulse events detected by HW are passed to hostapd
* hostapd performs per-wiphy DFS pattern matching
2) channel handling: on radar detection hostapd
* updates channel state (NOL handling)
* switches to new operating channel with TX disabled (on all wiphys)
* after CAC period enables TX on that channel

The proposed patches provide part 1) of the concept. The nl80211 interface is
extended with functions to pass DFS pulse events to hostapd.

Testing as described in [2] results in identical output, indicating that both
approaches (mac80211 and hostapd based) are equivalent from functional point
of view.

The testing was done with
* OpenWRT r25417
* Atheros AR9280 Rev:2
* DFS radars generated with R&S vector signal generator

We at our company would prefer this approach over the mac80211 based one as
soon as we see that part 2) can be implemented as proposed.



Cheers
Zefir



[1] http://article.gmane.org/gmane.linux.kernel.wireless.general/61868
[2] http://linuxwireless.org/en/developers/DFS/DetectorDesignNt
---
--
1.7.1



2011-02-09 15:18:20

by Vasanth Thiagarajan

[permalink] [raw]
Subject: RE: [PATCH 2/2] ath9k: pulse detection


+ /* update 64-bit time stamp with that of rs */
+ /* TODO: do this per-wiphy */
+ ts = rs->rs_tstamp;
+ if (ts <= (last_ts & 0xffffffff))

last_ts is not initialized.
+ ts_hi++;
+ last_ts = ((u64)ts_hi << 32) | ts;
+
+
+ if (rs->rs_datalen != 0) {
+ char *vdata = (char *)ds->ds_vdata + rs->rs_datalen - 3;
+ dur = (u32) vdata[0];
+ }
+
+ /* ZKU: reverse measured scaling factor of 2/3 for duration */

ZKU ??

Vasanth

2011-02-09 12:02:06

by Zefir Kurtisi

[permalink] [raw]
Subject: [PATCH 2/3] hostapd: nl80211 interface

Interface implementation to process DFS pulse events passed through nl80211.

Includes
* update nl80211_copy.h
* parsing of NL80211_CMD_SET_DFS_EVENT messages and forwarding it to detector
---
src/drivers/driver_nl80211.c | 55 ++++++++++++++++++++++++
src/drivers/nl80211_copy.h | 95 ++++++++++++++++++++++++++++++++++++------
2 files changed, 137 insertions(+), 13 deletions(-)

diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index afb72a6..3707673 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -41,6 +41,12 @@
#include "rfkill.h"
#include "driver.h"

+#ifdef CONFIG_DFS_DETECTOR
+#include "dfs/dfs.h"
+#endif
+
+
+
#ifdef CONFIG_LIBNL20
/* libnl 2.0 compatibility code */
#define nl_handle nl_sock
@@ -1298,6 +1304,52 @@ static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
wpa_supplicant_event(drv->ctx, EVENT_IBSS_RSN_START, &data);
}

+#ifdef CONFIG_DFS_DETECTOR
+static void nl80211_dfs_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ unsigned int if_index = 0;
+ struct nlattr *tp;
+ struct pulse_event event;
+
+ if (global_dfs_handler == NULL)
+ return;
+
+ memset(&event, 0, sizeof(event));
+
+ tp = tb[NL80211_ATTR_IFINDEX];
+ if (tp)
+ if_index = nla_get_u32(tp);
+ tp = tb[NL80211_DFSEVENT_ATTR_FREQ];
+ if (tp)
+ event.freq = nla_get_u16(tp);
+ tp = tb[NL80211_DFSEVENT_ATTR_TS];
+ if (tp)
+ event.ts = nla_get_u64(tp);
+ tp = tb[NL80211_DFSEVENT_ATTR_WIDTH];
+ if (tp)
+ event.width = nla_get_u8(tp);
+
+ wpa_printf(MSG_ERROR, "nl80211_dfs_event: if_index=%d, freq=%d, "
+ "ts=%llu, width=%d)\n", if_index,
+ event.freq, event.ts, event.width);
+
+ /* TBD: handle pulse per device based on if_index */
+ if (global_dfs_handler->add_pulse(global_dfs_handler, &event)) {
+ wpa_printf(MSG_ERROR, "******** radar detected on freq %d\n",
+ event.freq);
+ }
+
+}
+#else
+static void nl80211_dfs_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ return;
+}
+#endif
+
+

static int process_event(struct nl_msg *msg, void *arg)
{
@@ -1414,6 +1466,9 @@ static int process_event(struct nl_msg *msg, void *arg)
case NL80211_CMD_NEW_STATION:
nl80211_new_station_event(drv, tb);
break;
+ case NL80211_CMD_SET_DFS_EVENT:
+ nl80211_dfs_event(drv, tb);
+ break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
"(cmd=%d)", gnlh->cmd);
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 7483a89..ae3f6ae 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -148,6 +148,10 @@
* @NL80211_CMD_SET_MPATH: Set mesh path attributes for mesh path to
* destination %NL80211_ATTR_MAC on the interface identified by
* %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_NEW_MPATH: Create a new mesh path for the destination given by
+ * %NL80211_ATTR_MAC via %NL80211_ATTR_MPATH_NEXT_HOP.
+ * @NL80211_CMD_DEL_MPATH: Delete a mesh path to the destination given by
+ * %NL80211_ATTR_MAC.
* @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the
* the interface identified by %NL80211_ATTR_IFINDEX.
* @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC
@@ -172,10 +176,10 @@
* to the specified ISO/IEC 3166-1 alpha2 country code. The core will
* store this as a valid request and then query userspace for it.
*
- * @NL80211_CMD_GET_MESH_PARAMS: Get mesh networking properties for the
+ * @NL80211_CMD_GET_MESH_CONFIG: Get mesh networking properties for the
* interface identified by %NL80211_ATTR_IFINDEX
*
- * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the
+ * @NL80211_CMD_SET_MESH_CONFIG: Set mesh networking properties for the
* interface identified by %NL80211_ATTR_IFINDEX
*
* @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The
@@ -448,8 +452,8 @@ enum nl80211_commands {
NL80211_CMD_SET_REG,
NL80211_CMD_REQ_SET_REG,

- NL80211_CMD_GET_MESH_PARAMS,
- NL80211_CMD_SET_MESH_PARAMS,
+ NL80211_CMD_GET_MESH_CONFIG,
+ NL80211_CMD_SET_MESH_CONFIG,

NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */,

@@ -520,6 +524,8 @@ enum nl80211_commands {

/* add new commands above here */

+ NL80211_CMD_SET_DFS_EVENT,
+
/* used to define NL80211_CMD_MAX below */
__NL80211_CMD_AFTER_LAST,
NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1
@@ -538,6 +544,10 @@ enum nl80211_commands {
#define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE
#define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT

+/* source-level API compatibility */
+#define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG
+#define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG
+
/**
* enum nl80211_attrs - nl80211 netlink attributes
*
@@ -608,7 +618,7 @@ enum nl80211_commands {
* consisting of a nested array.
*
* @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes).
- * @NL80211_ATTR_PLINK_ACTION: action to perform on the mesh peer link.
+ * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link.
* @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path.
* @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path
* info given for %NL80211_CMD_GET_MPATH, nested attribute described at
@@ -854,6 +864,12 @@ enum nl80211_commands {
* the hardware should not be configured to receive on this antenna.
* For a more detailed descripton see @NL80211_ATTR_WIPHY_ANTENNA_TX.
*
+ * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX: Bitmap of antennas which are available
+ * for configuration as TX antennas via the above parameters.
+ *
+ * @NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX: Bitmap of antennas which are available
+ * for configuration as RX antennas via the above parameters.
+ *
* @NL80211_ATTR_MCAST_RATE: Multicast tx rate (in 100 kbps) for IBSS
*
* @NL80211_ATTR_OFFCHANNEL_TX_OK: For management frame TX, the frame may be
@@ -868,6 +884,11 @@ enum nl80211_commands {
* attributes, specifying what a key should be set as default as.
* See &enum nl80211_key_default_types.
*
+ * @NL80211_ATTR_MESH_SETUP: Optional mesh setup parameters. These cannot be
+ * changed once the mesh is active.
+ * @NL80211_ATTR_MESH_CONFIG: Mesh configuration parameters, a nested attribute
+ * containing attributes from &enum nl80211_meshconf_params.
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -922,7 +943,7 @@ enum nl80211_attrs {
NL80211_ATTR_REG_ALPHA2,
NL80211_ATTR_REG_RULES,

- NL80211_ATTR_MESH_PARAMS,
+ NL80211_ATTR_MESH_CONFIG,

NL80211_ATTR_BSS_BASIC_RATES,

@@ -1050,14 +1071,24 @@ enum nl80211_attrs {

NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,

+ NL80211_ATTR_MESH_SETUP,
+
+ NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
+ NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
+
/* add attributes here, update the policy in nl80211.c */

+ NL80211_DFSEVENT_ATTR_FREQ,
+ NL80211_DFSEVENT_ATTR_TS,
+ NL80211_DFSEVENT_ATTR_WIDTH,
+
__NL80211_ATTR_AFTER_LAST,
NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
};

/* source-level API compatibility */
#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION
+#define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG

/*
* Allow user space programs to use #ifdef on new attributes by defining them
@@ -1206,8 +1237,6 @@ enum nl80211_rate_info {
* @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs)
* @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station)
* @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station)
- * @__NL80211_STA_INFO_AFTER_LAST: internal
- * @NL80211_STA_INFO_MAX: highest possible station info attribute
* @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
* @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
* containing info as possible, see &enum nl80211_sta_info_txrate.
@@ -1217,6 +1246,11 @@ enum nl80211_rate_info {
* @NL80211_STA_INFO_TX_RETRIES: total retries (u32, to this station)
* @NL80211_STA_INFO_TX_FAILED: total failed packets (u32, to this station)
* @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm)
+ * @NL80211_STA_INFO_LLID: the station's mesh LLID
+ * @NL80211_STA_INFO_PLID: the station's mesh PLID
+ * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station
+ * @__NL80211_STA_INFO_AFTER_LAST: internal
+ * @NL80211_STA_INFO_MAX: highest possible station info attribute
*/
enum nl80211_sta_info {
__NL80211_STA_INFO_INVALID,
@@ -1559,7 +1593,8 @@ enum nl80211_mntr_flags {
/**
* enum nl80211_meshconf_params - mesh configuration parameters
*
- * Mesh configuration parameters
+ * Mesh configuration parameters. These can be changed while the mesh is
+ * active.
*
* @__NL80211_MESHCONF_INVALID: internal use
*
@@ -1582,9 +1617,6 @@ enum nl80211_mntr_flags {
* @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh
* point.
*
- * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a
- * source mesh point for path selection elements.
- *
* @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically
* open peer links when we detect compatible mesh peers.
*
@@ -1609,7 +1641,10 @@ enum nl80211_mntr_flags {
* @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs)
* that it takes for an HWMP information element to propagate across the mesh
*
- * @NL80211_MESHCONF_ROOTMODE: whether root mode is enabled or not
+ * @NL80211_MESHCONF_HWMP_ROOTMODE: whether root mode is enabled or not
+ *
+ * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a
+ * source mesh point for path selection elements.
*
* @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute
*
@@ -1639,6 +1674,40 @@ enum nl80211_meshconf_params {
};

/**
+ * enum nl80211_mesh_setup_params - mesh setup parameters
+ *
+ * Mesh setup parameters. These are used to start/join a mesh and cannot be
+ * changed while the mesh is active.
+ *
+ * @__NL80211_MESH_SETUP_INVALID: Internal use
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a
+ * vendor specific path selection algorithm or disable it to use the default
+ * HWMP.
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a
+ * vendor specific path metric or disable it to use the default Airtime
+ * metric.
+ *
+ * @NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE: A vendor specific information
+ * element that vendors will use to identify the path selection methods and
+ * metrics in use.
+ *
+ * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number
+ * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use
+ */
+enum nl80211_mesh_setup_params {
+ __NL80211_MESH_SETUP_INVALID,
+ NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL,
+ NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC,
+ NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE,
+
+ /* keep last */
+ __NL80211_MESH_SETUP_ATTR_AFTER_LAST,
+ NL80211_MESH_SETUP_ATTR_MAX = __NL80211_MESH_SETUP_ATTR_AFTER_LAST - 1
+};
+
+/**
* enum nl80211_txq_attr - TX queue parameter attributes
* @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved
* @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*)
--
1.7.1


2011-02-09 12:02:01

by Zefir Kurtisi

[permalink] [raw]
Subject: [PATCH 2/2] ath9k: pulse detection

Activate DFS pulse detection in ath9k and report pulse events to mac80211
to be passed through nl80211.
---
drivers/net/wireless/ath/ath9k/mac.c | 54 +++++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath9k/main.c | 9 +++++
2 files changed, 63 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index c75d40f..f09a796 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -16,6 +16,7 @@

#include "hw.h"
#include "hw-ops.h"
+#include <net/cfg80211.h>

static void ath9k_hw_set_txq_interrupts(struct ath_hw *ah,
struct ath9k_tx_queue_info *qi)
@@ -624,6 +625,57 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q)
}
EXPORT_SYMBOL(ath9k_hw_resettxqueue);

+/*
+ * DFS: check PHY-error for radar pulse and feed the detector
+ */
+void ath9k_dfs_process_phyerr(struct ath_hw *ah, struct ath_desc *ds,
+ struct ath_rx_status *rs, u_int64_t fulltsf)
+{
+ /* fulltsf is zero => simulate monotonic time based on rs->rs_tstamp */
+ static u64 last_ts;
+ static u32 ts_hi;
+
+ struct ath9k_channel *chan = ah->curchan;
+ u8 rssi;
+ u32 dur = 0;
+ u32 ts;
+ struct wiphy *wiphy = NULL;
+
+ if ((!(rs->rs_phyerr != ATH9K_PHYERR_RADAR))
+ && (!(rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT))) {
+ printk(KERN_INFO "Error: rs_phyer=0x%x not a radar error\n",
+ rs->rs_phyerr);
+ return;
+ }
+
+ /* we need a valid wiphy to report per-wiphy pulses */
+ if (ah->common.hw == NULL)
+ return;
+
+ rssi = (u8) rs->rs_rssi_ctl0;
+ if (rssi == 0)
+ return;
+
+ /* update 64-bit time stamp with that of rs */
+ /* TODO: do this per-wiphy */
+ ts = rs->rs_tstamp;
+ if (ts <= (last_ts & 0xffffffff))
+ ts_hi++;
+ last_ts = ((u64)ts_hi << 32) | ts;
+
+
+ if (rs->rs_datalen != 0) {
+ char *vdata = (char *)ds->ds_vdata + rs->rs_datalen - 3;
+ dur = (u32) vdata[0];
+ }
+
+ /* ZKU: reverse measured scaling factor of 2/3 for duration */
+ dur = (dur * 66 + 50) / 100;
+
+ wiphy = ah->common.hw->wiphy;
+ ieee80211_add_radar_pulse(wiphy, chan->channel, last_ts - dur, dur);
+}
+
int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
struct ath_rx_status *rs, u64 tsf)
{
@@ -702,6 +754,8 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
rs->rs_status |= ATH9K_RXERR_PHY;
phyerr = MS(ads.ds_rxstatus8, AR_PHYErrCode);
rs->rs_phyerr = phyerr;
+ /* DFS: feed assumed radar pulse */
+ ath9k_dfs_process_phyerr(ah, ds, rs, tsf);
} else if (ads.ds_rxstatus8 & AR_DecryptCRCErr)
rs->rs_status |= ATH9K_RXERR_DECRYPT;
else if (ads.ds_rxstatus8 & AR_MichaelErr)
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 4ed43b2..746532a 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -289,6 +289,15 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
ath_start_ani(common);
}

+ /**
+ * enable radar pulse detection
+ *
+ * TODO: do this only for DFS channels
+ */
+ ah->private_ops.set_radar_params(ah, &ah->radar_conf);
+ ath9k_hw_setrxfilter(ah,
+ ath9k_hw_getrxfilter(ah) | ATH9K_RX_FILTER_PHYRADAR);
+
ps_restore:
ieee80211_wake_queues(hw);

--
1.7.1


2011-02-14 15:54:05

by Zefir Kurtisi

[permalink] [raw]
Subject: Re: [PATCH 1/2] nl80211: interface update

On 02/14/2011 11:20 AM, Johannes Berg wrote:
> I'm thinking this is [RFC] really?
>
>> @@ -1076,6 +1078,10 @@ enum nl80211_attrs {
>>
>> /* add attributes here, update the policy in nl80211.c */
>>
>> + NL80211_DFSEVENT_ATTR_FREQ,
>> + NL80211_DFSEVENT_ATTR_TS,
>> + NL80211_DFSEVENT_ATTR_WIDTH,
>
> I think it would be preferable to create a new DFS_EVENT attribute (also
> note that all attributse should be prefixed by NL80211_ATTR_) and then
> nest data into it.
>
> However, why is the TS/width data needed at all?
>
> johannes
>
Thanks for the review.

I will follow Your suggestions in case we decided to take the approach of
DFS handling done in userspace (we most probably will not).

FYI, TS and width are the attributes of radar pulse events that a DFS pattern
detector is fed with. In fact, the width is inaccurate (e.g. with Atheros
chipsets) for most test patterns, so you basically have a series of time
stamps to do the pattern matching.



Cheers
Zefir



2011-02-09 15:32:50

by Zefir Kurtisi

[permalink] [raw]
Subject: Re: [PATCH 1/2] nl80211: interface update

On 02/09/2011 03:31 PM, John W. Linville wrote:
> On Wed, Feb 09, 2011 at 01:01:41PM +0100, Zefir Kurtisi wrote:
>> Interface implementation to pass DFS pulse events via nl80211.
>>
>> Includes
>> * new command NL80211_CMD_SET_DFS_EVENT
>> * new attributes NL80211_DFSEVENT_ATTR_{FREQ,TS,WIDTH}
>> * functions to pass DFS pulse events through netlink interface
>
>> diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
>> index 864ddfb..8e75f40 100644
>> --- a/net/wireless/nl80211.c
>> +++ b/net/wireless/nl80211.c
>> @@ -4015,7 +4015,6 @@ __cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
>> skb = nlmsg_new(approxlen + 100, gfp);
>> if (!skb)
>> return NULL;
>> -
>> hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
>> if (!hdr) {
>> kfree_skb(skb);
>
> Please don't randomly remove whitespace in your patches. :-)
>
Sure. No idea how this could ever happen ;)
> John


2011-02-09 15:30:42

by Zefir Kurtisi

[permalink] [raw]
Subject: Re: [PATCH 2/2] ath9k: pulse detection

On 02/09/2011 04:15 PM, Vasanth Thiagarajan wrote:
>
> + /* update 64-bit time stamp with that of rs */
> + /* TODO: do this per-wiphy */
> + ts = rs->rs_tstamp;
> + if (ts <= (last_ts & 0xffffffff))
>
> last_ts is not initialized.
true, but does not really matter since only deltas are relevant.

> + ts_hi++;
> + last_ts = ((u64)ts_hi << 32) | ts;
> +
> +
> + if (rs->rs_datalen != 0) {
> + char *vdata = (char *)ds->ds_vdata + rs->rs_datalen - 3;
> + dur = (u32) vdata[0];
> + }
> +
> + /* ZKU: reverse measured scaling factor of 2/3 for duration */
>
> ZKU ??
>
thats me :)

Sorry letting you read the code. It is meant only to test the proposed
approach. I missed to change the prefix to RFC.

Zefir

> Vasanth--
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html


2011-02-09 17:51:32

by Jouni Malinen

[permalink] [raw]
Subject: Re: [RFC] DFS userspace handler

On Wed, Feb 09, 2011 at 05:09:49PM +0000, Chaoxing Lin wrote:
> Do we have to choose between hostapd and wpa_supplicant?

No, but it should be noted that either one of those is most likely going
to be present on the system anyway if any sort of security is used in
AP/WDS/mesh/IBSS/STA.

> Why not a new daemon, e.g. DFSd or radard? So that it can be used intuitively for all master or IBSS modes.

One more interface to define and implement (user space between that new
daemon and hostapd/wpa_supplicant).. I'm not strongly against this, but
I would expect it to increase the total amount of work needed.

--
Jouni Malinen PGP id EFC895FA

2011-02-09 12:02:05

by Zefir Kurtisi

[permalink] [raw]
Subject: [PATCH 3/3] hostapd: integrate DFS detection

A global DFS handler instance is allocated at startup and ready to be fed
with pulse events passed through nl80211.

Includes
* add src/dfs into hostapd
* enable DFS channels
* enable syslog output (to see reported DFS pattern matches)
---
hostapd/Makefile | 13 +++++++++++++
hostapd/main.c | 19 +++++++++++++++++++
src/Makefile | 2 ++
src/ap/hw_features.c | 3 +++
4 files changed, 37 insertions(+), 0 deletions(-)

diff --git a/hostapd/Makefile b/hostapd/Makefile
index 1331e64..b68e11e 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -53,6 +53,19 @@ OBJS += ../src/ap/wpa_auth_ie.o
OBJS += ../src/ap/preauth_auth.o
OBJS += ../src/ap/pmksa_cache_auth.o

+CONFIG_DFS_DETECTOR=y
+
+ifdef CONFIG_DFS_DETECTOR
+CFLAGS += -DCONFIG_DFS_DETECTOR
+OBJS += ../src/dfs/dfs_debug.o
+OBJS += ../src/dfs/dfs_handler.o
+OBJS += ../src/dfs/dfs_pattern_detector.o
+
+# enable syslog logging to see DFS pattern detector results
+CFLAGS += -DCONFIG_DEBUG_SYSLOG
+endif
+
+
NEED_RC4=y
NEED_AES=y
NEED_MD5=y
diff --git a/hostapd/main.c b/hostapd/main.c
index 635698a..42749ca 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -31,6 +31,12 @@
#include "dump_state.h"
#include "ctrl_iface.h"

+#ifdef CONFIG_DFS_DETECTOR
+#include "dfs/dfs.h"
+
+/* only one global DFS handler instance enough for proof-of-concept */
+struct dfs_handler *global_dfs_handler;
+#endif

extern int wpa_debug_level;
extern int wpa_debug_show_keys;
@@ -194,6 +200,10 @@ static struct hostapd_iface * hostapd_init(const char *config_file)
if (hapd_iface == NULL)
goto fail;

+#ifdef CONFIG_DEBUG_SYSLOG
+ wpa_debug_open_syslog();
+#endif
+
hapd_iface->init_complete = hostapd_init_complete;
hapd_iface->reload_config = hostapd_reload_config;
hapd_iface->config_read_cb = hostapd_config_read;
@@ -399,6 +409,9 @@ static int hostapd_global_init(struct hapd_interfaces *interfaces)
openlog("hostapd", 0, LOG_DAEMON);
#endif /* CONFIG_NATIVE_WINDOWS */

+#ifdef CONFIG_DFS_DETECTOR
+ global_dfs_handler = dfs_handler_init(DFS_ETSI_DOMAIN);
+#endif
return 0;
}

@@ -415,6 +428,12 @@ static void hostapd_global_deinit(void)
closelog();
#endif /* CONFIG_NATIVE_WINDOWS */

+#ifdef CONFIG_DFS_DETECTOR
+ if (global_dfs_handler) {
+ global_dfs_handler->exit(global_dfs_handler);
+ global_dfs_handler = NULL;
+ }
+#endif
eap_server_unregister_methods();

os_daemonize_terminate(pid_file);
diff --git a/src/Makefile b/src/Makefile
index d73a175..06aa85e 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,5 +1,7 @@
SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p radius rsn_supp tls utils wps

+SUBDIRS += dfs
+
all:
for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done

diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index f326183..3496388 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -80,12 +80,15 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
* since that (in addition to full DFS) is not yet
* supported.
*/
+#ifndef CONFIG_DFS_DETECTOR
+ /* do not disable if we have DFS detection */
if (feature->channels[j].flag &
(HOSTAPD_CHAN_NO_IBSS |
HOSTAPD_CHAN_PASSIVE_SCAN |
HOSTAPD_CHAN_RADAR))
feature->channels[j].flag |=
HOSTAPD_CHAN_DISABLED;
+#endif /* CONFIG_DFS_DETECTOR */
if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED)
continue;
wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
--
1.7.1


2011-02-09 12:02:03

by Zefir Kurtisi

[permalink] [raw]
Subject: [PATCH 1/2] nl80211: interface update

Interface implementation to pass DFS pulse events via nl80211.

Includes
* new command NL80211_CMD_SET_DFS_EVENT
* new attributes NL80211_DFSEVENT_ATTR_{FREQ,TS,WIDTH}
* functions to pass DFS pulse events through netlink interface
---
include/linux/nl80211.h | 6 +++++
include/net/cfg80211.h | 6 +++++
net/wireless/core.c | 11 ++++++++++
net/wireless/nl80211.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++-
net/wireless/nl80211.h | 3 ++
5 files changed, 73 insertions(+), 1 deletions(-)

diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 821ffb9..ae3f6ae 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -524,6 +524,8 @@ enum nl80211_commands {

/* add new commands above here */

+ NL80211_CMD_SET_DFS_EVENT,
+
/* used to define NL80211_CMD_MAX below */
__NL80211_CMD_AFTER_LAST,
NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1
@@ -1076,6 +1078,10 @@ enum nl80211_attrs {

/* add attributes here, update the policy in nl80211.c */

+ NL80211_DFSEVENT_ATTR_FREQ,
+ NL80211_DFSEVENT_ATTR_TS,
+ NL80211_DFSEVENT_ATTR_WIDTH,
+
__NL80211_ATTR_AFTER_LAST,
NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
};
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 679a049..59ced7a 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -2715,6 +2715,12 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
void cfg80211_cqm_pktloss_notify(struct net_device *dev,
const u8 *peer, u32 num_packets, gfp_t gfp);

+
+/**
+ * DFS radar pulse reporting
+ */
+void ieee80211_add_radar_pulse(struct wiphy *wiphy, u16 freq, u64 ts, u8 width);
+
/* Logging, debugging and troubleshooting/diagnostic helpers. */

/* wiphy_printk helpers, similar to dev_printk */
diff --git a/net/wireless/core.c b/net/wireless/core.c
index fe01de2..4e62d37 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -944,3 +944,14 @@ static void __exit cfg80211_exit(void)
destroy_workqueue(cfg80211_wq);
}
module_exit(cfg80211_exit);
+
+
+/**
+ * DFS
+ */
+
+void ieee80211_add_radar_pulse(struct wiphy *wiphy, u16 freq, u64 ts, u8 width)
+{
+ return nl80211_send_dfs_event(wiphy, freq, ts, width);
+}
+EXPORT_SYMBOL(ieee80211_add_radar_pulse);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 864ddfb..8e75f40 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -4015,7 +4015,6 @@ __cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
skb = nlmsg_new(approxlen + 100, gfp);
if (!skb)
return NULL;
-
hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
if (!hdr) {
kfree_skb(skb);
@@ -6191,3 +6190,50 @@ void nl80211_exit(void)
netlink_unregister_notifier(&nl80211_netlink_notifier);
genl_unregister_family(&nl80211_fam);
}
+
+
+void nl80211_send_dfs_event(struct wiphy *wiphy, u16 freq, u64 ts, u8 width)
+{
+ struct sk_buff *msg;
+ void *hdr;
+ int wiphy_idx = 0;
+
+ if (wiphy == NULL) {
+ printk(KERN_WARNING "nl80211_send_dfs_event: wiphy=0\n");
+ return;
+ }
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_DFS_EVENT);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ wiphy_idx = get_wiphy_idx(wiphy);
+
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, wiphy_idx);
+ NLA_PUT_U16(msg, NL80211_DFSEVENT_ATTR_FREQ, freq);
+ NLA_PUT_U64(msg, NL80211_DFSEVENT_ATTR_TS, ts);
+ NLA_PUT_U8(msg, NL80211_DFSEVENT_ATTR_WIDTH, width);
+
+ if (genlmsg_end(msg, hdr) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ rcu_read_lock();
+ genlmsg_multicast_netns(wiphy_net(wiphy), msg, 0,
+ nl80211_regulatory_mcgrp.id, GFP_ATOMIC);
+ rcu_read_unlock();
+
+ return;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index e3f7fa8..3d3bbc4 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -98,4 +98,7 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *peer,
u32 num_packets, gfp_t gfp);

+void nl80211_send_dfs_event(struct wiphy *wiphy, u16 freq, u64 ts, u8 width);
+
+
#endif /* __NET_WIRELESS_NL80211_H */
--
1.7.1


2011-02-09 16:45:31

by John W. Linville

[permalink] [raw]
Subject: Re: [RFC] DFS userspace handler

On Wed, Feb 09, 2011 at 04:09:08PM +0000, Chaoxing Lin wrote:
> I would vote for a dedidate DFS daemon.
>
> Straightforward name is better.
> Beginners would be hard to understand why WDS/Mesh/IBSS mode requires a hostapd daemon which intuitively means for AP mode..

Perhaps it can be in wpa_supplicant instead?

John
--
John W. Linville Someday the world will need a hero, and you
[email protected] might be all we have. Be ready.

2011-02-09 14:45:27

by John W. Linville

[permalink] [raw]
Subject: Re: [RFC] DFS userspace handler

On Wed, Feb 09, 2011 at 01:01:40PM +0100, Zefir Kurtisi wrote:
> Hello DFS folks,
>
> this is a patch series to prove the concept of DFS handling done in userspace.
>
> It moves the radar pattern detector introduced in [1] and documented in [2]
> from mac80211 up to hostapd.

I'll let the smart people comment on the workability of this. :-)
But from a kernel interface standpoint it seems reasonable to me.

John
--
John W. Linville Someday the world will need a hero, and you
[email protected] might be all we have. Be ready.

2011-02-09 14:45:27

by John W. Linville

[permalink] [raw]
Subject: Re: [PATCH 1/2] nl80211: interface update

On Wed, Feb 09, 2011 at 01:01:41PM +0100, Zefir Kurtisi wrote:
> Interface implementation to pass DFS pulse events via nl80211.
>
> Includes
> * new command NL80211_CMD_SET_DFS_EVENT
> * new attributes NL80211_DFSEVENT_ATTR_{FREQ,TS,WIDTH}
> * functions to pass DFS pulse events through netlink interface

> diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
> index 864ddfb..8e75f40 100644
> --- a/net/wireless/nl80211.c
> +++ b/net/wireless/nl80211.c
> @@ -4015,7 +4015,6 @@ __cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
> skb = nlmsg_new(approxlen + 100, gfp);
> if (!skb)
> return NULL;
> -
> hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
> if (!hdr) {
> kfree_skb(skb);

Please don't randomly remove whitespace in your patches. :-)

John
--
John W. Linville Someday the world will need a hero, and you
[email protected] might be all we have. Be ready.

2011-02-09 17:09:51

by Chaoxing Lin

[permalink] [raw]
Subject: RE: [RFC] DFS userspace handler

Do we have to choose between hostapd and wpa_supplicant?

Why not a new daemon, e.g. DFSd or radard? So that it can be used intuitively for all master or IBSS modes.


-----Original Message-----
From: John W. Linville [mailto:[email protected]]
Sent: Wednesday, February 09, 2011 11:31 AM
To: Chaoxing Lin
Cc: 'Zefir Kurtisi'; [email protected]
Subject: Re: [RFC] DFS userspace handler

On Wed, Feb 09, 2011 at 04:09:08PM +0000, Chaoxing Lin wrote:
> I would vote for a dedidate DFS daemon.
>
> Straightforward name is better.
> Beginners would be hard to understand why WDS/Mesh/IBSS mode requires a hostapd daemon which intuitively means for AP mode..

Perhaps it can be in wpa_supplicant instead?

John
--
John W. Linville Someday the world will need a hero, and you
[email protected] might be all we have. Be ready.

2011-02-09 15:50:54

by Zefir Kurtisi

[permalink] [raw]
Subject: Re: [RFC] DFS userspace handler

On 02/09/2011 04:35 PM, Chaoxing Lin wrote:
> What about DFS in WDS / Mesh / IBSS mode if DFS is only implemented in hostapd?
>
> Will an instance of hostapd be required for all these modes, too?
>
> Thanks
>
Yes, with this approach you will need to have some userspace component that
evaluates the reported pulses and handles channels upon radar detection.

hostapd was taken as the first shot, but one might also think of a dedicated
DFS deamon that takes this role. Actually that is our intended gain of this
approach: having a defined interface for pulse events reporting and highest
flexibility wrt detection and handling.

Cheers

> -----Original Message-----
> From: [email protected] [mailto:[email protected]] On Behalf Of Zefir Kurtisi
> Sent: Wednesday, February 09, 2011 7:02 AM
> To: [email protected]
> Cc: Zefir Kurtisi
> Subject: [RFC] DFS userspace handler
>
> Hello DFS folks,
>
> this is a patch series to prove the concept of DFS handling done in userspace.
>
> It moves the radar pattern detector introduced in [1] and documented in [2]
> from mac80211 up to hostapd.
>
> The basic concept is:
> 1) pattern detection
> * pulse events detected by HW are passed to hostapd
> * hostapd performs per-wiphy DFS pattern matching
> 2) channel handling: on radar detection hostapd
> * updates channel state (NOL handling)
> * switches to new operating channel with TX disabled (on all wiphys)
> * after CAC period enables TX on that channel
>
> The proposed patches provide part 1) of the concept. The nl80211 interface is
> extended with functions to pass DFS pulse events to hostapd.
>
> Testing as described in [2] results in identical output, indicating that both
> approaches (mac80211 and hostapd based) are equivalent from functional point
> of view.
>
> The testing was done with
> * OpenWRT r25417
> * Atheros AR9280 Rev:2
> * DFS radars generated with R&S vector signal generator
>
> We at our company would prefer this approach over the mac80211 based one as
> soon as we see that part 2) can be implemented as proposed.
>
>
>
> Cheers
> Zefir
>
>
>
> [1] http://article.gmane.org/gmane.linux.kernel.wireless.general/61868
> [2] http://linuxwireless.org/en/developers/DFS/DetectorDesignNt
> ---


2011-02-09 12:02:08

by Zefir Kurtisi

[permalink] [raw]
Subject: [PATCH 1/3] hostapd: simple pattern detector

Provides a very basic pattern detector to test the proposed design.

Implements detection for ETSI 1.5.1 single PRI radar test signals. It is
usable only for off-channel-scans, i.e. does not tolerate false pulses.
---
src/dfs/Makefile | 8 +
src/dfs/dfs.h | 83 ++++++
src/dfs/dfs_debug.c | 15 +
src/dfs/dfs_debug.h | 110 ++++++++
src/dfs/dfs_handler.c | 109 ++++++++
src/dfs/dfs_pattern_detector.c | 568 ++++++++++++++++++++++++++++++++++++++++
src/dfs/dfs_pattern_detector.h | 46 ++++
src/dfs/dfs_radar_types.h | 48 ++++
8 files changed, 987 insertions(+), 0 deletions(-)
create mode 100644 src/dfs/Makefile
create mode 100644 src/dfs/dfs.h
create mode 100644 src/dfs/dfs_debug.c
create mode 100644 src/dfs/dfs_debug.h
create mode 100644 src/dfs/dfs_handler.c
create mode 100644 src/dfs/dfs_pattern_detector.c
create mode 100644 src/dfs/dfs_pattern_detector.h
create mode 100644 src/dfs/dfs_radar_types.h

diff --git a/src/dfs/Makefile b/src/dfs/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/src/dfs/Makefile
@@ -0,0 +1,8 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ rm -f *~ *.o *.d
+
+install:
+ @echo Nothing to be made.
diff --git a/src/dfs/dfs.h b/src/dfs/dfs.h
new file mode 100644
index 0000000..4ca71ec
--- /dev/null
+++ b/src/dfs/dfs.h
@@ -0,0 +1,83 @@
+#ifndef DFS_H
+#define DFS_H
+/*
+ * Copyright 2011, Neratec Solutions AG, <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/**
+ * DOC: Introduction
+ *
+ * DFS radar detector interface
+ *
+ * This is a proposal for a common DFS pattern detector interface.
+ *
+ * It should be used by devices that are able to detect radar pulses and need
+ * pattern matching (as defined by ETSI, FCC, JP regulatories).
+ *
+ */
+
+#include <stdint.h>
+
+/* TODO: move those to more common place */
+enum dfs_domain {
+ DFS_FCC_DOMAIN = 1, /* FCC dfs domain */
+ DFS_ETSI_DOMAIN = 2, /* ETSI dfs domain */
+ DFS_JP_DOMAIN = 3, /* Japan dfs domain */
+};
+
+/**
+ * struct pulse_event - events fed to the dfs handler
+ *
+ * @ts: absolute time stamp for start of pulse in [us] (e.g. as TSF)
+ * @freq: channel frequency in [MHz]
+ * @width: pulse width in [us]
+ *
+ */
+struct pulse_event {
+ uint64_t ts;
+ uint16_t freq;
+ uint8_t width;
+};
+
+
+/**
+ * struct dfs_handler - DFS handler pseudo-OO interface
+ *
+ * @exit: terminate DFS handler and release all resources
+ * @add_pulse: add given pulse event to detector lines
+ * returns 1 if added event triggered a pattern match
+ * @data: private instance data
+ *
+ */
+struct dfs_handler {
+ /* VFT */
+ void (*exit)(struct dfs_handler *_this);
+ int (*add_pulse)(struct dfs_handler *_this, struct pulse_event *event);
+
+ /* private data */
+ struct dfs_data *data;
+};
+
+/**
+ * dfs_handler_init - DFS handler constructor
+ *
+ * @dfs_domain: DFS domain to detect radar patterns for
+ *
+ * A DFS handler instance is allocated via this constructor.
+ * On success the pointer to the fully initialized handler is returned that
+ * can be fed with radar pulses during its lifetime. Allocated resources are
+ * released upon calling the destructor.
+ *
+ * On failure NULL is returned.
+ */
+struct dfs_handler *dfs_handler_init(enum dfs_domain dfs_domain);
+
+/* only one global DFS handler instance enough for proof-of-concept */
+extern struct dfs_handler *global_dfs_handler;
+
+
+#endif /* DFS_H */
diff --git a/src/dfs/dfs_debug.c b/src/dfs/dfs_debug.c
new file mode 100644
index 0000000..2cce48f
--- /dev/null
+++ b/src/dfs/dfs_debug.c
@@ -0,0 +1,15 @@
+#include "dfs_debug.h"
+
+#define USE_FULL_DEBUG 0
+
+uint32_t dfs_debug_level = 0
+ | DFS_DEBUG_ERROR
+ | DFS_DEBUG_WARN
+ | DFS_DEBUG_INFO
+#if USE_FULL_DEBUG
+ | DFS_DEBUG_TRACE
+ | DFS_DEBUG_LOG
+#endif
+;
+
+char dbg_buff[MAX_DEBUG_SPRINTF + 1] = {0};
diff --git a/src/dfs/dfs_debug.h b/src/dfs/dfs_debug.h
new file mode 100644
index 0000000..06f8f22
--- /dev/null
+++ b/src/dfs/dfs_debug.h
@@ -0,0 +1,110 @@
+#ifndef DFS_DEBUG_H
+#define DFS_DEBUG_H
+
+#include <stdint.h>
+
+#ifdef __KERNEL__
+#include <linux/kernel.h>
+
+#define DFS_DPRINTK(LEVEL, FMT, ...) \
+do { \
+ if ((LEVEL) & dfs_debug_level) \
+ printk(FMT, ##__VA_ARGS__); \
+} while (0)
+
+#define ASSERT(expr) \
+if (unlikely(!(expr))) { \
+ panic(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \
+ #expr, __SHORT_FILE__, __func__, __LINE__); \
+}
+#else
+#include <string.h>
+#include <stdio.h>
+
+#define DFS_DPRINTK(LEVEL, FMT, ...) \
+do { \
+ if ((LEVEL) & dfs_debug_level) \
+ printf(FMT, ##__VA_ARGS__); \
+} while (0)
+
+#define ASSERT(expr) \
+do { \
+ if (!(expr)) { \
+ printf("Assertion failed! %s,%s,%s,line=%d\n", \
+ #expr, __SHORT_FILE__, __func__, __LINE__); \
+ } \
+} while (0)
+
+#endif
+
+enum {
+ DFS_DEBUG_ERROR = 0x000100,
+ DFS_DEBUG_WARN = 0x000200,
+ DFS_DEBUG_INFO = 0x000400,
+ DFS_DEBUG_TRACE = 0x000800,
+ DFS_DEBUG_LOG = 0x001000,
+};
+
+extern uint32_t dfs_debug_level;
+#define MAX_DEBUG_SPRINTF 511
+extern char dbg_buff[MAX_DEBUG_SPRINTF + 1];
+
+#define __SHORT_FILE__ \
+ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
+
+
+
+#define DTRACE(...) \
+do { \
+ DFS_DPRINTK(DFS_DEBUG_TRACE, "TRACE: %s\n", __func__); \
+} while (0)
+
+#define DLOG(FMT, ...) \
+do { \
+ snprintf(dbg_buff, MAX_DEBUG_SPRINTF, "LOG: %s\n", FMT); \
+ DFS_DPRINTK(DFS_DEBUG_LOG, dbg_buff, ##__VA_ARGS__); \
+} while (0)
+
+#define DINFO(FMT, ...) \
+do { \
+ snprintf(dbg_buff, MAX_DEBUG_SPRINTF, "INFO: %s\n", FMT); \
+ DFS_DPRINTK(DFS_DEBUG_INFO, dbg_buff, ##__VA_ARGS__); \
+} while (0)
+
+#define DWARN(FMT, ...) \
+do { \
+ snprintf(dbg_buff, MAX_DEBUG_SPRINTF, \
+ "WARN: %s: %s\n", __func__, FMT); \
+ DFS_DPRINTK(DFS_DEBUG_WARN, dbg_buff, ##__VA_ARGS__); \
+} while (0)
+
+#define DERROR(FMT, ...) \
+do { \
+ snprintf(dbg_buff, MAX_DEBUG_SPRINTF, \
+ "WARN: %s: %s\n", __func__, FMT); \
+ DFS_DPRINTK(DFS_DEBUG_ERROR, dbg_buff, ##__VA_ARGS__); \
+} while (0)
+
+#define DFATAL(FMT, ...) \
+do { \
+ snprintf(dbg_buff, MAX_DEBUG_SPRINTF, \
+ "FATAL: %s: %s\n", __func__, FMT); \
+ DFS_DPRINTK(DFS_DEBUG_ERROR, dbg_buff, ##__VA_ARGS__); \
+} while (0)
+
+
+#define DINIT(FMT, ...) \
+do { \
+ snprintf(dbg_buff, MAX_DEBUG_SPRINTF, \
+ "INIT: %s: %s\n", __func__, FMT); \
+ DFS_DPRINTK(DFS_DEBUG_ERROR, dbg_buff, ##__VA_ARGS__); \
+} while (0)
+
+#define DINFO_OK(FMT, ...) \
+do { \
+ snprintf(dbg_buff, MAX_DEBUG_SPRINTF, "OK: %s: %s\n", __func__, FMT); \
+ DFS_DPRINTK(DFS_DEBUG_INFO, dbg_buff, ##__VA_ARGS__); \
+} while (0)
+
+
+#endif /* DFS_DEBUG_H */
diff --git a/src/dfs/dfs_handler.c b/src/dfs/dfs_handler.c
new file mode 100644
index 0000000..a13d45c
--- /dev/null
+++ b/src/dfs/dfs_handler.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2011, Neratec Solutions AG, <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <stdlib.h>
+
+#include "dfs.h"
+#include "dfs_debug.h"
+#include "dfs_pattern_detector.h"
+
+
+/**
+ * struct dfs_data - DFS handler private data
+ *
+ * @dfs_handler: instance back-reference
+ * @dfs_domain: DFS domain the handler is currently working
+ * @pattern_detector: instance reference to pattern detector
+ */
+struct dfs_data {
+ struct dfs_handler *dfs_handler;
+ enum dfs_domain dfs_domain;
+ struct dfs_pattern_detector *pattern_detector;
+};
+
+
+
+/* Destructor */
+static void dh_exit(struct dfs_handler *_this)
+{
+ ASSERT(_this != NULL);
+
+ if (_this->data != NULL) {
+ struct dfs_data *dfs_data = _this->data;
+ if (dfs_data->pattern_detector != NULL)
+ dfs_data->pattern_detector->
+ exit(dfs_data->pattern_detector);
+ free(dfs_data);
+ _this->data = NULL;
+ }
+ free(_this);
+}
+
+static int dh_add_pulse(struct dfs_handler *_this, struct pulse_event *event)
+{
+ int detector_result;
+ struct dfs_data *dfs_data;
+ DTRACE();
+ ASSERT((_this != NULL) && (_this->data != NULL));
+
+ dfs_data = _this->data;
+ detector_result = dfs_data->pattern_detector->
+ add_pulse(dfs_data->pattern_detector, event);
+ if (detector_result == RADAR_DETECTED) {
+ DINIT("found radar");
+ return 1;
+ }
+ return 0;
+}
+
+static struct dfs_handler default_dfs_handler = {
+ .exit = dh_exit,
+ .add_pulse = dh_add_pulse,
+};
+
+/* Constructor */
+struct dfs_handler *dfs_handler_init(enum dfs_domain dfs_domain)
+{
+ struct dfs_handler *_this;
+ struct dfs_data *dfs_data;
+ int sz = sizeof(struct dfs_handler);
+ _this = malloc(sz);
+
+ if (_this == NULL) {
+ DFATAL("dfs_handler allocation failed");
+ return NULL;
+ }
+
+ *_this = default_dfs_handler;
+
+
+ sz = sizeof(struct dfs_data);
+ dfs_data = malloc(sz);
+ if (dfs_data == NULL) {
+ DFATAL("dfs_data allocation failed");
+ goto failed;
+ }
+
+ memset(dfs_data, 0, sz);
+
+ _this->data = dfs_data;
+ dfs_data->pattern_detector = dfs_pattern_detector_init(dfs_domain);
+ if (dfs_data->pattern_detector == NULL) {
+ DFATAL("detector_init() failed!");
+ goto failed;
+ }
+ _this->data->dfs_domain = dfs_domain;
+ _this->data->dfs_handler = _this;
+
+ DINIT("ok");
+ return _this;
+
+failed:
+ _this->exit(_this);
+ return NULL;
+}
diff --git a/src/dfs/dfs_pattern_detector.c b/src/dfs/dfs_pattern_detector.c
new file mode 100644
index 0000000..d240796
--- /dev/null
+++ b/src/dfs/dfs_pattern_detector.c
@@ -0,0 +1,568 @@
+/*
+ * Copyright 2011, Neratec Solutions AG, <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <stdlib.h>
+
+#include "dfs_pattern_detector.h"
+#include "dfs_debug.h"
+#include "dfs.h"
+
+#include "dfs_radar_types.h"
+
+
+/*
+ * Abbreviations used (based on regulatory specs):
+ * * prf: pulse repetition frequency [Hz]
+ * * pri: pulse repetition interval = 1/prf, here used as [us]
+ * * ppb: pulses per burst
+ */
+
+
+#define DELTA(X, Y) ((X < Y) ? (Y-X) : (X-Y))
+
+/* number of deviation of radar time in usecs tolerated on both sides
+ * TODO: this might need to be HW-dependent
+ */
+#define MAX_PRI_TOLERANCE 10
+
+
+/**
+ * struct radar_specs - specifies a radar pattern type
+ *
+ * @type_id: pattern type, as defined by ETSI / FCC
+ * @width_min: minimum radar pulse width in [us]
+ * @width_max: maximum radar pulse width in [us]
+ * @pri_min: minimum pulse repetition interval in [us] (including tolerance)
+ * @pri_max: minimum pri in [us] (including tolerance)
+ * @num_pri: maximum number of different pri for this type
+ * @ppb: pulses per bursts for this type
+ * @ppb_thresh: number of pulses required to trigger detection
+ * @max_dur: absolute max duration of pattern: num_pri * pri_max * ppb
+ *
+ * Characteristics of each radar pattern type are calculated at initialization
+ * based on radar test signal types defined by the chosen regulatory.
+ * They remain unchanged thereafter.
+ */
+struct radar_specs {
+ unsigned int type_id;
+ unsigned int width_min;
+ unsigned int width_max;
+ unsigned int pri_min;
+ unsigned int pri_max;
+ unsigned int num_pri;
+ unsigned int ppb;
+ unsigned int ppb_thresh;
+ unsigned int max_dur;
+};
+
+
+/* so far, maximum prf number is 3 for ETSI types 5 and 6 */
+#define MAX_PRF_NUM 3
+
+/**
+ * struct radar_stats - detector statistics updated on each pulse
+ *
+ * @pri_count: number of pri used for this pattern type so far
+ * @pri: array of pris in use
+ * @matching_pulse_count: number of pulses detected correctly so far
+ * @missed_pulse_count: number of pulses assumed as lost so far
+ * @false_pulse_count: number of invalid / false pulses so far
+ * @first_ts: timestamp of first valid pulse for this type
+ * @last_ts: timestamp of last valid pulse for this type
+ *
+ * The statistics reflect the current state of the related detector line.
+ *
+ * Detection is performed in place updating the affected detector lines
+ * whenever a pulse is added. The algorithm operates without keeping track
+ * of the pulse history but requires only the statistics collected so far.
+ *
+ * Statistical decisions are made based on the numbers for matching,
+ * missed, and false pulses count.
+ */
+struct radar_stats {
+ uint32_t pri_count;
+ uint32_t pri[MAX_PRF_NUM];
+ uint32_t matching_pulse_count;
+ uint32_t missed_pulse_count;
+ uint32_t false_pulse_count;
+ uint64_t first_ts;
+ uint64_t last_ts;
+};
+
+/**
+ * struct dfs_channels - DFS channels' frequencies, assumed constant
+ */
+static const uint16_t dfs_channels[] = {
+ 5260, 5280, 5300, 5320,
+ 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680, 5700,
+};
+#define NUM_DFS_CHANNELS (sizeof(dfs_channels)/sizeof(dfs_channels[0]))
+
+
+/**
+ * struct detector_line - detector line for one specific dfs pattern type
+ *
+ * @specs: dfs pattern type specification
+ * @stats: array of statistics for for all DFS channels
+ *
+ * Each detector line consists of a constant radar type specification and
+ * an array of statistics for all DFS channels.
+ */
+struct detector_line {
+ struct radar_specs specs;
+ struct radar_stats stats[NUM_DFS_CHANNELS];
+};
+
+
+/**
+ * struct pattern_detector_data - private instance data
+ *
+ * @num_detector_elements: number of different radar types
+ * @radar_detectors: array of num_detector_elements detector lines
+ * @min_valid_width: combined min of valid pulse widths
+ * @max_valid_width: combined max of valid pulse widths
+ * @min_valid_pri: combined min of valid pris
+ * @max_valid_pri: combined max of valid pris
+ * @max_radar_dur: combined max duration of radar patterns
+ * @first_pulse_ts: timestamp of first pulse after detector reset
+ * @last_pulse_ts: timestamp of last valid pulse
+ * @last_pulse_rssi: rssi of last pulse
+ *
+ * For global range checking dfs_pattern_detector instances are initialized
+ * with a pre-calculated set of global limits that combine the limits of
+ * all detector lines.
+ */
+struct pattern_detector_data {
+ uint32_t num_detector_elements;
+ struct detector_line *radar_detectors;
+ uint32_t min_valid_width;
+ uint32_t max_valid_width;
+ uint32_t min_valid_pri;
+ uint32_t max_valid_pri;
+ uint32_t max_radar_dur;
+ uint64_t first_pulse_ts;
+ uint64_t last_pulse_ts;
+ uint32_t last_pulse_rssi;
+};
+
+/**
+ * get_dfs_channel_idx - (private) find DFS channel index for given frequency
+ *
+ * @freq: frequency to search for
+ *
+ * Returns -1 if not found.
+ */
+static int get_dfs_channel_idx(uint16_t freq)
+{
+ int i;
+ for (i = 0; i < NUM_DFS_CHANNELS; i++)
+ if (dfs_channels[i] == freq)
+ return i;
+ return -1;
+}
+
+/**
+ * reset_detector_element - (private) reset one detector element
+ *
+ * @rs: radar statistics to reset
+ * @ts: time stamp to be reset to
+ *
+ * Resets the statistics for one pattern type of one channel. Sets the
+ * timestamp for the last valid pulse to given value.
+ */
+static void reset_detector_element(struct radar_stats *rs, uint64_t ts)
+{
+ memset(rs, 0, sizeof(struct radar_stats));
+ rs->last_ts = ts;
+}
+
+
+/**
+ * detector_reset - (private) reset all detector lines for a given channel
+ *
+ * @pd_data: instance data ptr
+ * @dfs_channel_idx: DFS channel index to be reset
+ *
+ * Resets the statistics for all pattern types of one given channel.
+ */
+static void detector_reset(struct pattern_detector_data *pd_data,
+ int dfs_channel_idx)
+{
+ int i;
+ uint64_t ts = pd_data->last_pulse_ts;
+ DTRACE();
+ for (i = 0; i < pd_data->num_detector_elements; i++) {
+ struct radar_stats *rs;
+ rs = &pd_data->radar_detectors[i].stats[dfs_channel_idx];
+ reset_detector_element(rs, ts);
+ }
+}
+
+
+/**
+ * check_pulse_lost - (private) check potentially lost pulses
+ *
+ * @rs: radar stats to be checked
+ * @delta_ts: pulse interval to be checked
+ *
+ * In case we missed some pulse '.' in a row of valid pulses '|', we try to
+ * reconstruct them by checking for delta_ts being a multiple of the pri.
+ *
+ * Assume we were fed with a pattern like
+ * | | . . |
+ * Evaluating the last pulse we check if the last interval is a multiple of our
+ * pri and in that case return 2 as the number of (potentially) lost pulses.
+ *
+ * The global check if the last interval exceeds the max duration of this
+ * pattern type is performed by the caller.
+ *
+ */
+static int check_pulse_lost(struct radar_stats *rs, uint32_t delta_ts)
+{
+ int lost_pulses = 0;
+
+ if (rs->pri_count == 1) {
+ /* check constant pri patterns */
+ uint32_t pri = rs->pri[0];
+ while (delta_ts > pri) {
+ /* we already checked that we are within valid duration
+ * => won't loop too long */
+ lost_pulses++;
+ delta_ts -= pri;
+ }
+
+ if (DELTA(pri, delta_ts) <= MAX_PRI_TOLERANCE)
+ return lost_pulses;
+
+ return 0;
+ } else {
+ /* TODO: check staggered radar patterns
+ here we need to support
+ * single burst / packet based and
+ * single burst / single pulse
+ staggered PRF radar test signals
+ */
+ }
+ return 0;
+}
+
+/**
+ * detector_check_match - (private) check for pattern match
+ *
+ * @rp: radar specs to be checked
+ * @rs: radar stats to be checked
+ * @delta_ts: pulse interval to be checked
+ *
+ * Returns 1 on match
+ */
+static int detector_check_match(struct radar_specs *rp, struct radar_stats *rs)
+{
+
+ if (rs->matching_pulse_count >= rp->ppb_thresh) {
+ DINIT("XXXXXXXXXXXXXXXXXXXXXXX MATCH on type %d", rp->type_id);
+ return 1;
+ }
+ return 0;
+}
+
+static int detector_check_pri(struct radar_specs *rp, struct radar_stats *rs,
+ uint32_t delta_ts)
+{
+ int lost_pulses;
+
+ int pri_num;
+ DLOG("OK: delta_ts=%d <= max_dur[%d]=%d",
+ delta_ts, rp->type_id, rp->max_dur);
+ for (pri_num = 0; pri_num < rs->pri_count; pri_num++) {
+ if (DELTA(delta_ts, rs->pri[pri_num]) < MAX_PRI_TOLERANCE) {
+ rs->matching_pulse_count++;
+ DLOG("delta_ts=%d matches pri_num[%d][%d] => "
+ "matching_pulse_count = %d", delta_ts,
+ rp->type_id, pri_num, rs->matching_pulse_count);
+ if (detector_check_match(rp, rs))
+ return 1;
+ /* we only take the first match */
+ return 0;
+ }
+ lost_pulses = check_pulse_lost(rs, delta_ts);
+ if (lost_pulses > 0) {
+ rs->matching_pulse_count += lost_pulses + 1;
+ DLOG("[%d] assuming %d lost pulses => "
+ "matching_pulse_count = %d",
+ rp->type_id, lost_pulses,
+ rs->matching_pulse_count);
+ if (detector_check_match(rp, rs))
+ return 1;
+ return 0;
+ }
+ DLOG("delta_ts=%d not multiple of [%d] = %d",
+ delta_ts, rp->type_id, rs->pri[0]);
+ reset_detector_element(rs, rs->last_ts);
+ return 0;
+
+ }
+ if (rs->pri_count >= rp->num_pri) {
+ rs->false_pulse_count++;
+ } else {
+ /* pri was not found in the current array, add it as new */
+ rs->pri[rs->pri_count++] = delta_ts;
+ rs->matching_pulse_count += 2;
+ DLOG("added new pri[%d][%d]=%d",
+ rp->type_id, rs->pri_count-1, delta_ts);
+ }
+ return 0;
+}
+
+static int detector_check_pulse_ts(struct radar_specs *rp,
+ struct radar_stats *rs, uint64_t ts)
+{
+ uint32_t delta_ts;
+
+ DTRACE();
+ delta_ts = ts - rs->last_ts;
+ DLOG("[%d]: ts=%llu, last_ts=%llu, delta_ts=%d, pri_min=%d, pri_max=%d,"
+ "max_dur=%d", rp->type_id, ts, rs->last_ts, delta_ts,
+ rp->pri_min, rp->pri_max, rp->max_dur);
+ if (delta_ts >= rp->pri_min) {
+ DLOG("OK: delta_ts >= pri_min");
+ if (delta_ts <= rp->max_dur) {
+ /* this one is for us */
+ rs->last_ts = ts;
+ return detector_check_pri(rp, rs, delta_ts);
+ } else {
+ DLOG("NOK: delta_ts=%d > max_dur[%d]=%d",
+ delta_ts, rp->type_id, rp->max_dur);
+ }
+ } else
+ DLOG("delta_ts=%d < pri_min[%d]=%d",
+ delta_ts, rp->type_id, rp->pri_min);
+ /* if for some reason this radar was not for me, safely reset stats
+ * since this pulse invalidates all previous
+ */
+ reset_detector_element(rs, ts);
+ return 0;
+}
+
+
+static uint32_t freq_to_usec(uint32_t freq)
+{
+ return 1000000 / freq;
+}
+
+/* percentage of ppb threshold to trigger detection */
+#define MIN_PPB_THRESH 66
+#define PPB_THRESH(X) ((X*MIN_PPB_THRESH + 50) / 100)
+
+static void dpd_exit(struct dfs_pattern_detector *_this)
+{
+ if (_this->data != NULL) {
+ if (_this->data->radar_detectors != NULL)
+ free(_this->data->radar_detectors);
+ free(_this->data);
+ }
+ free(_this);
+}
+
+static enum dfs_detector_result dpd_add_pulse(
+ struct dfs_pattern_detector *_this, struct pulse_event *event)
+{
+ int detector_result = NO_DETECTION;
+ struct pattern_detector_data *pd_data = _this->data;
+ uint64_t delta_ts = event->ts - pd_data->last_pulse_ts;
+ uint32_t width = event->width;
+ int dfs_channel_idx;
+ DTRACE();
+
+ DINFO("e->width=%d, e->ts=%llu, delta_ts=%llu, e->freq=%d",
+ event->width, event->ts, delta_ts, event->freq);
+
+ dfs_channel_idx = get_dfs_channel_idx(event->freq);
+ if (dfs_channel_idx < 0) {
+ DERROR("pulse_event.freq=%d is no DFS frequency, dropping");
+ return PULSE_DROPPED;
+ }
+
+ /* global condition checks */
+
+ /* condition: pulse width inside valid range? */
+ if ((width > pd_data->max_valid_width) ||
+ (width < pd_data->min_valid_width)) {
+ DINFO("pulse width %d outside valid range [%d, %d], dropping",
+ width, pd_data->min_valid_pri, pd_data->max_valid_pri);
+ return PULSE_DROPPED;
+ }
+
+ pd_data->last_pulse_ts = event->ts;
+
+ /* condition: pulse interval < max allowed pattern duration */
+ if (delta_ts > pd_data->max_radar_dur) {
+ DINFO("pulse with delta_ts=%llu > max_radar_dur=%d, resetting",
+ delta_ts, pd_data->max_radar_dur);
+ detector_reset(pd_data, dfs_channel_idx);
+ return NO_DETECTION;
+ }
+
+ /* condition: pulse interval larger that min allowed pri
+ * NOTE: we are not checking against max allowed pri to
+ * allow for coverage of multiple pris
+ */
+ if (delta_ts >= pd_data->min_valid_pri) {
+ int i;
+
+ /* do type individual pattern matching */
+ for (i = 0; i < pd_data->num_detector_elements; i++) {
+ struct radar_specs *rp;
+ rp = &pd_data->radar_detectors[i].specs;
+ /* condition: width within type specific width range */
+ if (width >= rp->width_min && width <= rp->width_max) {
+ struct radar_stats *rs;
+ rs = &pd_data->radar_detectors[i].
+ stats[dfs_channel_idx];
+ if (detector_check_pulse_ts(rp,
+ rs, event->ts)) {
+ detector_result = RADAR_DETECTED;
+ /* stop here, don't care if further
+ * patterns might also match */
+ break;
+ }
+ }
+ }
+ if (detector_result == RADAR_DETECTED) {
+ /* radar pattern found -> reset detector line */
+ detector_reset(pd_data, dfs_channel_idx);
+ }
+ return detector_result;
+ } else
+ DINFO("pulse with delta_ts=%llu outside valid pri-range "
+ "[%d, %d], resetting", delta_ts,
+ pd_data->min_valid_pri, pd_data->max_valid_pri);
+ return 0;
+}
+
+
+/* our base VFT */
+static struct dfs_pattern_detector dpd_default_vft = {
+ .exit = dpd_exit,
+ .add_pulse = dpd_add_pulse,
+};
+
+
+static void print_detector_specs(struct pattern_detector_data *pd_data)
+{
+ int i;
+ for (i = 0; i < pd_data->num_detector_elements; i++) {
+ struct radar_specs *rs = &pd_data->radar_detectors[i].specs;
+ DINIT("Initialized radar pattern type %d", i);
+ DINIT(" rs->type_id = %d", rs->type_id);
+ DINIT(" rs->width_min = %d", rs->width_min);
+ DINIT(" rs->width_max = %d", rs->width_max);
+ DINIT(" rs->pri_min = %d", rs->pri_min);
+ DINIT(" rs->pri_max = %d", rs->pri_max);
+ DINIT(" rs->num_pri = %d", rs->num_pri);
+ DINIT(" rs->ppb_thresh = %d", rs->ppb_thresh);
+ DINIT(" rs->max_dur = %d", rs->max_dur);
+ }
+ DINIT("valid ranges: width=[%d, %d], pri=[%d, %d], dur=%d",
+ pd_data->min_valid_width, pd_data->max_valid_width,
+ pd_data->min_valid_pri, pd_data->max_valid_pri,
+ pd_data->max_radar_dur);
+}
+
+
+/* allocate and initialize object data */
+static struct pattern_detector_data *setup_detector_data(struct radar_type *rt)
+{
+ int i;
+ struct pattern_detector_data *pd_data;
+ int sz = sizeof(struct pattern_detector_data);
+ pd_data = malloc(sz);
+ if (pd_data == NULL) {
+ DERROR("allocation of pattern_detector_data failed");
+ return NULL;
+ }
+
+ memset(pd_data, 0, sz);
+
+ sz = sizeof(struct detector_line) * rt->num_radar_types;
+ pd_data->radar_detectors = malloc(sz);
+ if (pd_data->radar_detectors == NULL) {
+ DERROR("allocation of radar_detectors failed");
+ return NULL;
+ }
+ memset(pd_data->radar_detectors, 0, sz);
+
+ pd_data->num_detector_elements = rt->num_radar_types;
+ pd_data->min_valid_width = (uint32_t) -1;
+ pd_data->max_valid_width = 0;
+ pd_data->min_valid_pri = (uint32_t) -1;
+ pd_data->max_valid_pri = 0;
+ pd_data->max_radar_dur = 0;
+
+ for (i = 0; i < rt->num_radar_types; i++) {
+ struct radar_signal_type *rst = &rt->radar_types[i];
+ struct radar_specs *rs = &pd_data->radar_detectors[i].specs;
+ DINIT("Initializing type %d", i);
+ rs->type_id = rst->type_id;
+ rs->width_min = rst->width_min;
+ rs->width_max = rst->width_max;
+ rs->pri_min = freq_to_usec(rst->pps_max) - MAX_PRI_TOLERANCE;
+ rs->pri_max = freq_to_usec(rst->pps_min) + MAX_PRI_TOLERANCE;
+ rs->num_pri = rst->num_pri;
+ rs->ppb = rst->ppb;
+ rs->ppb_thresh = PPB_THRESH(rst->ppb);
+ rs->max_dur = rs->pri_max * rst->ppb * rst->num_pri;
+
+ if (rs->width_min < pd_data->min_valid_width)
+ pd_data->min_valid_width = rs->width_min;
+ if (rs->width_max > pd_data->max_valid_width)
+ pd_data->max_valid_width = rs->width_max;
+ if (rs->pri_min < pd_data->min_valid_pri)
+ pd_data->min_valid_pri = rs->pri_min;
+ if (rs->pri_max > pd_data->max_valid_pri)
+ pd_data->max_valid_pri = rs->pri_max;
+ if (rs->max_dur > pd_data->max_radar_dur)
+ pd_data->max_radar_dur = rs->max_dur;
+ }
+ print_detector_specs(pd_data);
+ return pd_data;
+}
+
+
+struct dfs_pattern_detector *dfs_pattern_detector_init(
+ enum dfs_domain dfs_domain)
+{
+ int i;
+ struct dfs_pattern_detector *_this;
+ struct radar_type *rt;
+
+ /* find supported radar type */
+ for (i = 0; /* nothing */; i++) {
+ rt = supported_radar_types[i];
+ if (rt == NULL) {
+ DERROR("non-supported dfs-domain %d", dfs_domain);
+ return NULL;
+ }
+ if (rt->dfs_id == dfs_domain)
+ break;
+ }
+ /* allocate object instance */
+ _this = malloc(sizeof(struct dfs_pattern_detector));
+ if (_this == NULL) {
+ DERROR("allocation of dfs_pattern_detector failed");
+ return NULL;
+ }
+ *_this = dpd_default_vft;
+
+ /* allocate and initialize object data */
+ _this->data = setup_detector_data(rt);
+ if (_this->data == NULL) {
+ _this->exit(_this);
+ return NULL;
+ }
+ return _this;
+}
diff --git a/src/dfs/dfs_pattern_detector.h b/src/dfs/dfs_pattern_detector.h
new file mode 100644
index 0000000..384774f
--- /dev/null
+++ b/src/dfs/dfs_pattern_detector.h
@@ -0,0 +1,46 @@
+#ifndef DFS_PATTERN_DETECTOR_H
+#define DFS_PATTERN_DETECTOR_H
+
+#include "dfs.h"
+
+
+/**
+ * enum dfs_detector_result - DFS detector result after adding pulse
+ *
+ * Feeding a potential radar pulse to the detector might result in:
+ * @NO_DETECTION: pulse added, but no detection so far
+ * @RADAR_DETECTED: pulse added, pattern matched => radar detected
+ * @PULSE_DROPPED: pulse not added, outside valid pattern ranges
+ */
+enum dfs_detector_result {
+ NO_DETECTION,
+ RADAR_DETECTED,
+ PULSE_DROPPED,
+};
+
+/**
+ * struct dfs_pattern_detector - pseudo-OO DFS pattern detector class
+ *
+ * A DFS pattern detector object is instantiated with its constructor that
+ * returns ptr to initialized object.
+ *
+ * The VFT so far needs only two public methods:
+ * @exit: destructor
+ * @add_pulse: adds radar pulse to detector
+ *
+ * All data is private.
+ */
+struct dfs_pattern_detector {
+ /* VFT */
+ void (*exit)(struct dfs_pattern_detector *_this);
+ enum dfs_detector_result (*add_pulse)
+ (struct dfs_pattern_detector *_this, struct pulse_event *pe);
+
+ /* private data */
+ struct pattern_detector_data *data;
+};
+
+/* Constructor */
+struct dfs_pattern_detector *dfs_pattern_detector_init(enum dfs_domain);
+
+#endif /* DFS_PATTERN_DETECTOR_H */
diff --git a/src/dfs/dfs_radar_types.h b/src/dfs/dfs_radar_types.h
new file mode 100644
index 0000000..435ce32
--- /dev/null
+++ b/src/dfs/dfs_radar_types.h
@@ -0,0 +1,48 @@
+#ifndef DFS_RADAR_TYPES_H
+#define DFS_RADAR_TYPES_H
+
+#include "dfs.h"
+
+struct radar_signal_type {
+ unsigned int type_id;
+ unsigned int width_min;
+ unsigned int width_max;
+ unsigned int pps_min;
+ unsigned int pps_max;
+ unsigned int num_pri;
+ unsigned int ppb;
+};
+
+static struct radar_signal_type etsi_radar_ref_types_v15[] = {
+ { 0, 0, 1, 700, 700, 1, 18, },
+ { 1, 0, 5, 200, 1000, 1, 10, },
+ { 2, 0, 15, 200, 1600, 1, 15, },
+ { 3, 0, 15, 2300, 4000, 1, 25, },
+ { 4, 20, 30, 2000, 4000, 1, 20, },
+ { 5, 0, 2, 300, 400, 3, 10, },
+ { 6, 0, 2, 400, 1200, 3, 15, },
+};
+
+struct radar_type {
+ uint32_t dfs_id;
+ uint32_t num_radar_types;
+ struct radar_signal_type *radar_types;
+};
+
+static struct radar_type etsi_radar_types_v15 = {
+ .dfs_id = DFS_ETSI_DOMAIN,
+ .num_radar_types = sizeof(etsi_radar_ref_types_v15) /
+ sizeof(struct radar_signal_type),
+ .radar_types = etsi_radar_ref_types_v15,
+};
+
+
+static struct radar_type *supported_radar_types[] = {
+ &etsi_radar_types_v15,
+ 0,
+};
+
+
+
+#endif /* DFS_RADAR_TYPES_H */
+
--
1.7.1


2011-02-14 10:21:00

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 1/2] nl80211: interface update

I'm thinking this is [RFC] really?

> @@ -1076,6 +1078,10 @@ enum nl80211_attrs {
>
> /* add attributes here, update the policy in nl80211.c */
>
> + NL80211_DFSEVENT_ATTR_FREQ,
> + NL80211_DFSEVENT_ATTR_TS,
> + NL80211_DFSEVENT_ATTR_WIDTH,

I think it would be preferable to create a new DFS_EVENT attribute (also
note that all attributse should be prefixed by NL80211_ATTR_) and then
nest data into it.

However, why is the TS/width data needed at all?

johannes


2011-02-09 16:09:10

by Chaoxing Lin

[permalink] [raw]
Subject: RE: [RFC] DFS userspace handler

I would vote for a dedidate DFS daemon.

Straightforward name is better.
Beginners would be hard to understand why WDS/Mesh/IBSS mode requires a hostapd daemon which intuitively means for AP mode..



-----Original Message-----
From: Zefir Kurtisi [mailto:[email protected]]
Sent: Wednesday, February 09, 2011 10:51 AM
To: Chaoxing Lin
Cc: [email protected]
Subject: Re: [RFC] DFS userspace handler

On 02/09/2011 04:35 PM, Chaoxing Lin wrote:
> What about DFS in WDS / Mesh / IBSS mode if DFS is only implemented in hostapd?
>
> Will an instance of hostapd be required for all these modes, too?
>
> Thanks
>
Yes, with this approach you will need to have some userspace component that
evaluates the reported pulses and handles channels upon radar detection.

hostapd was taken as the first shot, but one might also think of a dedicated
DFS deamon that takes this role. Actually that is our intended gain of this
approach: having a defined interface for pulse events reporting and highest
flexibility wrt detection and handling.

Cheers

> -----Original Message-----
> From: [email protected] [mailto:[email protected]] On Behalf Of Zefir Kurtisi
> Sent: Wednesday, February 09, 2011 7:02 AM
> To: [email protected]
> Cc: Zefir Kurtisi
> Subject: [RFC] DFS userspace handler
>
> Hello DFS folks,
>
> this is a patch series to prove the concept of DFS handling done in userspace.
>
> It moves the radar pattern detector introduced in [1] and documented in [2]
> from mac80211 up to hostapd.
>
> The basic concept is:
> 1) pattern detection
> * pulse events detected by HW are passed to hostapd
> * hostapd performs per-wiphy DFS pattern matching
> 2) channel handling: on radar detection hostapd
> * updates channel state (NOL handling)
> * switches to new operating channel with TX disabled (on all wiphys)
> * after CAC period enables TX on that channel
>
> The proposed patches provide part 1) of the concept. The nl80211 interface is
> extended with functions to pass DFS pulse events to hostapd.
>
> Testing as described in [2] results in identical output, indicating that both
> approaches (mac80211 and hostapd based) are equivalent from functional point
> of view.
>
> The testing was done with
> * OpenWRT r25417
> * Atheros AR9280 Rev:2
> * DFS radars generated with R&S vector signal generator
>
> We at our company would prefer this approach over the mac80211 based one as
> soon as we see that part 2) can be implemented as proposed.
>
>
>
> Cheers
> Zefir
>
>
>
> [1] http://article.gmane.org/gmane.linux.kernel.wireless.general/61868
> [2] http://linuxwireless.org/en/developers/DFS/DetectorDesignNt
> ---


2011-02-09 15:35:40

by Chaoxing Lin

[permalink] [raw]
Subject: RE: [RFC] DFS userspace handler

What about DFS in WDS / Mesh / IBSS mode if DFS is only implemented in hostapd?

Will an instance of hostapd be required for all these modes, too?

Thanks

-----Original Message-----
From: [email protected] [mailto:[email protected]] On Behalf Of Zefir Kurtisi
Sent: Wednesday, February 09, 2011 7:02 AM
To: [email protected]
Cc: Zefir Kurtisi
Subject: [RFC] DFS userspace handler

Hello DFS folks,

this is a patch series to prove the concept of DFS handling done in userspace.

It moves the radar pattern detector introduced in [1] and documented in [2]
from mac80211 up to hostapd.

The basic concept is:
1) pattern detection
* pulse events detected by HW are passed to hostapd
* hostapd performs per-wiphy DFS pattern matching
2) channel handling: on radar detection hostapd
* updates channel state (NOL handling)
* switches to new operating channel with TX disabled (on all wiphys)
* after CAC period enables TX on that channel

The proposed patches provide part 1) of the concept. The nl80211 interface is
extended with functions to pass DFS pulse events to hostapd.

Testing as described in [2] results in identical output, indicating that both
approaches (mac80211 and hostapd based) are equivalent from functional point
of view.

The testing was done with
* OpenWRT r25417
* Atheros AR9280 Rev:2
* DFS radars generated with R&S vector signal generator

We at our company would prefer this approach over the mac80211 based one as
soon as we see that part 2) can be implemented as proposed.



Cheers
Zefir



[1] http://article.gmane.org/gmane.linux.kernel.wireless.general/61868
[2] http://linuxwireless.org/en/developers/DFS/DetectorDesignNt
---
--
1.7.1