Commit cc1d2806 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville

cfg80211: provide channel to join_mesh function

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.

As libertas uses mesh mode interfaces but has
no join_mesh callback and we can't simply break
it, keep some compatibility code for that case
and configure the channel directly for it.

In the non-libertas case, where we store the
channel until join, allow setting it while the
interface is down.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 685d12a1
...@@ -831,6 +831,8 @@ struct mesh_config { ...@@ -831,6 +831,8 @@ struct mesh_config {
/** /**
* struct mesh_setup - 802.11s mesh setup configuration * 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: the mesh ID
* @mesh_id_len: length of the mesh ID, at least 1 and at most 32 bytes * @mesh_id_len: length of the mesh ID, at least 1 and at most 32 bytes
* @sync_method: which synchronization method to use * @sync_method: which synchronization method to use
...@@ -845,6 +847,8 @@ struct mesh_config { ...@@ -845,6 +847,8 @@ struct mesh_config {
* These parameters are fixed when the mesh is created. * These parameters are fixed when the mesh is created.
*/ */
struct mesh_setup { struct mesh_setup {
struct ieee80211_channel *channel;
enum nl80211_channel_type channel_type;
const u8 *mesh_id; const u8 *mesh_id;
u8 mesh_id_len; u8 mesh_id_len;
u8 sync_method; u8 sync_method;
......
...@@ -1598,6 +1598,12 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev, ...@@ -1598,6 +1598,12 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
err = copy_mesh_setup(ifmsh, setup); err = copy_mesh_setup(ifmsh, setup);
if (err) if (err)
return err; return err;
err = ieee80211_set_channel(wiphy, dev, setup->channel,
setup->channel_type);
if (err)
return err;
ieee80211_start_mesh(sdata); ieee80211_start_mesh(sdata);
return 0; return 0;
......
...@@ -303,14 +303,17 @@ extern const struct mesh_config default_mesh_config; ...@@ -303,14 +303,17 @@ extern const struct mesh_config default_mesh_config;
extern const struct mesh_setup default_mesh_setup; extern const struct mesh_setup default_mesh_setup;
int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev, struct net_device *dev,
const struct mesh_setup *setup, struct mesh_setup *setup,
const struct mesh_config *conf); const struct mesh_config *conf);
int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev, struct net_device *dev,
const struct mesh_setup *setup, struct mesh_setup *setup,
const struct mesh_config *conf); const struct mesh_config *conf);
int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev); struct net_device *dev);
int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, int freq,
enum nl80211_channel_type channel_type);
/* MLME */ /* MLME */
int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
......
...@@ -65,6 +65,9 @@ const struct mesh_config default_mesh_config = { ...@@ -65,6 +65,9 @@ const struct mesh_config default_mesh_config = {
}; };
const struct mesh_setup default_mesh_setup = { 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, .sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,
.path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP, .path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP,
.path_metric = IEEE80211_PATH_METRIC_AIRTIME, .path_metric = IEEE80211_PATH_METRIC_AIRTIME,
...@@ -75,7 +78,7 @@ const struct mesh_setup default_mesh_setup = { ...@@ -75,7 +78,7 @@ const struct mesh_setup default_mesh_setup = {
int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev, struct net_device *dev,
const struct mesh_setup *setup, struct mesh_setup *setup,
const struct mesh_config *conf) const struct mesh_config *conf)
{ {
struct wireless_dev *wdev = dev->ieee80211_ptr; struct wireless_dev *wdev = dev->ieee80211_ptr;
...@@ -101,6 +104,51 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, ...@@ -101,6 +104,51 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
if (!rdev->ops->join_mesh) if (!rdev->ops->join_mesh)
return -EOPNOTSUPP; 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); err = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup);
if (!err) { if (!err) {
memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len); memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len);
...@@ -112,7 +160,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, ...@@ -112,7 +160,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev, struct net_device *dev,
const struct mesh_setup *setup, struct mesh_setup *setup,
const struct mesh_config *conf) const struct mesh_config *conf)
{ {
struct wireless_dev *wdev = dev->ieee80211_ptr; struct wireless_dev *wdev = dev->ieee80211_ptr;
...@@ -125,6 +173,45 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, ...@@ -125,6 +173,45 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
return err; return err;
} }
int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, int freq,
enum nl80211_channel_type channel_type)
{
struct ieee80211_channel *channel;
/*
* Workaround for libertas (only!), it puts the interface
* into mesh mode but doesn't implement join_mesh. Instead,
* it is configured via sysfs and then joins the mesh when
* you set the channel. Note that the libertas mesh isn't
* compatible with 802.11 mesh.
*/
if (!rdev->ops->join_mesh) {
int err;
if (!netif_running(wdev->netdev))
return -ENETDOWN;
wdev_lock(wdev);
err = cfg80211_set_freq(rdev, wdev, freq, channel_type);
wdev_unlock(wdev);
return err;
}
if (wdev->mesh_id_len)
return -EBUSY;
channel = rdev_freq_to_chan(rdev, freq, channel_type);
if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy,
channel,
channel_type)) {
return -EINVAL;
}
wdev->preset_chan = channel;
wdev->preset_chantype = channel_type;
return 0;
}
void cfg80211_notify_new_peer_candidate(struct net_device *dev, void cfg80211_notify_new_peer_candidate(struct net_device *dev,
const u8 *macaddr, const u8* ie, u8 ie_len, gfp_t gfp) const u8 *macaddr, const u8* ie, u8 ie_len, gfp_t gfp)
{ {
......
...@@ -921,7 +921,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, ...@@ -921,7 +921,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS)) if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
goto nla_put_failure; goto nla_put_failure;
} }
if (dev->ops->set_channel || dev->ops->start_ap) { if (dev->ops->set_channel || dev->ops->start_ap ||
dev->ops->join_mesh) {
i++; i++;
if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL)) if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
goto nla_put_failure; goto nla_put_failure;
...@@ -1166,17 +1167,19 @@ static int parse_txq_params(struct nlattr *tb[], ...@@ -1166,17 +1167,19 @@ static int parse_txq_params(struct nlattr *tb[],
static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
{ {
/* /*
* You can only set the channel explicitly for AP and * You can only set the channel explicitly for WDS interfaces,
* mesh type interfaces; all others have their channel * all others have their channel managed via their respective
* managed via their respective "establish a connection" * "establish a connection" command (connect, join, ...)
* command (connect, join, ...) *
* For AP/GO and mesh mode, the channel can be set with the
* channel userspace API, but is only stored and passed to the
* low-level driver when the AP starts or the mesh is joined.
* This is for backward compatibility, userspace can also give
* the channel in the start-ap or join-mesh commands instead.
* *
* Monitors are special as they are normally slaved to * Monitors are special as they are normally slaved to
* whatever else is going on, so they behave as though * whatever else is going on, so they behave as though
* you tried setting the wiphy channel itself. * you tried setting the wiphy channel itself.
*
* For AP/GO modes, it's only for compatibility, you can
* also give the channel to the start-AP command.
*/ */
return !wdev || return !wdev ||
wdev->iftype == NL80211_IFTYPE_AP || wdev->iftype == NL80211_IFTYPE_AP ||
...@@ -1246,6 +1249,9 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, ...@@ -1246,6 +1249,9 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
wdev->preset_chantype = channel_type; wdev->preset_chantype = channel_type;
result = 0; result = 0;
break; break;
case NL80211_IFTYPE_MESH_POINT:
result = cfg80211_set_mesh_freq(rdev, wdev, freq, channel_type);
break;
default: default:
wdev_lock(wdev); wdev_lock(wdev);
result = cfg80211_set_freq(rdev, wdev, freq, channel_type); result = cfg80211_set_freq(rdev, wdev, freq, channel_type);
...@@ -1335,8 +1341,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) ...@@ -1335,8 +1341,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
result = 0; result = 0;
mutex_lock(&rdev->mtx); mutex_lock(&rdev->mtx);
} else if (netif_running(netdev) && } else if (nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
wdev = netdev->ieee80211_ptr; wdev = netdev->ieee80211_ptr;
else else
wdev = NULL; wdev = NULL;
...@@ -6080,6 +6085,24 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) ...@@ -6080,6 +6085,24 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
return err; 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); return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
} }
......
...@@ -796,7 +796,6 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, ...@@ -796,7 +796,6 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra); return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra);
case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_MESH_POINT:
freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
if (freq < 0) if (freq < 0)
return freq; return freq;
...@@ -808,6 +807,17 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, ...@@ -808,6 +807,17 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
wdev_unlock(wdev); wdev_unlock(wdev);
mutex_unlock(&rdev->devlist_mtx); mutex_unlock(&rdev->devlist_mtx);
return err; return err;
case NL80211_IFTYPE_MESH_POINT:
freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
if (freq < 0)
return freq;
if (freq == 0)
return -EINVAL;
mutex_lock(&rdev->devlist_mtx);
err = cfg80211_set_mesh_freq(rdev, wdev, freq,
NL80211_CHAN_NO_HT);
mutex_unlock(&rdev->devlist_mtx);
return err;
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment