diff options
author | Johannes Berg <johannes.berg@intel.com> | 2012-05-16 17:50:20 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-06-05 15:32:18 -0400 |
commit | cc1d2806bf06ab92268343d26eb3d8d8f00f8bc9 (patch) | |
tree | b14ffa920d616470715e715df49f990bfdcf12ae /net | |
parent | 685d12a1929f274bd91497e33b4255fe164ac8ec (diff) |
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: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/cfg.c | 6 | ||||
-rw-r--r-- | net/wireless/core.h | 7 | ||||
-rw-r--r-- | net/wireless/mesh.c | 91 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 43 | ||||
-rw-r--r-- | net/wireless/wext-compat.c | 12 |
5 files changed, 144 insertions, 15 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 8e9d525c4653..f47af8b3185e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -1598,6 +1598,12 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev, | |||
1598 | err = copy_mesh_setup(ifmsh, setup); | 1598 | err = copy_mesh_setup(ifmsh, setup); |
1599 | if (err) | 1599 | if (err) |
1600 | return err; | 1600 | return err; |
1601 | |||
1602 | err = ieee80211_set_channel(wiphy, dev, setup->channel, | ||
1603 | setup->channel_type); | ||
1604 | if (err) | ||
1605 | return err; | ||
1606 | |||
1601 | ieee80211_start_mesh(sdata); | 1607 | ieee80211_start_mesh(sdata); |
1602 | 1608 | ||
1603 | return 0; | 1609 | return 0; |
diff --git a/net/wireless/core.h b/net/wireless/core.h index 8523f3878677..1d3d24126946 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -303,14 +303,17 @@ extern const struct mesh_config default_mesh_config; | |||
303 | extern const struct mesh_setup default_mesh_setup; | 303 | extern const struct mesh_setup default_mesh_setup; |
304 | int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, | 304 | int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, |
305 | struct net_device *dev, | 305 | struct net_device *dev, |
306 | const struct mesh_setup *setup, | 306 | struct mesh_setup *setup, |
307 | const struct mesh_config *conf); | 307 | const struct mesh_config *conf); |
308 | int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, | 308 | int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, |
309 | struct net_device *dev, | 309 | struct net_device *dev, |
310 | const struct mesh_setup *setup, | 310 | struct mesh_setup *setup, |
311 | const struct mesh_config *conf); | 311 | const struct mesh_config *conf); |
312 | int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, | 312 | int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, |
313 | struct net_device *dev); | 313 | struct net_device *dev); |
314 | int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, | ||
315 | struct wireless_dev *wdev, int freq, | ||
316 | enum nl80211_channel_type channel_type); | ||
314 | 317 | ||
315 | /* MLME */ | 318 | /* MLME */ |
316 | int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, | 319 | int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, |
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 2749cb86b462..2e3b700eba32 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c | |||
@@ -65,6 +65,9 @@ const struct mesh_config default_mesh_config = { | |||
65 | }; | 65 | }; |
66 | 66 | ||
67 | const struct mesh_setup default_mesh_setup = { | 67 | const struct mesh_setup default_mesh_setup = { |
68 | /* cfg80211_join_mesh() will pick a channel if needed */ | ||
69 | .channel = NULL, | ||
70 | .channel_type = NL80211_CHAN_NO_HT, | ||
68 | .sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET, | 71 | .sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET, |
69 | .path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP, | 72 | .path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP, |
70 | .path_metric = IEEE80211_PATH_METRIC_AIRTIME, | 73 | .path_metric = IEEE80211_PATH_METRIC_AIRTIME, |
@@ -75,7 +78,7 @@ const struct mesh_setup default_mesh_setup = { | |||
75 | 78 | ||
76 | int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, | 79 | int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, |
77 | struct net_device *dev, | 80 | struct net_device *dev, |
78 | const struct mesh_setup *setup, | 81 | struct mesh_setup *setup, |
79 | const struct mesh_config *conf) | 82 | const struct mesh_config *conf) |
80 | { | 83 | { |
81 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 84 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
@@ -101,6 +104,51 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, | |||
101 | if (!rdev->ops->join_mesh) | 104 | if (!rdev->ops->join_mesh) |
102 | return -EOPNOTSUPP; | 105 | return -EOPNOTSUPP; |
103 | 106 | ||
107 | if (!setup->channel) { | ||
108 | /* if no channel explicitly given, use preset channel */ | ||
109 | setup->channel = wdev->preset_chan; | ||
110 | setup->channel_type = wdev->preset_chantype; | ||
111 | } | ||
112 | |||
113 | if (!setup->channel) { | ||
114 | /* if we don't have that either, use the first usable channel */ | ||
115 | enum ieee80211_band band; | ||
116 | |||
117 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | ||
118 | struct ieee80211_supported_band *sband; | ||
119 | struct ieee80211_channel *chan; | ||
120 | int i; | ||
121 | |||
122 | sband = rdev->wiphy.bands[band]; | ||
123 | if (!sband) | ||
124 | continue; | ||
125 | |||
126 | for (i = 0; i < sband->n_channels; i++) { | ||
127 | chan = &sband->channels[i]; | ||
128 | if (chan->flags & (IEEE80211_CHAN_NO_IBSS | | ||
129 | IEEE80211_CHAN_PASSIVE_SCAN | | ||
130 | IEEE80211_CHAN_DISABLED | | ||
131 | IEEE80211_CHAN_RADAR)) | ||
132 | continue; | ||
133 | setup->channel = chan; | ||
134 | break; | ||
135 | } | ||
136 | |||
137 | if (setup->channel) | ||
138 | break; | ||
139 | } | ||
140 | |||
141 | /* no usable channel ... */ | ||
142 | if (!setup->channel) | ||
143 | return -EINVAL; | ||
144 | |||
145 | setup->channel_type = NL80211_CHAN_NO_HT; | ||
146 | } | ||
147 | |||
148 | if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, setup->channel, | ||
149 | setup->channel_type)) | ||
150 | return -EINVAL; | ||
151 | |||
104 | err = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup); | 152 | err = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup); |
105 | if (!err) { | 153 | if (!err) { |
106 | memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len); | 154 | memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len); |
@@ -112,7 +160,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, | |||
112 | 160 | ||
113 | int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, | 161 | int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, |
114 | struct net_device *dev, | 162 | struct net_device *dev, |
115 | const struct mesh_setup *setup, | 163 | struct mesh_setup *setup, |
116 | const struct mesh_config *conf) | 164 | const struct mesh_config *conf) |
117 | { | 165 | { |
118 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 166 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
@@ -125,6 +173,45 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, | |||
125 | return err; | 173 | return err; |
126 | } | 174 | } |
127 | 175 | ||
176 | int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, | ||
177 | struct wireless_dev *wdev, int freq, | ||
178 | enum nl80211_channel_type channel_type) | ||
179 | { | ||
180 | struct ieee80211_channel *channel; | ||
181 | |||
182 | /* | ||
183 | * Workaround for libertas (only!), it puts the interface | ||
184 | * into mesh mode but doesn't implement join_mesh. Instead, | ||
185 | * it is configured via sysfs and then joins the mesh when | ||
186 | * you set the channel. Note that the libertas mesh isn't | ||
187 | * compatible with 802.11 mesh. | ||
188 | */ | ||
189 | if (!rdev->ops->join_mesh) { | ||
190 | int err; | ||
191 | |||
192 | if (!netif_running(wdev->netdev)) | ||
193 | return -ENETDOWN; | ||
194 | wdev_lock(wdev); | ||
195 | err = cfg80211_set_freq(rdev, wdev, freq, channel_type); | ||
196 | wdev_unlock(wdev); | ||
197 | |||
198 | return err; | ||
199 | } | ||
200 | |||
201 | if (wdev->mesh_id_len) | ||
202 | return -EBUSY; | ||
203 | |||
204 | channel = rdev_freq_to_chan(rdev, freq, channel_type); | ||
205 | if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy, | ||
206 | channel, | ||
207 | channel_type)) { | ||
208 | return -EINVAL; | ||
209 | } | ||
210 | wdev->preset_chan = channel; | ||
211 | wdev->preset_chantype = channel_type; | ||
212 | return 0; | ||
213 | } | ||
214 | |||
128 | void cfg80211_notify_new_peer_candidate(struct net_device *dev, | 215 | void cfg80211_notify_new_peer_candidate(struct net_device *dev, |
129 | const u8 *macaddr, const u8* ie, u8 ie_len, gfp_t gfp) | 216 | const u8 *macaddr, const u8* ie, u8 ie_len, gfp_t gfp) |
130 | { | 217 | { |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 089a5204dad5..b22f1f876881 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -921,7 +921,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
921 | if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS)) | 921 | if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS)) |
922 | goto nla_put_failure; | 922 | goto nla_put_failure; |
923 | } | 923 | } |
924 | if (dev->ops->set_channel || dev->ops->start_ap) { | 924 | if (dev->ops->set_channel || dev->ops->start_ap || |
925 | dev->ops->join_mesh) { | ||
925 | i++; | 926 | i++; |
926 | if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL)) | 927 | if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL)) |
927 | goto nla_put_failure; | 928 | goto nla_put_failure; |
@@ -1166,17 +1167,19 @@ static int parse_txq_params(struct nlattr *tb[], | |||
1166 | static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) | 1167 | static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) |
1167 | { | 1168 | { |
1168 | /* | 1169 | /* |
1169 | * You can only set the channel explicitly for AP and | 1170 | * You can only set the channel explicitly for WDS interfaces, |
1170 | * mesh type interfaces; all others have their channel | 1171 | * all others have their channel managed via their respective |
1171 | * managed via their respective "establish a connection" | 1172 | * "establish a connection" command (connect, join, ...) |
1172 | * command (connect, join, ...) | 1173 | * |
1174 | * For AP/GO and mesh mode, the channel can be set with the | ||
1175 | * channel userspace API, but is only stored and passed to the | ||
1176 | * low-level driver when the AP starts or the mesh is joined. | ||
1177 | * This is for backward compatibility, userspace can also give | ||
1178 | * the channel in the start-ap or join-mesh commands instead. | ||
1173 | * | 1179 | * |
1174 | * Monitors are special as they are normally slaved to | 1180 | * Monitors are special as they are normally slaved to |
1175 | * whatever else is going on, so they behave as though | 1181 | * whatever else is going on, so they behave as though |
1176 | * you tried setting the wiphy channel itself. | 1182 | * you tried setting the wiphy channel itself. |
1177 | * | ||
1178 | * For AP/GO modes, it's only for compatibility, you can | ||
1179 | * also give the channel to the start-AP command. | ||
1180 | */ | 1183 | */ |
1181 | return !wdev || | 1184 | return !wdev || |
1182 | wdev->iftype == NL80211_IFTYPE_AP || | 1185 | wdev->iftype == NL80211_IFTYPE_AP || |
@@ -1246,6 +1249,9 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, | |||
1246 | wdev->preset_chantype = channel_type; | 1249 | wdev->preset_chantype = channel_type; |
1247 | result = 0; | 1250 | result = 0; |
1248 | break; | 1251 | break; |
1252 | case NL80211_IFTYPE_MESH_POINT: | ||
1253 | result = cfg80211_set_mesh_freq(rdev, wdev, freq, channel_type); | ||
1254 | break; | ||
1249 | default: | 1255 | default: |
1250 | wdev_lock(wdev); | 1256 | wdev_lock(wdev); |
1251 | result = cfg80211_set_freq(rdev, wdev, freq, channel_type); | 1257 | 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 | result = 0; | 1341 | result = 0; |
1336 | 1342 | ||
1337 | mutex_lock(&rdev->mtx); | 1343 | mutex_lock(&rdev->mtx); |
1338 | } else if (netif_running(netdev) && | 1344 | } else if (nl80211_can_set_dev_channel(netdev->ieee80211_ptr)) |
1339 | nl80211_can_set_dev_channel(netdev->ieee80211_ptr)) | ||
1340 | wdev = netdev->ieee80211_ptr; | 1345 | wdev = netdev->ieee80211_ptr; |
1341 | else | 1346 | else |
1342 | wdev = NULL; | 1347 | wdev = NULL; |
@@ -6080,6 +6085,24 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) | |||
6080 | return err; | 6085 | return err; |
6081 | } | 6086 | } |
6082 | 6087 | ||
6088 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { | ||
6089 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
6090 | |||
6091 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && | ||
6092 | !nl80211_valid_channel_type(info, &channel_type)) | ||
6093 | return -EINVAL; | ||
6094 | |||
6095 | setup.channel = rdev_freq_to_chan(rdev, | ||
6096 | nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]), | ||
6097 | channel_type); | ||
6098 | if (!setup.channel) | ||
6099 | return -EINVAL; | ||
6100 | setup.channel_type = channel_type; | ||
6101 | } else { | ||
6102 | /* cfg80211_join_mesh() will sort it out */ | ||
6103 | setup.channel = NULL; | ||
6104 | } | ||
6105 | |||
6083 | return cfg80211_join_mesh(rdev, dev, &setup, &cfg); | 6106 | return cfg80211_join_mesh(rdev, dev, &setup, &cfg); |
6084 | } | 6107 | } |
6085 | 6108 | ||
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index b082fcc26f06..faeb03548aa4 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c | |||
@@ -796,7 +796,6 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, | |||
796 | case NL80211_IFTYPE_ADHOC: | 796 | case NL80211_IFTYPE_ADHOC: |
797 | return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra); | 797 | return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra); |
798 | case NL80211_IFTYPE_MONITOR: | 798 | case NL80211_IFTYPE_MONITOR: |
799 | case NL80211_IFTYPE_MESH_POINT: | ||
800 | freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); | 799 | freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); |
801 | if (freq < 0) | 800 | if (freq < 0) |
802 | return freq; | 801 | return freq; |
@@ -808,6 +807,17 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, | |||
808 | wdev_unlock(wdev); | 807 | wdev_unlock(wdev); |
809 | mutex_unlock(&rdev->devlist_mtx); | 808 | mutex_unlock(&rdev->devlist_mtx); |
810 | return err; | 809 | return err; |
810 | case NL80211_IFTYPE_MESH_POINT: | ||
811 | freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); | ||
812 | if (freq < 0) | ||
813 | return freq; | ||
814 | if (freq == 0) | ||
815 | return -EINVAL; | ||
816 | mutex_lock(&rdev->devlist_mtx); | ||
817 | err = cfg80211_set_mesh_freq(rdev, wdev, freq, | ||
818 | NL80211_CHAN_NO_HT); | ||
819 | mutex_unlock(&rdev->devlist_mtx); | ||
820 | return err; | ||
811 | default: | 821 | default: |
812 | return -EOPNOTSUPP; | 822 | return -EOPNOTSUPP; |
813 | } | 823 | } |