1 /* 2 * This file contains helper code to handle channel 3 * settings and keeping track of what is possible at 4 * any point in time. 5 * 6 * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> 7 */ 8 9 #include <linux/export.h> 10 #include <net/cfg80211.h> 11 #include "core.h" 12 #include "rdev-ops.h" 13 14 void cfg80211_chandef_create(struct cfg80211_chan_def *chandef, 15 struct ieee80211_channel *chan, 16 enum nl80211_channel_type chan_type) 17 { 18 if (WARN_ON(!chan)) 19 return; 20 21 chandef->chan = chan; 22 chandef->center_freq2 = 0; 23 24 switch (chan_type) { 25 case NL80211_CHAN_NO_HT: 26 chandef->width = NL80211_CHAN_WIDTH_20_NOHT; 27 chandef->center_freq1 = chan->center_freq; 28 break; 29 case NL80211_CHAN_HT20: 30 chandef->width = NL80211_CHAN_WIDTH_20; 31 chandef->center_freq1 = chan->center_freq; 32 break; 33 case NL80211_CHAN_HT40PLUS: 34 chandef->width = NL80211_CHAN_WIDTH_40; 35 chandef->center_freq1 = chan->center_freq + 10; 36 break; 37 case NL80211_CHAN_HT40MINUS: 38 chandef->width = NL80211_CHAN_WIDTH_40; 39 chandef->center_freq1 = chan->center_freq - 10; 40 break; 41 default: 42 WARN_ON(1); 43 } 44 } 45 EXPORT_SYMBOL(cfg80211_chandef_create); 46 47 bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) 48 { 49 u32 control_freq; 50 51 if (!chandef->chan) 52 return false; 53 54 control_freq = chandef->chan->center_freq; 55 56 switch (chandef->width) { 57 case NL80211_CHAN_WIDTH_20: 58 case NL80211_CHAN_WIDTH_20_NOHT: 59 if (chandef->center_freq1 != control_freq) 60 return false; 61 if (chandef->center_freq2) 62 return false; 63 break; 64 case NL80211_CHAN_WIDTH_40: 65 if (chandef->center_freq1 != control_freq + 10 && 66 chandef->center_freq1 != control_freq - 10) 67 return false; 68 if (chandef->center_freq2) 69 return false; 70 break; 71 case NL80211_CHAN_WIDTH_80P80: 72 if (chandef->center_freq1 != control_freq + 30 && 73 chandef->center_freq1 != control_freq + 10 && 74 chandef->center_freq1 != control_freq - 10 && 75 chandef->center_freq1 != control_freq - 30) 76 return false; 77 if (!chandef->center_freq2) 78 return false; 79 break; 80 case NL80211_CHAN_WIDTH_80: 81 if (chandef->center_freq1 != control_freq + 30 && 82 chandef->center_freq1 != control_freq + 10 && 83 chandef->center_freq1 != control_freq - 10 && 84 chandef->center_freq1 != control_freq - 30) 85 return false; 86 if (chandef->center_freq2) 87 return false; 88 break; 89 case NL80211_CHAN_WIDTH_160: 90 if (chandef->center_freq1 != control_freq + 70 && 91 chandef->center_freq1 != control_freq + 50 && 92 chandef->center_freq1 != control_freq + 30 && 93 chandef->center_freq1 != control_freq + 10 && 94 chandef->center_freq1 != control_freq - 10 && 95 chandef->center_freq1 != control_freq - 30 && 96 chandef->center_freq1 != control_freq - 50 && 97 chandef->center_freq1 != control_freq - 70) 98 return false; 99 if (chandef->center_freq2) 100 return false; 101 break; 102 default: 103 return false; 104 } 105 106 return true; 107 } 108 EXPORT_SYMBOL(cfg80211_chandef_valid); 109 110 static void chandef_primary_freqs(const struct cfg80211_chan_def *c, 111 int *pri40, int *pri80) 112 { 113 int tmp; 114 115 switch (c->width) { 116 case NL80211_CHAN_WIDTH_40: 117 *pri40 = c->center_freq1; 118 *pri80 = 0; 119 break; 120 case NL80211_CHAN_WIDTH_80: 121 case NL80211_CHAN_WIDTH_80P80: 122 *pri80 = c->center_freq1; 123 /* n_P20 */ 124 tmp = (30 + c->chan->center_freq - c->center_freq1)/20; 125 /* n_P40 */ 126 tmp /= 2; 127 /* freq_P40 */ 128 *pri40 = c->center_freq1 - 20 + 40 * tmp; 129 break; 130 case NL80211_CHAN_WIDTH_160: 131 /* n_P20 */ 132 tmp = (70 + c->chan->center_freq - c->center_freq1)/20; 133 /* n_P40 */ 134 tmp /= 2; 135 /* freq_P40 */ 136 *pri40 = c->center_freq1 - 60 + 40 * tmp; 137 /* n_P80 */ 138 tmp /= 2; 139 *pri80 = c->center_freq1 - 40 + 80 * tmp; 140 break; 141 default: 142 WARN_ON_ONCE(1); 143 } 144 } 145 146 const struct cfg80211_chan_def * 147 cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, 148 const struct cfg80211_chan_def *c2) 149 { 150 u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80; 151 152 /* If they are identical, return */ 153 if (cfg80211_chandef_identical(c1, c2)) 154 return c1; 155 156 /* otherwise, must have same control channel */ 157 if (c1->chan != c2->chan) 158 return NULL; 159 160 /* 161 * If they have the same width, but aren't identical, 162 * then they can't be compatible. 163 */ 164 if (c1->width == c2->width) 165 return NULL; 166 167 if (c1->width == NL80211_CHAN_WIDTH_20_NOHT || 168 c1->width == NL80211_CHAN_WIDTH_20) 169 return c2; 170 171 if (c2->width == NL80211_CHAN_WIDTH_20_NOHT || 172 c2->width == NL80211_CHAN_WIDTH_20) 173 return c1; 174 175 chandef_primary_freqs(c1, &c1_pri40, &c1_pri80); 176 chandef_primary_freqs(c2, &c2_pri40, &c2_pri80); 177 178 if (c1_pri40 != c2_pri40) 179 return NULL; 180 181 WARN_ON(!c1_pri80 && !c2_pri80); 182 if (c1_pri80 && c2_pri80 && c1_pri80 != c2_pri80) 183 return NULL; 184 185 if (c1->width > c2->width) 186 return c1; 187 return c2; 188 } 189 EXPORT_SYMBOL(cfg80211_chandef_compatible); 190 191 static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, 192 u32 center_freq, u32 bandwidth, 193 u32 prohibited_flags) 194 { 195 struct ieee80211_channel *c; 196 u32 freq; 197 198 for (freq = center_freq - bandwidth/2 + 10; 199 freq <= center_freq + bandwidth/2 - 10; 200 freq += 20) { 201 c = ieee80211_get_channel(wiphy, freq); 202 if (!c || c->flags & prohibited_flags) 203 return false; 204 } 205 206 return true; 207 } 208 209 bool cfg80211_chandef_usable(struct wiphy *wiphy, 210 const struct cfg80211_chan_def *chandef, 211 u32 prohibited_flags) 212 { 213 struct ieee80211_sta_ht_cap *ht_cap; 214 struct ieee80211_sta_vht_cap *vht_cap; 215 u32 width, control_freq; 216 217 if (WARN_ON(!cfg80211_chandef_valid(chandef))) 218 return false; 219 220 ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap; 221 vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap; 222 223 control_freq = chandef->chan->center_freq; 224 225 switch (chandef->width) { 226 case NL80211_CHAN_WIDTH_20: 227 if (!ht_cap->ht_supported) 228 return false; 229 case NL80211_CHAN_WIDTH_20_NOHT: 230 width = 20; 231 break; 232 case NL80211_CHAN_WIDTH_40: 233 width = 40; 234 if (!ht_cap->ht_supported) 235 return false; 236 if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || 237 ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) 238 return false; 239 if (chandef->center_freq1 < control_freq && 240 chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS) 241 return false; 242 if (chandef->center_freq1 > control_freq && 243 chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS) 244 return false; 245 break; 246 case NL80211_CHAN_WIDTH_80P80: 247 if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) 248 return false; 249 case NL80211_CHAN_WIDTH_80: 250 if (!vht_cap->vht_supported) 251 return false; 252 width = 80; 253 break; 254 case NL80211_CHAN_WIDTH_160: 255 if (!vht_cap->vht_supported) 256 return false; 257 if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)) 258 return false; 259 width = 160; 260 break; 261 default: 262 WARN_ON_ONCE(1); 263 return false; 264 } 265 266 /* TODO: missing regulatory check on 80/160 bandwidth */ 267 268 if (width > 20) 269 prohibited_flags |= IEEE80211_CHAN_NO_OFDM; 270 271 if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1, 272 width, prohibited_flags)) 273 return false; 274 275 if (!chandef->center_freq2) 276 return true; 277 return cfg80211_secondary_chans_ok(wiphy, chandef->center_freq2, 278 width, prohibited_flags); 279 } 280 EXPORT_SYMBOL(cfg80211_chandef_usable); 281 282 bool cfg80211_reg_can_beacon(struct wiphy *wiphy, 283 struct cfg80211_chan_def *chandef) 284 { 285 bool res; 286 287 trace_cfg80211_reg_can_beacon(wiphy, chandef); 288 289 res = cfg80211_chandef_usable(wiphy, chandef, 290 IEEE80211_CHAN_DISABLED | 291 IEEE80211_CHAN_PASSIVE_SCAN | 292 IEEE80211_CHAN_NO_IBSS | 293 IEEE80211_CHAN_RADAR); 294 295 trace_cfg80211_return_bool(res); 296 return res; 297 } 298 EXPORT_SYMBOL(cfg80211_reg_can_beacon); 299 300 int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, 301 struct cfg80211_chan_def *chandef) 302 { 303 if (!rdev->ops->set_monitor_channel) 304 return -EOPNOTSUPP; 305 if (!cfg80211_has_monitors_only(rdev)) 306 return -EBUSY; 307 308 return rdev_set_monitor_channel(rdev, chandef); 309 } 310 311 void 312 cfg80211_get_chan_state(struct wireless_dev *wdev, 313 struct ieee80211_channel **chan, 314 enum cfg80211_chan_mode *chanmode) 315 { 316 *chan = NULL; 317 *chanmode = CHAN_MODE_UNDEFINED; 318 319 ASSERT_WDEV_LOCK(wdev); 320 321 if (wdev->netdev && !netif_running(wdev->netdev)) 322 return; 323 324 switch (wdev->iftype) { 325 case NL80211_IFTYPE_ADHOC: 326 if (wdev->current_bss) { 327 *chan = wdev->current_bss->pub.channel; 328 *chanmode = wdev->ibss_fixed 329 ? CHAN_MODE_SHARED 330 : CHAN_MODE_EXCLUSIVE; 331 return; 332 } 333 case NL80211_IFTYPE_STATION: 334 case NL80211_IFTYPE_P2P_CLIENT: 335 if (wdev->current_bss) { 336 *chan = wdev->current_bss->pub.channel; 337 *chanmode = CHAN_MODE_SHARED; 338 return; 339 } 340 break; 341 case NL80211_IFTYPE_AP: 342 case NL80211_IFTYPE_P2P_GO: 343 if (wdev->beacon_interval) { 344 *chan = wdev->channel; 345 *chanmode = CHAN_MODE_SHARED; 346 } 347 return; 348 case NL80211_IFTYPE_MESH_POINT: 349 if (wdev->mesh_id_len) { 350 *chan = wdev->channel; 351 *chanmode = CHAN_MODE_SHARED; 352 } 353 return; 354 case NL80211_IFTYPE_MONITOR: 355 case NL80211_IFTYPE_AP_VLAN: 356 case NL80211_IFTYPE_WDS: 357 /* these interface types don't really have a channel */ 358 return; 359 case NL80211_IFTYPE_P2P_DEVICE: 360 if (wdev->wiphy->features & 361 NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL) 362 *chanmode = CHAN_MODE_EXCLUSIVE; 363 return; 364 case NL80211_IFTYPE_UNSPECIFIED: 365 case NUM_NL80211_IFTYPES: 366 WARN_ON(1); 367 } 368 369 return; 370 } 371