diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 129 |
1 files changed, 106 insertions, 23 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 206465dc0cab..7ae54b82291f 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -921,7 +921,12 @@ 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 | CMD(set_channel, SET_CHANNEL); | 924 | if (dev->ops->set_monitor_channel || dev->ops->start_ap || |
925 | dev->ops->join_mesh) { | ||
926 | i++; | ||
927 | if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL)) | ||
928 | goto nla_put_failure; | ||
929 | } | ||
925 | CMD(set_wds_peer, SET_WDS_PEER); | 930 | CMD(set_wds_peer, SET_WDS_PEER); |
926 | if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) { | 931 | if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) { |
927 | CMD(tdls_mgmt, TDLS_MGMT); | 932 | CMD(tdls_mgmt, TDLS_MGMT); |
@@ -1162,18 +1167,22 @@ static int parse_txq_params(struct nlattr *tb[], | |||
1162 | static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) | 1167 | static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) |
1163 | { | 1168 | { |
1164 | /* | 1169 | /* |
1165 | * You can only set the channel explicitly for AP, mesh | 1170 | * You can only set the channel explicitly for WDS interfaces, |
1166 | * and WDS type interfaces; all others have their channel | 1171 | * all others have their channel managed via their respective |
1167 | * managed via their respective "establish a connection" | 1172 | * "establish a connection" command (connect, join, ...) |
1168 | * 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. | ||
1169 | * | 1179 | * |
1170 | * Monitors are special as they are normally slaved to | 1180 | * Monitors are special as they are normally slaved to |
1171 | * whatever else is going on, so they behave as though | 1181 | * whatever else is going on, so they have their own special |
1172 | * you tried setting the wiphy channel itself. | 1182 | * operation to set the monitor channel if possible. |
1173 | */ | 1183 | */ |
1174 | return !wdev || | 1184 | return !wdev || |
1175 | wdev->iftype == NL80211_IFTYPE_AP || | 1185 | wdev->iftype == NL80211_IFTYPE_AP || |
1176 | wdev->iftype == NL80211_IFTYPE_WDS || | ||
1177 | wdev->iftype == NL80211_IFTYPE_MESH_POINT || | 1186 | wdev->iftype == NL80211_IFTYPE_MESH_POINT || |
1178 | wdev->iftype == NL80211_IFTYPE_MONITOR || | 1187 | wdev->iftype == NL80211_IFTYPE_MONITOR || |
1179 | wdev->iftype == NL80211_IFTYPE_P2P_GO; | 1188 | wdev->iftype == NL80211_IFTYPE_P2P_GO; |
@@ -1204,9 +1213,14 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, | |||
1204 | struct wireless_dev *wdev, | 1213 | struct wireless_dev *wdev, |
1205 | struct genl_info *info) | 1214 | struct genl_info *info) |
1206 | { | 1215 | { |
1216 | struct ieee80211_channel *channel; | ||
1207 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | 1217 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; |
1208 | u32 freq; | 1218 | u32 freq; |
1209 | int result; | 1219 | int result; |
1220 | enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR; | ||
1221 | |||
1222 | if (wdev) | ||
1223 | iftype = wdev->iftype; | ||
1210 | 1224 | ||
1211 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) | 1225 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) |
1212 | return -EINVAL; | 1226 | return -EINVAL; |
@@ -1221,12 +1235,32 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, | |||
1221 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | 1235 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); |
1222 | 1236 | ||
1223 | mutex_lock(&rdev->devlist_mtx); | 1237 | mutex_lock(&rdev->devlist_mtx); |
1224 | if (wdev) { | 1238 | switch (iftype) { |
1225 | wdev_lock(wdev); | 1239 | case NL80211_IFTYPE_AP: |
1226 | result = cfg80211_set_freq(rdev, wdev, freq, channel_type); | 1240 | case NL80211_IFTYPE_P2P_GO: |
1227 | wdev_unlock(wdev); | 1241 | if (wdev->beacon_interval) { |
1228 | } else { | 1242 | result = -EBUSY; |
1229 | result = cfg80211_set_freq(rdev, NULL, freq, channel_type); | 1243 | break; |
1244 | } | ||
1245 | channel = rdev_freq_to_chan(rdev, freq, channel_type); | ||
1246 | if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy, | ||
1247 | channel, | ||
1248 | channel_type)) { | ||
1249 | result = -EINVAL; | ||
1250 | break; | ||
1251 | } | ||
1252 | wdev->preset_chan = channel; | ||
1253 | wdev->preset_chantype = channel_type; | ||
1254 | result = 0; | ||
1255 | break; | ||
1256 | case NL80211_IFTYPE_MESH_POINT: | ||
1257 | result = cfg80211_set_mesh_freq(rdev, wdev, freq, channel_type); | ||
1258 | break; | ||
1259 | case NL80211_IFTYPE_MONITOR: | ||
1260 | result = cfg80211_set_monitor_channel(rdev, freq, channel_type); | ||
1261 | break; | ||
1262 | default: | ||
1263 | result = -EINVAL; | ||
1230 | } | 1264 | } |
1231 | mutex_unlock(&rdev->devlist_mtx); | 1265 | mutex_unlock(&rdev->devlist_mtx); |
1232 | 1266 | ||
@@ -1310,8 +1344,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
1310 | result = 0; | 1344 | result = 0; |
1311 | 1345 | ||
1312 | mutex_lock(&rdev->mtx); | 1346 | mutex_lock(&rdev->mtx); |
1313 | } else if (netif_running(netdev) && | 1347 | } else if (nl80211_can_set_dev_channel(netdev->ieee80211_ptr)) |
1314 | nl80211_can_set_dev_channel(netdev->ieee80211_ptr)) | ||
1315 | wdev = netdev->ieee80211_ptr; | 1348 | wdev = netdev->ieee80211_ptr; |
1316 | else | 1349 | else |
1317 | wdev = NULL; | 1350 | wdev = NULL; |
@@ -2299,6 +2332,29 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) | |||
2299 | info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]); | 2332 | info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]); |
2300 | } | 2333 | } |
2301 | 2334 | ||
2335 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { | ||
2336 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
2337 | |||
2338 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && | ||
2339 | !nl80211_valid_channel_type(info, &channel_type)) | ||
2340 | return -EINVAL; | ||
2341 | |||
2342 | params.channel = rdev_freq_to_chan(rdev, | ||
2343 | nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]), | ||
2344 | channel_type); | ||
2345 | if (!params.channel) | ||
2346 | return -EINVAL; | ||
2347 | params.channel_type = channel_type; | ||
2348 | } else if (wdev->preset_chan) { | ||
2349 | params.channel = wdev->preset_chan; | ||
2350 | params.channel_type = wdev->preset_chantype; | ||
2351 | } else | ||
2352 | return -EINVAL; | ||
2353 | |||
2354 | if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, params.channel, | ||
2355 | params.channel_type)) | ||
2356 | return -EINVAL; | ||
2357 | |||
2302 | err = rdev->ops->start_ap(&rdev->wiphy, dev, ¶ms); | 2358 | err = rdev->ops->start_ap(&rdev->wiphy, dev, ¶ms); |
2303 | if (!err) | 2359 | if (!err) |
2304 | wdev->beacon_interval = params.beacon_interval; | 2360 | wdev->beacon_interval = params.beacon_interval; |
@@ -5489,18 +5545,18 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, | |||
5489 | 5545 | ||
5490 | duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); | 5546 | duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); |
5491 | 5547 | ||
5548 | if (!rdev->ops->remain_on_channel || | ||
5549 | !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)) | ||
5550 | return -EOPNOTSUPP; | ||
5551 | |||
5492 | /* | 5552 | /* |
5493 | * We should be on that channel for at least one jiffie, | 5553 | * We should be on that channel for at least a minimum amount of |
5494 | * and more than 5 seconds seems excessive. | 5554 | * time (10ms) but no longer than the driver supports. |
5495 | */ | 5555 | */ |
5496 | if (!duration || !msecs_to_jiffies(duration) || | 5556 | if (duration < NL80211_MIN_REMAIN_ON_CHANNEL_TIME || |
5497 | duration > rdev->wiphy.max_remain_on_channel_duration) | 5557 | duration > rdev->wiphy.max_remain_on_channel_duration) |
5498 | return -EINVAL; | 5558 | return -EINVAL; |
5499 | 5559 | ||
5500 | if (!rdev->ops->remain_on_channel || | ||
5501 | !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)) | ||
5502 | return -EOPNOTSUPP; | ||
5503 | |||
5504 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && | 5560 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && |
5505 | !nl80211_valid_channel_type(info, &channel_type)) | 5561 | !nl80211_valid_channel_type(info, &channel_type)) |
5506 | return -EINVAL; | 5562 | return -EINVAL; |
@@ -5771,6 +5827,15 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) | |||
5771 | if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) | 5827 | if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) |
5772 | return -EINVAL; | 5828 | return -EINVAL; |
5773 | wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); | 5829 | wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); |
5830 | |||
5831 | /* | ||
5832 | * We should wait on the channel for at least a minimum amount | ||
5833 | * of time (10ms) but no longer than the driver supports. | ||
5834 | */ | ||
5835 | if (wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME || | ||
5836 | wait > rdev->wiphy.max_remain_on_channel_duration) | ||
5837 | return -EINVAL; | ||
5838 | |||
5774 | } | 5839 | } |
5775 | 5840 | ||
5776 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | 5841 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { |
@@ -6032,6 +6097,24 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) | |||
6032 | return err; | 6097 | return err; |
6033 | } | 6098 | } |
6034 | 6099 | ||
6100 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { | ||
6101 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
6102 | |||
6103 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && | ||
6104 | !nl80211_valid_channel_type(info, &channel_type)) | ||
6105 | return -EINVAL; | ||
6106 | |||
6107 | setup.channel = rdev_freq_to_chan(rdev, | ||
6108 | nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]), | ||
6109 | channel_type); | ||
6110 | if (!setup.channel) | ||
6111 | return -EINVAL; | ||
6112 | setup.channel_type = channel_type; | ||
6113 | } else { | ||
6114 | /* cfg80211_join_mesh() will sort it out */ | ||
6115 | setup.channel = NULL; | ||
6116 | } | ||
6117 | |||
6035 | return cfg80211_join_mesh(rdev, dev, &setup, &cfg); | 6118 | return cfg80211_join_mesh(rdev, dev, &setup, &cfg); |
6036 | } | 6119 | } |
6037 | 6120 | ||