1 /* 2 * mac80211 - channel management 3 */ 4 5 #include <linux/nl80211.h> 6 #include <net/cfg80211.h> 7 #include "ieee80211_i.h" 8 #include "driver-ops.h" 9 10 static bool 11 ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1, 12 enum nl80211_channel_type chantype2, 13 enum nl80211_channel_type *compat) 14 { 15 /* 16 * start out with chantype1 being the result, 17 * overwriting later if needed 18 */ 19 if (compat) 20 *compat = chantype1; 21 22 switch (chantype1) { 23 case NL80211_CHAN_NO_HT: 24 if (compat) 25 *compat = chantype2; 26 break; 27 case NL80211_CHAN_HT20: 28 /* 29 * allow any change that doesn't go to no-HT 30 * (if it already is no-HT no change is needed) 31 */ 32 if (chantype2 == NL80211_CHAN_NO_HT) 33 break; 34 if (compat) 35 *compat = chantype2; 36 break; 37 case NL80211_CHAN_HT40PLUS: 38 case NL80211_CHAN_HT40MINUS: 39 /* allow smaller bandwidth and same */ 40 if (chantype2 == NL80211_CHAN_NO_HT) 41 break; 42 if (chantype2 == NL80211_CHAN_HT20) 43 break; 44 if (chantype2 == chantype1) 45 break; 46 return false; 47 } 48 49 return true; 50 } 51 52 static void ieee80211_change_chantype(struct ieee80211_local *local, 53 struct ieee80211_chanctx *ctx, 54 enum nl80211_channel_type chantype) 55 { 56 if (chantype == ctx->conf.channel_type) 57 return; 58 59 ctx->conf.channel_type = chantype; 60 drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE); 61 62 if (!local->use_chanctx) { 63 local->_oper_channel_type = chantype; 64 ieee80211_hw_config(local, 0); 65 } 66 } 67 68 static struct ieee80211_chanctx * 69 ieee80211_find_chanctx(struct ieee80211_local *local, 70 struct ieee80211_channel *channel, 71 enum nl80211_channel_type channel_type, 72 enum ieee80211_chanctx_mode mode) 73 { 74 struct ieee80211_chanctx *ctx; 75 enum nl80211_channel_type compat_type; 76 77 lockdep_assert_held(&local->chanctx_mtx); 78 79 if (mode == IEEE80211_CHANCTX_EXCLUSIVE) 80 return NULL; 81 if (WARN_ON(!channel)) 82 return NULL; 83 84 list_for_each_entry(ctx, &local->chanctx_list, list) { 85 compat_type = ctx->conf.channel_type; 86 87 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) 88 continue; 89 if (ctx->conf.channel != channel) 90 continue; 91 if (!ieee80211_channel_types_are_compatible(ctx->conf.channel_type, 92 channel_type, 93 &compat_type)) 94 continue; 95 96 ieee80211_change_chantype(local, ctx, compat_type); 97 98 return ctx; 99 } 100 101 return NULL; 102 } 103 104 static struct ieee80211_chanctx * 105 ieee80211_new_chanctx(struct ieee80211_local *local, 106 struct ieee80211_channel *channel, 107 enum nl80211_channel_type channel_type, 108 enum ieee80211_chanctx_mode mode) 109 { 110 struct ieee80211_chanctx *ctx; 111 int err; 112 113 lockdep_assert_held(&local->chanctx_mtx); 114 115 ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL); 116 if (!ctx) 117 return ERR_PTR(-ENOMEM); 118 119 ctx->conf.channel = channel; 120 ctx->conf.channel_type = channel_type; 121 ctx->mode = mode; 122 123 if (!local->use_chanctx) { 124 local->_oper_channel_type = channel_type; 125 local->_oper_channel = channel; 126 ieee80211_hw_config(local, 0); 127 } else { 128 err = drv_add_chanctx(local, ctx); 129 if (err) { 130 kfree(ctx); 131 return ERR_PTR(err); 132 } 133 } 134 135 list_add(&ctx->list, &local->chanctx_list); 136 137 return ctx; 138 } 139 140 static void ieee80211_free_chanctx(struct ieee80211_local *local, 141 struct ieee80211_chanctx *ctx) 142 { 143 lockdep_assert_held(&local->chanctx_mtx); 144 145 WARN_ON_ONCE(ctx->refcount != 0); 146 147 if (!local->use_chanctx) { 148 local->_oper_channel_type = NL80211_CHAN_NO_HT; 149 ieee80211_hw_config(local, 0); 150 } else { 151 drv_remove_chanctx(local, ctx); 152 } 153 154 list_del(&ctx->list); 155 kfree_rcu(ctx, rcu_head); 156 } 157 158 static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, 159 struct ieee80211_chanctx *ctx) 160 { 161 struct ieee80211_local *local = sdata->local; 162 int ret; 163 164 lockdep_assert_held(&local->chanctx_mtx); 165 166 ret = drv_assign_vif_chanctx(local, sdata, ctx); 167 if (ret) 168 return ret; 169 170 rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf); 171 ctx->refcount++; 172 173 return 0; 174 } 175 176 static enum nl80211_channel_type 177 ieee80211_calc_chantype(struct ieee80211_local *local, 178 struct ieee80211_chanctx *ctx) 179 { 180 struct ieee80211_chanctx_conf *conf = &ctx->conf; 181 struct ieee80211_sub_if_data *sdata; 182 enum nl80211_channel_type result = NL80211_CHAN_NO_HT; 183 184 lockdep_assert_held(&local->chanctx_mtx); 185 186 rcu_read_lock(); 187 list_for_each_entry_rcu(sdata, &local->interfaces, list) { 188 if (!ieee80211_sdata_running(sdata)) 189 continue; 190 if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) 191 continue; 192 193 WARN_ON_ONCE(!ieee80211_channel_types_are_compatible( 194 sdata->vif.bss_conf.channel_type, 195 result, &result)); 196 } 197 rcu_read_unlock(); 198 199 return result; 200 } 201 202 static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, 203 struct ieee80211_chanctx *ctx) 204 { 205 enum nl80211_channel_type chantype; 206 207 lockdep_assert_held(&local->chanctx_mtx); 208 209 chantype = ieee80211_calc_chantype(local, ctx); 210 ieee80211_change_chantype(local, ctx, chantype); 211 } 212 213 static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata, 214 struct ieee80211_chanctx *ctx) 215 { 216 struct ieee80211_local *local = sdata->local; 217 218 lockdep_assert_held(&local->chanctx_mtx); 219 220 ctx->refcount--; 221 rcu_assign_pointer(sdata->vif.chanctx_conf, NULL); 222 223 drv_unassign_vif_chanctx(local, sdata, ctx); 224 225 if (ctx->refcount > 0) 226 ieee80211_recalc_chanctx_chantype(sdata->local, ctx); 227 } 228 229 static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 230 { 231 struct ieee80211_local *local = sdata->local; 232 struct ieee80211_chanctx_conf *conf; 233 struct ieee80211_chanctx *ctx; 234 235 lockdep_assert_held(&local->chanctx_mtx); 236 237 conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 238 lockdep_is_held(&local->chanctx_mtx)); 239 if (!conf) 240 return; 241 242 ctx = container_of(conf, struct ieee80211_chanctx, conf); 243 244 ieee80211_unassign_vif_chanctx(sdata, ctx); 245 if (ctx->refcount == 0) 246 ieee80211_free_chanctx(local, ctx); 247 } 248 249 int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, 250 struct ieee80211_channel *channel, 251 enum nl80211_channel_type channel_type, 252 enum ieee80211_chanctx_mode mode) 253 { 254 struct ieee80211_local *local = sdata->local; 255 struct ieee80211_chanctx *ctx; 256 int ret; 257 258 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 259 260 mutex_lock(&local->chanctx_mtx); 261 __ieee80211_vif_release_channel(sdata); 262 263 ctx = ieee80211_find_chanctx(local, channel, channel_type, mode); 264 if (!ctx) 265 ctx = ieee80211_new_chanctx(local, channel, channel_type, mode); 266 if (IS_ERR(ctx)) { 267 ret = PTR_ERR(ctx); 268 goto out; 269 } 270 271 sdata->vif.bss_conf.channel_type = channel_type; 272 273 ret = ieee80211_assign_vif_chanctx(sdata, ctx); 274 if (ret) { 275 /* if assign fails refcount stays the same */ 276 if (ctx->refcount == 0) 277 ieee80211_free_chanctx(local, ctx); 278 goto out; 279 } 280 281 out: 282 mutex_unlock(&local->chanctx_mtx); 283 return ret; 284 } 285 286 void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) 287 { 288 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); 289 290 mutex_lock(&sdata->local->chanctx_mtx); 291 __ieee80211_vif_release_channel(sdata); 292 mutex_unlock(&sdata->local->chanctx_mtx); 293 } 294