2012-05-10 06:45:07

by Michal Kazior

[permalink] [raw]
Subject: [RFC/PATCH] multi-channel preparation work

Hi Johannes,

Here's some work on multi-channel operation. It refactors channel
switching logic with regard to ieee80211_hw_config and moves
oper_channel stuff to sdata.

I plan on doing channel contexts next. Monitor interface stuff probably
needs to be addressed as well since it's tied tightly with set_channel.


--
Pozdrawiam / Best regards, Michal Kazior.



2012-05-10 06:45:10

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 4/7] mac80211: introduce ieee80211_oper_channel_type

Prepares for _oper_channel_type removal.

Change-Id: I161842005dd195ad0935b6f4ba14740724ab9846
Signed-off-by: Michal Kazior <[email protected]>
---
net/mac80211/chan.c | 14 +++++++++++---
net/mac80211/ieee80211_i.h | 3 +++
2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 3234ad1..8513f70 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -66,14 +66,15 @@ ieee80211_get_channel_mode(struct ieee80211_local *local,

static enum nl80211_channel_type
ieee80211_get_superchan(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata)
+ struct ieee80211_sub_if_data *sdata,
+ bool ignore_self)
{
struct ieee80211_sub_if_data *tmp;
enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;

mutex_lock(&local->iflist_mtx);
list_for_each_entry(tmp, &local->interfaces, list) {
- if (tmp == sdata)
+ if (ignore_self && tmp == sdata)
continue;

if (!ieee80211_sdata_running(tmp))
@@ -135,13 +136,20 @@ static bool ieee80211_channel_types_are_compatible(
return result;
}

+enum nl80211_channel_type
+ieee80211_oper_channel_type(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ return ieee80211_get_superchan(local, sdata, 0);
+}
+
bool ieee80211_set_channel_type(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
enum nl80211_channel_type chantype)
{
enum nl80211_channel_type superchan;

- superchan = ieee80211_get_superchan(local, sdata);
+ superchan = ieee80211_get_superchan(local, sdata, 1);
if (!ieee80211_channel_types_are_compatible(superchan, chantype))
return false;

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 122717f..e40cd06 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1527,6 +1527,9 @@ enum ieee80211_chan_mode {
enum ieee80211_chan_mode
ieee80211_get_channel_mode(struct ieee80211_local *local,
struct ieee80211_sub_if_data *ignore);
+enum nl80211_channel_type
+ieee80211_oper_channel_type(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata);
bool ieee80211_set_channel_type(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
enum nl80211_channel_type chantype);
--
1.7.0.4


2012-05-10 07:31:43

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 7/7] mac80211: move oper_channel to sdata

On Thu, 2012-05-10 at 08:44 +0200, Michal Kazior wrote:

> + /* cfg80211 arbitrates multi-channel */
> + if (local->hw.wiphy->flags & WIPHY_FLAG_ENFORCE_COMBINATIONS)
> + return true;

This no longer exists, but that's just arbitrating
multi-virtual-interface, not necessarily multi-channel, I think?

I need more time to look over this, in particular wrt. this arbitration.

johannes


2012-05-10 09:05:49

by Michal Kazior

[permalink] [raw]
Subject: Re: [PATCH 5/7] cfg80211: extend get_channel to take wdev argument

Johannes Berg wrote:
>
>> struct ieee80211_channel *(*get_channel)(struct wiphy *wiphy,
>> - enum nl80211_channel_type *type);
>> + struct wireless_dev *wdev,
>> + enum nl80211_channel_type *type);
>
> Hmm. This is mostly used for monitor mode, and there we would have to
> return NULL anyway while using multiple channels at the same time. Is it
> really worth the complexity?

Would we really have to return NULL? With my last patch that moves
oper_channel to sdata it can return proper channel.

But then monitor mode.. ugh.


--
Pozdrawiam / Best regards, Michal Kazior.

2012-05-10 06:45:09

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 2/7] mac80211: split channel logic from hw_config

Extracts two functions from hw_config:

* ieee80211_recalc_channel
* ieee80211_recalc_power_level

Both are per-sdata. They either supplement or
substitute ieee80211_hw_config in the code.

Prepares channel switching logic for future
multi-channel operation.

Change-Id: Ibfe2965a6967c7f34c16b4111ea9ab2fceabaf22
Signed-off-by: Michal Kazior <[email protected]>
---
net/mac80211/cfg.c | 46 ++++++++++++++++++---------------
net/mac80211/ibss.c | 2 +-
net/mac80211/ieee80211_i.h | 2 +
net/mac80211/iface.c | 8 ++++-
net/mac80211/main.c | 61 ++++++++++++++++++++++++++-----------------
net/mac80211/mlme.c | 7 +++--
net/mac80211/scan.c | 18 ++++--------
net/mac80211/util.c | 6 ++++
net/mac80211/work.c | 6 +++-
9 files changed, 91 insertions(+), 65 deletions(-)

diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 510a745..9fa5044 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1519,7 +1519,7 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
/* Update driver if changes were actually made. */
if ((old_oper != local->oper_channel) ||
(old_oper_type != local->_oper_channel_type))
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+ ieee80211_recalc_channel(sdata);

if (sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR &&
old_vif_oper_type != sdata->vif.bss_conf.channel_type)
@@ -1708,28 +1708,32 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
{
struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_channel *chan = local->hw.conf.channel;
- u32 changes = 0;
+ struct ieee80211_sub_if_data *sdata;

- switch (type) {
- case NL80211_TX_POWER_AUTOMATIC:
- local->user_power_level = -1;
- break;
- case NL80211_TX_POWER_LIMITED:
- if (mbm < 0 || (mbm % 100))
- return -EOPNOTSUPP;
- local->user_power_level = MBM_TO_DBM(mbm);
- break;
- case NL80211_TX_POWER_FIXED:
- if (mbm < 0 || (mbm % 100))
- return -EOPNOTSUPP;
- /* TODO: move to cfg80211 when it knows the channel */
- if (MBM_TO_DBM(mbm) > chan->max_power)
- return -EINVAL;
- local->user_power_level = MBM_TO_DBM(mbm);
- break;
- }
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ switch (type) {
+ case NL80211_TX_POWER_AUTOMATIC:
+ local->user_power_level = -1;
+ break;
+ case NL80211_TX_POWER_LIMITED:
+ if (mbm < 0 || (mbm % 100))
+ return -EOPNOTSUPP;
+ local->user_power_level = MBM_TO_DBM(mbm);
+ break;
+ case NL80211_TX_POWER_FIXED:
+ if (mbm < 0 || (mbm % 100))
+ return -EOPNOTSUPP;
+ /* TODO: move to cfg80211 when it knows the channel */
+ if (MBM_TO_DBM(mbm) > chan->max_power)
+ return -EINVAL;
+ local->user_power_level = MBM_TO_DBM(mbm);
+ break;
+ }

- ieee80211_hw_config(local, changes);
+ ieee80211_recalc_power_level(sdata);
+ }
+ mutex_unlock(&local->iflist_mtx);

return 0;
}
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 49a2079..718ee49 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -91,7 +91,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
WARN_ON(!ieee80211_set_channel_type(local, sdata,
NL80211_CHAN_HT20));
}
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+ ieee80211_recalc_channel(sdata);

sband = local->hw.wiphy->bands[chan->band];

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index c109960..122717f 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1201,6 +1201,8 @@ static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
}


+void ieee80211_recalc_power_level(struct ieee80211_sub_if_data *sdata);
+void ieee80211_recalc_channel(struct ieee80211_sub_if_data *sdata);
int ieee80211_hw_config(struct ieee80211_local *local, u32 changed);
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx);
void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 6e85fae..cefdf73 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -467,8 +467,10 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
if (coming_up)
local->open_count++;

- if (hw_reconf_flags)
+ if (hw_reconf_flags) {
ieee80211_hw_config(local, hw_reconf_flags);
+ ieee80211_recalc_channel(sdata);
+ }

ieee80211_recalc_ps(local, -1);

@@ -685,8 +687,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
ieee80211_set_channel_type(local, NULL, NL80211_CHAN_NO_HT);

/* do after stop to avoid reconfiguring when we stop anyway */
- if (hw_reconf_flags || (orig_ct != local->_oper_channel_type))
+ if (hw_reconf_flags || (orig_ct != local->_oper_channel_type)) {
ieee80211_hw_config(local, hw_reconf_flags);
+ ieee80211_recalc_channel(sdata);
+ }

spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index b70f7f0..01c0e8b 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -93,16 +93,38 @@ static void ieee80211_reconfig_filter(struct work_struct *work)
ieee80211_configure_filter(local);
}

-int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
+void ieee80211_recalc_power_level(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_channel *chan;
- int ret = 0;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_channel *chan = local->hw.conf.channel;
int power;
+
+ if (test_bit(SCAN_SW_SCANNING, &local->scanning) ||
+ test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) ||
+ test_bit(SCAN_HW_SCANNING, &local->scanning))
+ power = chan->max_power;
+ else
+ power = local->power_constr_level ?
+ min(chan->max_power,
+ (chan->max_reg_power - local->power_constr_level)) :
+ chan->max_power;
+
+ if (local->user_power_level >= 0)
+ power = min(power, local->user_power_level);
+
+ if (local->hw.conf.power_level != power) {
+ local->hw.conf.power_level = power;
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_POWER);
+ }
+}
+
+void ieee80211_recalc_channel(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_channel *chan;
enum nl80211_channel_type channel_type;
u32 offchannel_flag;

- might_sleep();
-
offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
if (local->scan_channel) {
chan = local->scan_channel;
@@ -133,8 +155,17 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
channel_type != local->hw.conf.channel_type) {
local->hw.conf.channel = chan;
local->hw.conf.channel_type = channel_type;
- changed |= IEEE80211_CONF_CHANGE_CHANNEL;
+
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+ ieee80211_recalc_power_level(sdata);
}
+}
+
+int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
+{
+ int ret = 0;
+
+ might_sleep();

if (!conf_is_ht(&local->hw.conf)) {
/*
@@ -148,24 +179,6 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
changed |= IEEE80211_CONF_CHANGE_SMPS;
}

- if (test_bit(SCAN_SW_SCANNING, &local->scanning) ||
- test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) ||
- test_bit(SCAN_HW_SCANNING, &local->scanning))
- power = chan->max_power;
- else
- power = local->power_constr_level ?
- min(chan->max_power,
- (chan->max_reg_power - local->power_constr_level)) :
- chan->max_power;
-
- if (local->user_power_level >= 0)
- power = min(power, local->user_power_level);
-
- if (local->hw.conf.power_level != power) {
- changed |= IEEE80211_CONF_CHANGE_POWER;
- local->hw.conf.power_level = power;
- }
-
if (changed && local->open_count) {
ret = drv_config(local, changed);
/*
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index c8836fa..f75947b 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -681,8 +681,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
sdata->local->oper_channel = sdata->local->csa_channel;
if (!sdata->local->ops->channel_switch) {
/* call "hw_config" only if doing sw channel switch */
- ieee80211_hw_config(sdata->local,
- IEEE80211_CONF_CHANGE_CHANNEL);
+ ieee80211_recalc_channel(sdata);
} else {
/* update the device channel directly */
sdata->local->hw.conf.channel = sdata->local->oper_channel;
@@ -815,7 +814,7 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
if ((*pwr_constr_elem <= conf->channel->max_reg_power) &&
(*pwr_constr_elem != sdata->local->power_constr_level)) {
sdata->local->power_constr_level = *pwr_constr_elem;
- ieee80211_hw_config(sdata->local, 0);
+ ieee80211_recalc_power_level(sdata);
}
}

@@ -1417,6 +1416,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
/* channel(_type) changes are handled by ieee80211_hw_config */
WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
ieee80211_hw_config(local, 0);
+ ieee80211_recalc_channel(sdata);

/* disassociated - set to defaults now */
ieee80211_set_wmm_default(sdata, false);
@@ -3111,6 +3111,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,

local->oper_channel = cbss->channel;
ieee80211_hw_config(local, 0);
+ ieee80211_recalc_channel(sdata);

if (!have_sta) {
u32 rates = 0, basic_rates = 0;
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index d8c0a34..71d642e 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -291,6 +291,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
bool was_hw_scan)
{
struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata = local->scan_sdata;

lockdep_assert_held(&local->mtx);

@@ -324,7 +325,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
local->scan_channel = NULL;

/* Set power back to normal operating levels. */
- ieee80211_hw_config(local, 0);
+ ieee80211_recalc_power_level(sdata);

if (!was_hw_scan) {
ieee80211_configure_filter(local);
@@ -379,7 +380,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
ieee80211_configure_filter(local);

/* We need to set power level at maximum rate for scanning. */
- ieee80211_hw_config(local, 0);
+ ieee80211_recalc_power_level(local->scan_sdata);

ieee80211_queue_delayed_work(&local->hw,
&local->scan_work, 0);
@@ -511,7 +512,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
ieee80211_configure_filter(local); /* accept probe-responses */

/* We need to ensure power level is at max for scanning. */
- ieee80211_hw_config(local, 0);
+ ieee80211_recalc_power_level(sdata);

if ((req->channels[0]->flags &
IEEE80211_CHAN_PASSIVE_SCAN) ||
@@ -652,18 +653,11 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,

local->scan_channel = chan;

- if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
- skip = 1;
+ ieee80211_recalc_channel(local->scan_sdata);

/* advance state machine to next channel/band */
local->scan_channel_idx++;

- if (skip) {
- /* if we skip this channel return to the decision state */
- local->next_scan_state = SCAN_DECISION;
- return;
- }
-
/*
* Probe delay is used to update the NAV, cf. 11.1.3.2.2
* (which unfortunately doesn't say _why_ step a) is done,
@@ -691,7 +685,7 @@ static void ieee80211_scan_state_suspend(struct ieee80211_local *local,
{
/* switch back to the operating channel */
local->scan_channel = NULL;
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+ ieee80211_recalc_channel(local->scan_sdata);

/*
* Re-enable vifs and beaconing. Leave PS
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 9d255a2..c0a1c0d 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1295,6 +1295,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
/* reconfigure hardware */
ieee80211_hw_config(local, ~0);

+ list_for_each_entry(sdata, &local->interfaces, list)
+ ieee80211_recalc_channel(sdata);
+
ieee80211_configure_filter(local);

/* Finally also reconfigure all the BSS information */
@@ -1528,6 +1531,9 @@ void ieee80211_recalc_smps(struct ieee80211_local *local)
local->smps_mode = smps_mode;
/* changed flag is auto-detected for this */
ieee80211_hw_config(local, 0);
+
+ list_for_each_entry(sdata, &local->interfaces, list)
+ ieee80211_recalc_channel(sdata);
}

static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index 2a0b252..59f01ff 100644
--- a/net/mac80211/work.c
+++ b/net/mac80211/work.c
@@ -156,7 +156,7 @@ static void ieee80211_work_work(struct work_struct *work)
local->tmp_channel = wk->chan;
local->tmp_channel_type = wk->chan_type;

- ieee80211_hw_config(local, 0);
+ ieee80211_recalc_channel(wk->sdata);

started = true;
wk->timeout = jiffies;
@@ -220,9 +220,11 @@ static void ieee80211_work_work(struct work_struct *work)
}

if (!remain_off_channel && local->tmp_channel) {
+ struct ieee80211_sub_if_data *sdata = local->tmp_sdata;
+
local->tmp_sdata = NULL;
local->tmp_channel = NULL;
- ieee80211_hw_config(local, 0);
+ ieee80211_recalc_channel(sdata);

ieee80211_offchannel_return(local, true);

--
1.7.0.4


2012-05-10 07:29:58

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 5/7] cfg80211: extend get_channel to take wdev argument


> struct ieee80211_channel *(*get_channel)(struct wiphy *wiphy,
> - enum nl80211_channel_type *type);
> + struct wireless_dev *wdev,
> + enum nl80211_channel_type *type);

Hmm. This is mostly used for monitor mode, and there we would have to
return NULL anyway while using multiple channels at the same time. Is it
really worth the complexity?

johannes


2012-05-10 10:57:48

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 5/7] cfg80211: extend get_channel to take wdev argument

On Thu, 2012-05-10 at 11:05 +0200, Michal Kazior wrote:
> Johannes Berg wrote:
> >
> >> struct ieee80211_channel *(*get_channel)(struct wiphy *wiphy,
> >> - enum nl80211_channel_type *type);
> >> + struct wireless_dev *wdev,
> >> + enum nl80211_channel_type *type);
> >
> > Hmm. This is mostly used for monitor mode, and there we would have to
> > return NULL anyway while using multiple channels at the same time. Is it
> > really worth the complexity?
>
> Would we really have to return NULL? With my last patch that moves
> oper_channel to sdata it can return proper channel.
>
> But then monitor mode.. ugh.

Well, we could return something proper for everything but monitor, but
we probably don't really care much in those cases?

For monitor we should return something if there's just a single channel
in use, and NULL otherwise I guess.

johannes


2012-05-10 06:45:08

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 1/7] mac80211: add tracking of temporary offchannel sdata

This is necessary if we want to have a sdata-based
channel recalculation.

Change-Id: I223e052146893b3ae1ca46de7d90c54ffc589f1b
Signed-off-by: Michal Kazior <[email protected]>
---
net/mac80211/ieee80211_i.h | 1 +
net/mac80211/work.c | 2 ++
2 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index b5e491b..c109960 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -997,6 +997,7 @@ struct ieee80211_local {
struct ieee80211_channel *oper_channel, *csa_channel;

/* Temporary remain-on-channel for off-channel operations */
+ struct ieee80211_sub_if_data *tmp_sdata;
struct ieee80211_channel *tmp_channel;
enum nl80211_channel_type tmp_channel_type;

diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index b2650a9..2a0b252 100644
--- a/net/mac80211/work.c
+++ b/net/mac80211/work.c
@@ -152,6 +152,7 @@ static void ieee80211_work_work(struct work_struct *work)
if (!started && !local->tmp_channel) {
ieee80211_offchannel_stop_vifs(local, true);

+ local->tmp_sdata = wk->sdata;
local->tmp_channel = wk->chan;
local->tmp_channel_type = wk->chan_type;

@@ -219,6 +220,7 @@ static void ieee80211_work_work(struct work_struct *work)
}

if (!remain_off_channel && local->tmp_channel) {
+ local->tmp_sdata = NULL;
local->tmp_channel = NULL;
ieee80211_hw_config(local, 0);

--
1.7.0.4


2012-05-10 07:26:34

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 1/7] mac80211: add tracking of temporary offchannel sdata

Hi Michal,

> This is necessary if we want to have a sdata-based
> channel recalculation.
>
> Change-Id: I223e052146893b3ae1ca46de7d90c54ffc589f1b
> Signed-off-by: Michal Kazior <[email protected]>
> ---
> net/mac80211/ieee80211_i.h | 1 +
> net/mac80211/work.c | 2 ++
> 2 files changed, 3 insertions(+), 0 deletions(-)
>
> diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
> index b5e491b..c109960 100644
> --- a/net/mac80211/ieee80211_i.h
> +++ b/net/mac80211/ieee80211_i.h
> @@ -997,6 +997,7 @@ struct ieee80211_local {
> struct ieee80211_channel *oper_channel, *csa_channel;
>
> /* Temporary remain-on-channel for off-channel operations */
> + struct ieee80211_sub_if_data *tmp_sdata;
> struct ieee80211_channel *tmp_channel;
> enum nl80211_channel_type tmp_channel_type;

Do you actually need this? I'm still tempted to not worry about any of
this and force drivers to implement remain-on-channel in the driver or
device for multi-channel, and this temporary thing is only used for
remain-on-channel operations now.

johannes


2012-05-10 06:45:13

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 7/7] mac80211: move oper_channel to sdata

Change-Id: I647c0e6dae9e9184f471d73a6a895f66d14d9b69
Signed-off-by: Michal Kazior <[email protected]>
---
net/mac80211/cfg.c | 58 +++++++++++++++++++++++++++++++-------------
net/mac80211/ibss.c | 8 +++---
net/mac80211/ieee80211_i.h | 3 +-
net/mac80211/iface.c | 4 ++-
net/mac80211/main.c | 11 ++++----
net/mac80211/mesh.c | 4 +-
net/mac80211/mesh_plink.c | 2 +-
net/mac80211/mlme.c | 22 ++++++++--------
net/mac80211/scan.c | 2 +-
9 files changed, 70 insertions(+), 44 deletions(-)

diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 1898299..9b3d8a0 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -750,7 +750,7 @@ static int sta_apply_parameters(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata = sta->sdata;
u32 mask, set;

- sband = local->hw.wiphy->bands[local->oper_channel->band];
+ sband = local->hw.wiphy->bands[sdata->oper_channel->band];

mask = params->sta_flags_mask;
set = params->sta_flags_set;
@@ -865,7 +865,7 @@ static int sta_apply_parameters(struct ieee80211_local *local,
rates |= BIT(j);
}
}
- sta->sta.supp_rates[local->oper_channel->band] = rates;
+ sta->sta.supp_rates[sdata->oper_channel->band] = rates;
}

if (params->ht_capa)
@@ -1409,9 +1409,8 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
if (params->basic_rates) {
int i, j;
u32 rates = 0;
- struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_supported_band *sband =
- wiphy->bands[local->oper_channel->band];
+ wiphy->bands[sdata->oper_channel->band];

for (i = 0; i < params->basic_rates_len; i++) {
int rate = (params->basic_rates[i] & 0x7f) * 5;
@@ -1479,6 +1478,31 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy,
return 0;
}

+static bool ieee80211_can_use_fixed_channel(struct ieee80211_local *local,
+ struct ieee80211_channel *channel)
+{
+ struct ieee80211_sub_if_data *sdata;
+ int result = true;
+
+ /* cfg80211 arbitrates multi-channel */
+ if (local->hw.wiphy->flags & WIPHY_FLAG_ENFORCE_COMBINATIONS)
+ return true;
+
+ /* support legacy single-channel mode */
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!ieee80211_sdata_running(sdata))
+ continue;
+ if (sdata->oper_channel != channel) {
+ result = false;
+ break;
+ }
+ }
+ mutex_unlock(&local->iflist_mtx);
+
+ return result;
+}
+
static int ieee80211_set_channel(struct wiphy *wiphy,
struct net_device *netdev,
struct ieee80211_channel *chan,
@@ -1497,7 +1521,7 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
case CHAN_MODE_HOPPING:
return -EBUSY;
case CHAN_MODE_FIXED:
- if (local->oper_channel != chan)
+ if (!ieee80211_can_use_fixed_channel(local, chan))
return -EBUSY;
break;
case CHAN_MODE_UNDEFINED:
@@ -1511,11 +1535,11 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
if (!ieee80211_set_channel_type(local, sdata, channel_type))
return -EBUSY;

- old_oper = local->oper_channel;
- local->oper_channel = chan;
+ old_oper = sdata->oper_channel;
+ sdata->oper_channel = chan;

/* Update driver if changes were actually made. */
- if ((old_oper != local->oper_channel) ||
+ if ((old_oper != sdata->oper_channel) ||
(old_oper_type != ieee80211_oper_channel_type(local, sdata)))
ieee80211_recalc_channel(sdata);

@@ -1613,9 +1637,9 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,
case CHAN_MODE_HOPPING:
return -EBUSY;
case CHAN_MODE_FIXED:
- if (local->oper_channel == req->bss->channel)
- break;
- return -EBUSY;
+ if (!ieee80211_can_use_fixed_channel(local, req->bss->channel))
+ return -EBUSY;
+ break;
case CHAN_MODE_UNDEFINED:
break;
}
@@ -1647,9 +1671,9 @@ static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
case CHAN_MODE_FIXED:
if (!params->channel_fixed)
return -EBUSY;
- if (local->oper_channel == params->channel)
- break;
- return -EBUSY;
+ if (!ieee80211_can_use_fixed_channel(local, params->channel))
+ return -EBUSY;
+ break;
case CHAN_MODE_UNDEFINED:
break;
}
@@ -2058,7 +2082,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,

/* Check that we are on the requested channel for transmission */
if (chan != local->tmp_channel &&
- chan != local->oper_channel)
+ chan != sdata->oper_channel)
is_offchan = true;
if (channel_type_valid &&
(channel_type != local->tmp_channel_type &&
@@ -2323,7 +2347,7 @@ static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
u16 capab;

capab = 0;
- if (local->oper_channel->band != IEEE80211_BAND_2GHZ)
+ if (sdata->oper_channel->band != IEEE80211_BAND_2GHZ)
return capab;

if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
@@ -2699,7 +2723,7 @@ ieee80211_wiphy_get_channel(struct wiphy *wiphy,
container_of(wdev, struct ieee80211_sub_if_data, wdev);

*type = ieee80211_oper_channel_type(local, sdata);
- return local->oper_channel;
+ return sdata->oper_channel;
}

#ifdef CONFIG_PM
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 718ee49..3159700 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -80,7 +80,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,

sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;

- local->oper_channel = chan;
+ sdata->oper_channel = chan;
channel_type = ifibss->channel_type;
if (channel_type > NL80211_CHAN_HT20 &&
!cfg80211_can_beacon_sec_chan(local->hw.wiphy, chan, channel_type))
@@ -496,7 +496,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
goto put_bss;

/* different channel */
- if (cbss->channel != local->oper_channel)
+ if (cbss->channel != sdata->oper_channel)
goto put_bss;

/* different SSID */
@@ -778,7 +778,7 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)

if (time_after(jiffies, ifibss->ibss_join_req +
IEEE80211_IBSS_JOIN_TIMEOUT)) {
- if (!(local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS)) {
+ if (!(sdata->oper_channel->flags & IEEE80211_CHAN_NO_IBSS)) {
ieee80211_sta_create_ibss(sdata);
return;
}
@@ -1088,7 +1088,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,

/* fix ourselves to that channel now already */
if (params->channel_fixed) {
- sdata->local->oper_channel = params->channel;
+ sdata->oper_channel = params->channel;
if (!ieee80211_set_channel_type(sdata->local, sdata,
params->channel_type)) {
mutex_unlock(&sdata->u.ibss.mtx);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 3a1af25..66fe242 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -717,6 +717,8 @@ struct ieee80211_sub_if_data {

bool arp_filter_state;

+ struct ieee80211_channel *oper_channel, *csa_channel;
+
/*
* AP this belongs to: self in AP mode and
* corresponding AP in VLAN mode, NULL for
@@ -993,7 +995,6 @@ struct ieee80211_local {
enum mac80211_scan_state next_scan_state;
struct delayed_work scan_work;
struct ieee80211_sub_if_data *scan_sdata;
- struct ieee80211_channel *oper_channel, *csa_channel;

/* Temporary remain-on-channel for off-channel operations */
struct ieee80211_sub_if_data *tmp_sdata;
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 77b9154..394d33f 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1124,7 +1124,7 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
return 0;

/* Setting ad-hoc mode on non-IBSS channel is not supported. */
- if (sdata->local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS &&
+ if (sdata->oper_channel->flags & IEEE80211_CHAN_NO_IBSS &&
type == NL80211_IFTYPE_ADHOC)
return -EOPNOTSUPP;

@@ -1312,6 +1312,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
sdata->arp_filter_state = true;
#endif

+ sdata->oper_channel = &local->hw.wiphy->bands[0]->channels[0];
+
for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
skb_queue_head_init(&sdata->fragments[i].skb_list);

diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 6c05464..3959dfb 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -133,7 +133,7 @@ void ieee80211_recalc_channel(struct ieee80211_sub_if_data *sdata)
/* If scanning on oper channel, use whatever channel-type
* is currently in use.
*/
- if (chan == local->oper_channel)
+ if (chan == sdata->oper_channel)
channel_type = oper_channel_type;
else
channel_type = NL80211_CHAN_NO_HT;
@@ -141,11 +141,11 @@ void ieee80211_recalc_channel(struct ieee80211_sub_if_data *sdata)
chan = local->tmp_channel;
channel_type = local->tmp_channel_type;
} else {
- chan = local->oper_channel;
+ chan = sdata->oper_channel;
channel_type = oper_channel_type;
}

- if (chan != local->oper_channel ||
+ if (chan != sdata->oper_channel ||
channel_type != oper_channel_type)
local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL;
else
@@ -737,10 +737,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
sband = local->hw.wiphy->bands[band];
if (!sband)
continue;
- if (!local->oper_channel) {
+ if (!local->hw.conf.channel) {
/* init channel we're on */
- local->hw.conf.channel =
- local->oper_channel = &sband->channels[0];
+ local->hw.conf.channel = &sband->channels[0];
local->hw.conf.channel_type = NL80211_CHAN_NO_HT;
}
channels += sband->n_channels;
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index ac03c4b..742124b 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -361,7 +361,7 @@ int mesh_add_ht_cap_ie(struct sk_buff *skb,
struct ieee80211_supported_band *sband;
u8 *pos;

- sband = local->hw.wiphy->bands[local->oper_channel->band];
+ sband = local->hw.wiphy->bands[sdata->oper_channel->band];
if (!sband->ht_cap.ht_supported ||
ieee80211_oper_channel_type(local, sdata) == NL80211_CHAN_NO_HT)
return 0;
@@ -379,7 +379,7 @@ int mesh_add_ht_oper_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
- struct ieee80211_channel *channel = local->oper_channel;
+ struct ieee80211_channel *channel = sdata->oper_channel;
enum nl80211_channel_type channel_type =
ieee80211_oper_channel_type(local, sdata);
struct ieee80211_supported_band *sband =
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 9c836e7..a97f5e6 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -93,7 +93,7 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband;
struct sta_info *sta;

- sband = local->hw.wiphy->bands[local->oper_channel->band];
+ sband = local->hw.wiphy->bands[sdata->oper_channel->band];

if (local->num_sta >= MESH_MAX_PLINKS)
return NULL;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index f75947b..17174a4 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -351,7 +351,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)

lockdep_assert_held(&ifmgd->mtx);

- sband = local->hw.wiphy->bands[local->oper_channel->band];
+ sband = local->hw.wiphy->bands[sdata->oper_channel->band];

if (assoc_data->supp_rates_len) {
/*
@@ -472,7 +472,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
*pos++ = WLAN_EID_PWR_CAPABILITY;
*pos++ = 2;
*pos++ = 0; /* min tx power */
- *pos++ = local->oper_channel->max_power; /* max tx power */
+ *pos++ = sdata->oper_channel->max_power; /* max tx power */

/* 2. supported channels */
/* TODO: get this in reg domain format */
@@ -510,7 +510,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)

if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
ieee80211_add_ht_ie(sdata, skb, assoc_data->ht_operation_ie,
- sband, local->oper_channel, ifmgd->ap_smps);
+ sband, sdata->oper_channel, ifmgd->ap_smps);

/* if present, add any custom non-vendor IEs that go after HT */
if (assoc_data->ie_len && assoc_data->ie) {
@@ -678,17 +678,17 @@ static void ieee80211_chswitch_work(struct work_struct *work)
if (!ifmgd->associated)
goto out;

- sdata->local->oper_channel = sdata->local->csa_channel;
+ sdata->oper_channel = sdata->csa_channel;
if (!sdata->local->ops->channel_switch) {
/* call "hw_config" only if doing sw channel switch */
ieee80211_recalc_channel(sdata);
} else {
/* update the device channel directly */
- sdata->local->hw.conf.channel = sdata->local->oper_channel;
+ sdata->local->hw.conf.channel = sdata->oper_channel;
}

/* XXX: shouldn't really modify cfg80211-owned data! */
- ifmgd->associated->channel = sdata->local->oper_channel;
+ ifmgd->associated->channel = sdata->oper_channel;

ieee80211_wake_queues_by_reason(&sdata->local->hw,
IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -713,7 +713,7 @@ void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
* good handling of this situation, possibly we
* should just drop the association.
*/
- sdata->local->csa_channel = sdata->local->oper_channel;
+ sdata->csa_channel = sdata->oper_channel;
}

ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
@@ -764,7 +764,7 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED)
return;

- sdata->local->csa_channel = new_ch;
+ sdata->csa_channel = new_ch;

if (sdata->local->ops->channel_switch) {
/* use driver's channel switch callback */
@@ -2021,7 +2021,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
return false;
}

- sband = local->hw.wiphy->bands[local->oper_channel->band];
+ sband = local->hw.wiphy->bands[sdata->oper_channel->band];

if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
@@ -3109,7 +3109,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
channel_type));
}

- local->oper_channel = cbss->channel;
+ sdata->oper_channel = cbss->channel;
ieee80211_hw_config(local, 0);
ieee80211_recalc_channel(sdata);

@@ -3143,7 +3143,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.basic_rates = basic_rates;

/* cf. IEEE 802.11 9.2.12 */
- if (local->oper_channel->band == IEEE80211_BAND_2GHZ &&
+ if (sdata->oper_channel->band == IEEE80211_BAND_2GHZ &&
have_higher_than_11mbit)
sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
else
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 71d642e..b20bff1 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -245,7 +245,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
if (bss)
ieee80211_rx_bss_put(sdata->local, bss);

- if (channel == sdata->local->oper_channel)
+ if (channel == sdata->oper_channel)
return RX_CONTINUE;

dev_kfree_skb(skb);
--
1.7.0.4


2012-05-10 08:59:13

by Michal Kazior

[permalink] [raw]
Subject: Re: [PATCH 7/7] mac80211: move oper_channel to sdata

Johannes Berg wrote:
> On Thu, 2012-05-10 at 08:44 +0200, Michal Kazior wrote:
>
>> + /* cfg80211 arbitrates multi-channel */
>> + if (local->hw.wiphy->flags& WIPHY_FLAG_ENFORCE_COMBINATIONS)
>> + return true;
>
> This no longer exists, but that's just arbitrating
> multi-virtual-interface, not necessarily multi-channel, I think?

Oh, my bad. You're right.

On a second thought I think this condition shouldn't be here yet
(putting aside that the flag does no longer exist).


--
Pozdrawiam / Best regards, Michal Kazior.

2012-05-10 07:57:52

by Michal Kazior

[permalink] [raw]
Subject: Re: [PATCH 1/7] mac80211: add tracking of temporary offchannel sdata

Johannes Berg wrote:
> Hi Michal,
>
>> This is necessary if we want to have a sdata-based
>> channel recalculation.
>>
>> Change-Id: I223e052146893b3ae1ca46de7d90c54ffc589f1b
>> Signed-off-by: Michal Kazior<[email protected]>
>> ---
>> net/mac80211/ieee80211_i.h | 1 +
>> net/mac80211/work.c | 2 ++
>> 2 files changed, 3 insertions(+), 0 deletions(-)
>>
>> diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
>> index b5e491b..c109960 100644
>> --- a/net/mac80211/ieee80211_i.h
>> +++ b/net/mac80211/ieee80211_i.h
>> @@ -997,6 +997,7 @@ struct ieee80211_local {
>> struct ieee80211_channel *oper_channel, *csa_channel;
>>
>> /* Temporary remain-on-channel for off-channel operations */
>> + struct ieee80211_sub_if_data *tmp_sdata;
>> struct ieee80211_channel *tmp_channel;
>> enum nl80211_channel_type tmp_channel_type;
>
> Do you actually need this? I'm still tempted to not worry about any of
> this and force drivers to implement remain-on-channel in the driver or
> device for multi-channel, and this temporary thing is only used for
> remain-on-channel operations now.

No I don't. But if it's not done we break the whole thing (sw
offchannel/scan) with my next patch. We might as well just remove all
the sw offchannel/scan code.

How many drivers are there that still depend on sw offchannel? Can we
just go ahead and break them?


--
Pozdrawiam / Best regards, Michal Kazior.

2012-05-11 12:39:49

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC/PATCH] multi-channel preparation work

On Fri, 2012-05-11 at 14:11 +0200, Johannes Berg wrote:

> You saw my patches for managing the AP channel differently. I think we
> should do the same for mesh, Thomas might actually be working on it but

Here's what I came up with -- untested!

Thomas, if I understood your other patch correctly you wanted to be able
to set the channel before the interface was up, this should allow you to
do that since it only takes effect on join mesh.

johannes


cfg80211: provide channel to join_mesh function

From: Johannes Berg <[email protected]>

Just like the AP mode patch, instead of setting
the channel and then joining the mesh network,
provide the channel to join the network on to
the join_mesh() function.

Like in AP mode, you can also give the channel
to the join-mesh nl80211 command now.

Unlike AP mode, it picks a default channel if
none was given.

Signed-off-by: Johannes Berg <[email protected]>
---
include/net/cfg80211.h | 4 +++
net/mac80211/cfg.c | 6 +++++
net/wireless/core.h | 4 +--
net/wireless/mesh.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++--
net/wireless/nl80211.c | 19 +++++++++++++++++
5 files changed, 81 insertions(+), 4 deletions(-)

--- a/include/net/cfg80211.h 2012-05-11 13:46:35.000000000 +0200
+++ b/include/net/cfg80211.h 2012-05-11 14:16:22.000000000 +0200
@@ -831,6 +831,8 @@ struct mesh_config {

/**
* struct mesh_setup - 802.11s mesh setup configuration
+ * @channel: the channel to start the mesh network on
+ * @channel_type: the channel type to use
* @mesh_id: the mesh ID
* @mesh_id_len: length of the mesh ID, at least 1 and at most 32 bytes
* @sync_method: which synchronization method to use
@@ -845,6 +847,8 @@ struct mesh_config {
* These parameters are fixed when the mesh is created.
*/
struct mesh_setup {
+ struct ieee80211_channel *channel;
+ enum nl80211_channel_type channel_type;
const u8 *mesh_id;
u8 mesh_id_len;
u8 sync_method;
--- a/net/wireless/nl80211.c 2012-05-11 13:46:35.000000000 +0200
+++ b/net/wireless/nl80211.c 2012-05-11 14:31:52.000000000 +0200
@@ -1190,6 +1190,7 @@ static int __nl80211_set_channel(struct
if (wdev) switch (wdev->iftype) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_MESH_POINT:
if (wdev->beacon_interval) {
result = -EBUSY;
break;
@@ -5996,6 +5997,24 @@ static int nl80211_join_mesh(struct sk_b
return err;
}

+ if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+ enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
+
+ if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
+ !nl80211_valid_channel_type(info, &channel_type))
+ return -EINVAL;
+
+ setup.channel = rdev_freq_to_chan(rdev,
+ nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
+ channel_type);
+ if (!setup.channel)
+ return -EINVAL;
+ setup.channel_type = channel_type;
+ } else {
+ /* cfg80211_join_mesh() will sort it out */
+ setup.channel = NULL;
+ }
+
return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
}

--- a/net/wireless/core.h 2012-05-10 15:40:30.000000000 +0200
+++ b/net/wireless/core.h 2012-05-11 14:18:31.000000000 +0200
@@ -303,11 +303,11 @@ extern const struct mesh_config default_
extern const struct mesh_setup default_mesh_setup;
int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev,
- const struct mesh_setup *setup,
+ struct mesh_setup *setup,
const struct mesh_config *conf);
int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev,
- const struct mesh_setup *setup,
+ struct mesh_setup *setup,
const struct mesh_config *conf);
int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev);
--- a/net/wireless/mesh.c 2012-05-08 13:02:00.000000000 +0200
+++ b/net/wireless/mesh.c 2012-05-11 14:35:21.000000000 +0200
@@ -65,6 +65,9 @@ const struct mesh_config default_mesh_co
};

const struct mesh_setup default_mesh_setup = {
+ /* cfg80211_join_mesh() will pick a channel if needed */
+ .channel = NULL,
+ .channel_type = NL80211_CHAN_NO_HT,
.sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,
.path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP,
.path_metric = IEEE80211_PATH_METRIC_AIRTIME,
@@ -75,7 +78,7 @@ const struct mesh_setup default_mesh_set

int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev,
- const struct mesh_setup *setup,
+ struct mesh_setup *setup,
const struct mesh_config *conf)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
@@ -101,6 +104,51 @@ int __cfg80211_join_mesh(struct cfg80211
if (!rdev->ops->join_mesh)
return -EOPNOTSUPP;

+ if (!setup->channel) {
+ /* if no channel explicitly given, use preset channel */
+ setup->channel = wdev->preset_chan;
+ setup->channel_type = wdev->preset_chantype;
+ }
+
+ if (!setup->channel) {
+ /* if we don't have that either, use the first usable channel */
+ enum ieee80211_band band;
+
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *chan;
+ int i;
+
+ sband = rdev->wiphy.bands[band];
+ if (!sband)
+ continue;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ chan = &sband->channels[i];
+ if (chan->flags & (IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN |
+ IEEE80211_CHAN_DISABLED |
+ IEEE80211_CHAN_RADAR))
+ continue;
+ setup->channel = chan;
+ break;
+ }
+
+ if (setup->channel)
+ break;
+ }
+
+ /* no usable channel ... */
+ if (!setup->channel)
+ return -EINVAL;
+
+ setup->channel_type = NL80211_CHAN_NO_HT;
+ }
+
+ if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, setup->channel,
+ setup->channel_type))
+ return -EINVAL;
+
err = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup);
if (!err) {
memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len);
@@ -112,7 +160,7 @@ int __cfg80211_join_mesh(struct cfg80211

int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev,
- const struct mesh_setup *setup,
+ struct mesh_setup *setup,
const struct mesh_config *conf)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
--- a/net/mac80211/cfg.c 2012-05-11 13:46:35.000000000 +0200
+++ b/net/mac80211/cfg.c 2012-05-11 14:31:34.000000000 +0200
@@ -1598,6 +1598,12 @@ static int ieee80211_join_mesh(struct wi
err = copy_mesh_setup(ifmsh, setup);
if (err)
return err;
+
+ err = ieee80211_set_channel(wiphy, dev, params->channel,
+ params->channel_type);
+ if (err)
+ return err;
+
ieee80211_start_mesh(sdata);

return 0;



2012-05-10 08:03:00

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 1/7] mac80211: add tracking of temporary offchannel sdata

On Thu, 2012-05-10 at 09:57 +0200, Michal Kazior wrote:

> >> /* Temporary remain-on-channel for off-channel operations */
> >> + struct ieee80211_sub_if_data *tmp_sdata;
> >> struct ieee80211_channel *tmp_channel;
> >> enum nl80211_channel_type tmp_channel_type;
> >
> > Do you actually need this? I'm still tempted to not worry about any of
> > this and force drivers to implement remain-on-channel in the driver or
> > device for multi-channel, and this temporary thing is only used for
> > remain-on-channel operations now.
>
> No I don't. But if it's not done we break the whole thing (sw
> offchannel/scan) with my next patch. We might as well just remove all
> the sw offchannel/scan code.
>
> How many drivers are there that still depend on sw offchannel? Can we
> just go ahead and break them?

Unfortunately we can't, a lot of them require it for P2P.

I guess I need to look at this in more detail.

johannes


2012-05-10 06:45:12

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 6/7] mac80211: remove _oper_channel_type

Removes _oper_channel_type since it may be
calculated on the fly now by using
ieee80211_oper_channel_type function.

Change-Id: I8b2942e3a43efb7d68ab5b6f9eb7a0265d1a3ef0
Signed-off-by: Michal Kazior <[email protected]>
---
net/mac80211/cfg.c | 12 ++++++------
net/mac80211/chan.c | 1 -
net/mac80211/ieee80211_i.h | 1 -
net/mac80211/iface.c | 7 ++++---
net/mac80211/main.c | 8 +++++---
net/mac80211/mesh.c | 7 ++++---
6 files changed, 19 insertions(+), 17 deletions(-)

diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 8b71e90..1898299 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1499,8 +1499,6 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
case CHAN_MODE_FIXED:
if (local->oper_channel != chan)
return -EBUSY;
- if (!sdata && local->_oper_channel_type == channel_type)
- return 0;
break;
case CHAN_MODE_UNDEFINED:
break;
@@ -1508,7 +1506,7 @@ static int ieee80211_set_channel(struct wiphy *wiphy,

if (sdata)
old_vif_oper_type = sdata->vif.bss_conf.channel_type;
- old_oper_type = local->_oper_channel_type;
+ old_oper_type = ieee80211_oper_channel_type(local, sdata);

if (!ieee80211_set_channel_type(local, sdata, channel_type))
return -EBUSY;
@@ -1518,7 +1516,7 @@ static int ieee80211_set_channel(struct wiphy *wiphy,

/* Update driver if changes were actually made. */
if ((old_oper != local->oper_channel) ||
- (old_oper_type != local->_oper_channel_type))
+ (old_oper_type != ieee80211_oper_channel_type(local, sdata)))
ieee80211_recalc_channel(sdata);

if (sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR &&
@@ -2064,7 +2062,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
is_offchan = true;
if (channel_type_valid &&
(channel_type != local->tmp_channel_type &&
- channel_type != local->_oper_channel_type))
+ channel_type != ieee80211_oper_channel_type(local, sdata)))
is_offchan = true;

if (chan == local->hw_roc_channel) {
@@ -2697,8 +2695,10 @@ ieee80211_wiphy_get_channel(struct wiphy *wiphy,
enum nl80211_channel_type *type)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_sub_if_data *sdata =
+ container_of(wdev, struct ieee80211_sub_if_data, wdev);

- *type = local->_oper_channel_type;
+ *type = ieee80211_oper_channel_type(local, sdata);
return local->oper_channel;
}

diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 8513f70..bfcaff8 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -153,7 +153,6 @@ bool ieee80211_set_channel_type(struct ieee80211_local *local,
if (!ieee80211_channel_types_are_compatible(superchan, chantype))
return false;

- local->_oper_channel_type = superchan;
if (sdata)
sdata->vif.bss_conf.channel_type = chantype;

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index e40cd06..3a1af25 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -993,7 +993,6 @@ struct ieee80211_local {
enum mac80211_scan_state next_scan_state;
struct delayed_work scan_work;
struct ieee80211_sub_if_data *scan_sdata;
- enum nl80211_channel_type _oper_channel_type;
struct ieee80211_channel *oper_channel, *csa_channel;

/* Temporary remain-on-channel for off-channel operations */
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index cefdf73..77b9154 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -515,7 +515,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, *tmp;
u32 hw_reconf_flags = 0;
int i;
- enum nl80211_channel_type orig_ct;
+ enum nl80211_channel_type orig_ct, new_ct;

clear_bit(SDATA_STATE_RUNNING, &sdata->state);

@@ -683,11 +683,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
/* Re-calculate channel-type, in case there are multiple vifs
* on different channel types.
*/
- orig_ct = local->_oper_channel_type;
+ orig_ct = ieee80211_oper_channel_type(local, sdata);
ieee80211_set_channel_type(local, NULL, NL80211_CHAN_NO_HT);
+ new_ct = ieee80211_oper_channel_type(local, sdata);

/* do after stop to avoid reconfiguring when we stop anyway */
- if (hw_reconf_flags || (orig_ct != local->_oper_channel_type)) {
+ if (hw_reconf_flags || orig_ct != new_ct) {
ieee80211_hw_config(local, hw_reconf_flags);
ieee80211_recalc_channel(sdata);
}
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 01c0e8b..6c05464 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -123,8 +123,10 @@ void ieee80211_recalc_channel(struct ieee80211_sub_if_data *sdata)
struct ieee80211_local *local = sdata->local;
struct ieee80211_channel *chan;
enum nl80211_channel_type channel_type;
+ enum nl80211_channel_type oper_channel_type;
u32 offchannel_flag;

+ oper_channel_type = ieee80211_oper_channel_type(local, sdata);
offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
if (local->scan_channel) {
chan = local->scan_channel;
@@ -132,7 +134,7 @@ void ieee80211_recalc_channel(struct ieee80211_sub_if_data *sdata)
* is currently in use.
*/
if (chan == local->oper_channel)
- channel_type = local->_oper_channel_type;
+ channel_type = oper_channel_type;
else
channel_type = NL80211_CHAN_NO_HT;
} else if (local->tmp_channel) {
@@ -140,11 +142,11 @@ void ieee80211_recalc_channel(struct ieee80211_sub_if_data *sdata)
channel_type = local->tmp_channel_type;
} else {
chan = local->oper_channel;
- channel_type = local->_oper_channel_type;
+ channel_type = oper_channel_type;
}

if (chan != local->oper_channel ||
- channel_type != local->_oper_channel_type)
+ channel_type != oper_channel_type)
local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL;
else
local->hw.conf.flags &= ~IEEE80211_CONF_OFFCHANNEL;
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 133c118..ac03c4b 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -101,7 +101,7 @@ bool mesh_matches_local(struct ieee802_11_elems *ie,

/* disallow peering with mismatched channel types for now */
if (ie->ht_operation &&
- (local->_oper_channel_type !=
+ (ieee80211_oper_channel_type(local, sdata) !=
ieee80211_ht_oper_to_channel_type(ie->ht_operation)))
goto mismatch;

@@ -363,7 +363,7 @@ int mesh_add_ht_cap_ie(struct sk_buff *skb,

sband = local->hw.wiphy->bands[local->oper_channel->band];
if (!sband->ht_cap.ht_supported ||
- local->_oper_channel_type == NL80211_CHAN_NO_HT)
+ ieee80211_oper_channel_type(local, sdata) == NL80211_CHAN_NO_HT)
return 0;

if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap))
@@ -380,7 +380,8 @@ int mesh_add_ht_oper_ie(struct sk_buff *skb,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_channel *channel = local->oper_channel;
- enum nl80211_channel_type channel_type = local->_oper_channel_type;
+ enum nl80211_channel_type channel_type =
+ ieee80211_oper_channel_type(local, sdata);
struct ieee80211_supported_band *sband =
local->hw.wiphy->bands[channel->band];
struct ieee80211_sta_ht_cap *ht_cap = &sband->ht_cap;
--
1.7.0.4


2012-05-10 06:45:09

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 3/7] mac80211: refactor ieee80211_set_channel_type

Splits original functionality to:
* ieee80211_get_superchan
* ieee80211_channel_types_are_compatible

Prepares for _oper_channel_type_removal.

Change-Id: Ic1bd50ad4fde619eb4fdbffdd33c440289071c9a
Signed-off-by: Michal Kazior <[email protected]>
---
net/mac80211/chan.c | 40 ++++++++++++++++++++++++++++------------
1 files changed, 28 insertions(+), 12 deletions(-)

diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index c76cf72..3234ad1 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -64,16 +64,14 @@ ieee80211_get_channel_mode(struct ieee80211_local *local,
return mode;
}

-bool ieee80211_set_channel_type(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- enum nl80211_channel_type chantype)
+static enum nl80211_channel_type
+ieee80211_get_superchan(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_sub_if_data *tmp;
enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
- bool result;

mutex_lock(&local->iflist_mtx);
-
list_for_each_entry(tmp, &local->interfaces, list) {
if (tmp == sdata)
continue;
@@ -99,6 +97,16 @@ bool ieee80211_set_channel_type(struct ieee80211_local *local,
break;
}
}
+ mutex_unlock(&local->iflist_mtx);
+
+ return superchan;
+}
+
+static bool ieee80211_channel_types_are_compatible(
+ enum nl80211_channel_type superchan,
+ enum nl80211_channel_type chantype)
+{
+ bool result = true;

switch (superchan) {
case NL80211_CHAN_NO_HT:
@@ -121,17 +129,25 @@ bool ieee80211_set_channel_type(struct ieee80211_local *local,
if (superchan == chantype)
break;
result = false;
- goto out;
+ break;
}

- local->_oper_channel_type = superchan;
+ return result;
+}
+
+bool ieee80211_set_channel_type(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ enum nl80211_channel_type chantype)
+{
+ enum nl80211_channel_type superchan;

+ superchan = ieee80211_get_superchan(local, sdata);
+ if (!ieee80211_channel_types_are_compatible(superchan, chantype))
+ return false;
+
+ local->_oper_channel_type = superchan;
if (sdata)
sdata->vif.bss_conf.channel_type = chantype;

- result = true;
- out:
- mutex_unlock(&local->iflist_mtx);
-
- return result;
+ return true;
}
--
1.7.0.4


2012-05-10 06:45:11

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 5/7] cfg80211: extend get_channel to take wdev argument

Prepares for moving oper_channel to sdata.

Change-Id: I1d66c4506a463c6b673457127a4bd595fa6018ce
Signed-off-by: Michal Kazior <[email protected]>
---
include/net/cfg80211.h | 3 ++-
net/mac80211/cfg.c | 1 +
net/wireless/nl80211.c | 7 ++++++-
net/wireless/wext-compat.c | 2 +-
4 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index d17ad5f..55b3c4b 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1711,7 +1711,8 @@ struct cfg80211_ops {
u16 noack_map);

struct ieee80211_channel *(*get_channel)(struct wiphy *wiphy,
- enum nl80211_channel_type *type);
+ struct wireless_dev *wdev,
+ enum nl80211_channel_type *type);
};

/*
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 9fa5044..8b71e90 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2693,6 +2693,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,

static struct ieee80211_channel *
ieee80211_wiphy_get_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
enum nl80211_channel_type *type)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index bcf6f70..2854a4f 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -1502,10 +1502,15 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
(cfg80211_rdev_list_generation << 2));

if (rdev->ops->get_channel) {
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
struct ieee80211_channel *chan;
enum nl80211_channel_type channel_type;

- chan = rdev->ops->get_channel(&rdev->wiphy, &channel_type);
+ wdev_lock(wdev);
+ chan = rdev->ops->get_channel(&rdev->wiphy, wdev,
+ &channel_type);
+ wdev_unlock(wdev);
+
if (chan) {
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ,
chan->center_freq);
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 6a6181a..d545afe 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -832,7 +832,7 @@ static int cfg80211_wext_giwfreq(struct net_device *dev,
if (!rdev->ops->get_channel)
return -EINVAL;

- chan = rdev->ops->get_channel(wdev->wiphy, &channel_type);
+ chan = rdev->ops->get_channel(wdev->wiphy, wdev, &channel_type);
if (!chan)
return -EINVAL;
freq->m = chan->center_freq;
--
1.7.0.4


2012-05-11 12:11:34

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC/PATCH] multi-channel preparation work

Hi Michal,

> Here's some work on multi-channel operation. It refactors channel
> switching logic with regard to ieee80211_hw_config and moves
> oper_channel stuff to sdata.

This could be useful, but it's hard to tell really. For example, if you
have a channel context, why would you need to store the channel/channel
type per sdata? Each sdata would just point to the channel context, I
think?

> I plan on doing channel contexts next. Monitor interface stuff probably
> needs to be addressed as well since it's tied tightly with set_channel.

Maybe we need to take a more radical approach in introducing
multi-channel and channel contexts :-)

You've started with very small changes like this patchset, but I can't
get a good feeling for how it'll be in the end with channel contexts.
For example, see the question above.

I think we should think about the end result / design more.

You saw my patches for managing the AP channel differently. I think we
should do the same for mesh, Thomas might actually be working on it but
if not it's pretty easy to do really. There are some questions about how
it would work though that Thomas and I may have to discuss [1].

The reason for all this is to get rid of the set_channel call. It's not
really how things should look like with multi-channel, since there we
need a clear indication of when we start and stop using a certain
channel. With the AP mode changes, we'll start using it in start_ap()
(and obviously stop using it in stop_ap()), which simplifies things.

Felix says WDS is completely broken today, so I don't think I care about
it, we should probably just remove the ability to set the channel on WDS
interfaces. Maybe even force them to be slaved to AP mode interfaces.
Felix, any thoughts? I had a patch to introduce explicit start-wds and
stop-wds operations but there were issues with error handling since I'd
have to call start-wds in NETDEV_UP for compatibility... and that can't
return errors any more [2].

That leaves monitor mode, and setting the channel when there's no
interface at all. Monitor mode is special, but we have a nice way out:
when a driver supports multi-channel it will probably need to use the
new queue scheme. Then, if _injection_ is desired, the driver needs to
implement support for explicit monitor mode interfaces anyway. So I
think we can force multi-channel capable drivers to implement explicit
monitor mode, if they want to support a real monitor mode at all.
Depending on what mesh needs, we may be able to equate "phy channel" to
"monitor mode channel", the cfg80211 callbacks pretty much make that
assumption today but inside cfg80211 it may not be true.

In any case, the result of all of this is that the cfg80211 set_channel
operation is no longer used by anything but monitor mode, we could
rename it to set_monitor_channel. This operation would only be available
when only monitor interfaces, if any interfaces at all, are active.
Internally in mac80211, the channel context for monitor interfaces would
be attached to the explicit monitor interface.

Now, if we have a driver that supports only a single channel and maybe
doesn't even support the explicit monitor interface API, then obviously
we have to translate all this to the explicit channel setting with
hw_config. However, that's not terribly difficult I think? We need to
have a limit on channel contexts anyway, so we don't create too many, so
here the limit is 1 and when we change that context we program the
hardware.

The only other difficult thing is software scanning/remain-on-channel,
i.e. the temporary channel. But this is only supported for drivers that
don't have multi-channel, so we can essentially keep it the same with
the tmp_channel variable, it just overrides the context.

I'm probably glossing over a lot of details, but at least I now have a
clearer picture of how we can do this. :-) I hope it helps explain my
thinking.

johannes

[1] The question is whether or not setting the channel on the *phy* is
needed for mesh, and if it has to support bringing up mesh without
having any channel set at all. These are backward compatibility
considerations.

[2] Alternatively, we could change the userspace API and require that
you set the WDS peer *after* bringing up the interface. Today, you have
to set it *before*. Then setting the peer could actually be the signal
to establish the "link". Given that it all doesn't work, maybe that's an
alternative? I'd have patches for this almost ready.