Skip to content

Commit

Permalink
cfg80211: provide channel to join_mesh function
Browse files Browse the repository at this point in the history
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: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Johannes Berg authored and John W. Linville committed Jun 5, 2012
1 parent 685d12a commit cc1d280
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 15 deletions.
4 changes: 4 additions & 0 deletions include/net/cfg80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions net/mac80211/cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -1598,6 +1598,12 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
err = copy_mesh_setup(ifmsh, setup);
if (err)
return err;

err = ieee80211_set_channel(wiphy, dev, setup->channel,
setup->channel_type);
if (err)
return err;

ieee80211_start_mesh(sdata);

return 0;
Expand Down
7 changes: 5 additions & 2 deletions net/wireless/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,14 +303,17 @@ extern const struct mesh_config default_mesh_config;
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);
int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, int freq,
enum nl80211_channel_type channel_type);

/* MLME */
int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
Expand Down
91 changes: 89 additions & 2 deletions net/wireless/mesh.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ const struct mesh_config default_mesh_config = {
};

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,
Expand All @@ -75,7 +78,7 @@ 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)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
Expand All @@ -101,6 +104,51 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
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);
Expand All @@ -112,7 +160,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,

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;
Expand All @@ -125,6 +173,45 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
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,
const u8 *macaddr, const u8* ie, u8 ie_len, gfp_t gfp)
{
Expand Down
43 changes: 33 additions & 10 deletions net/wireless/nl80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
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++;
if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
goto nla_put_failure;
Expand Down Expand Up @@ -1166,17 +1167,19 @@ static int parse_txq_params(struct nlattr *tb[],
static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
{
/*
* You can only set the channel explicitly for AP and
* mesh type interfaces; all others have their channel
* managed via their respective "establish a connection"
* command (connect, join, ...)
* You can only set the channel explicitly for WDS interfaces,
* all others have their channel managed via their respective
* "establish a connection" 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
* whatever else is going on, so they behave as though
* 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 ||
wdev->iftype == NL80211_IFTYPE_AP ||
Expand Down Expand Up @@ -1246,6 +1249,9 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
wdev->preset_chantype = channel_type;
result = 0;
break;
case NL80211_IFTYPE_MESH_POINT:
result = cfg80211_set_mesh_freq(rdev, wdev, freq, channel_type);
break;
default:
wdev_lock(wdev);
result = cfg80211_set_freq(rdev, wdev, freq, channel_type);
Expand Down Expand Up @@ -1335,8 +1341,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
result = 0;

mutex_lock(&rdev->mtx);
} else if (netif_running(netdev) &&
nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
} else if (nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
wdev = netdev->ieee80211_ptr;
else
wdev = NULL;
Expand Down Expand Up @@ -6080,6 +6085,24 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
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);
}

Expand Down
12 changes: 11 additions & 1 deletion net/wireless/wext-compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,6 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
case NL80211_IFTYPE_ADHOC:
return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra);
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_MESH_POINT:
freq = cfg80211_wext_freq(wdev->wiphy, wextfreq);
if (freq < 0)
return freq;
Expand All @@ -808,6 +807,17 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
wdev_unlock(wdev);
mutex_unlock(&rdev->devlist_mtx);
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:
return -EOPNOTSUPP;
}
Expand Down

0 comments on commit cc1d280

Please sign in to comment.