1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright 2015 Intel Deutschland GmbH 4 */ 5 #include <net/mac80211.h> 6 #include "ieee80211_i.h" 7 #include "trace.h" 8 #include "driver-ops.h" 9 10 int drv_start(struct ieee80211_local *local) 11 { 12 int ret; 13 14 might_sleep(); 15 16 if (WARN_ON(local->started)) 17 return -EALREADY; 18 19 trace_drv_start(local); 20 local->started = true; 21 /* allow rx frames */ 22 smp_mb(); 23 ret = local->ops->start(&local->hw); 24 trace_drv_return_int(local, ret); 25 26 if (ret) 27 local->started = false; 28 29 return ret; 30 } 31 32 void drv_stop(struct ieee80211_local *local) 33 { 34 might_sleep(); 35 36 if (WARN_ON(!local->started)) 37 return; 38 39 trace_drv_stop(local); 40 local->ops->stop(&local->hw); 41 trace_drv_return_void(local); 42 43 /* sync away all work on the tasklet before clearing started */ 44 tasklet_disable(&local->tasklet); 45 tasklet_enable(&local->tasklet); 46 47 barrier(); 48 49 local->started = false; 50 } 51 52 int drv_add_interface(struct ieee80211_local *local, 53 struct ieee80211_sub_if_data *sdata) 54 { 55 int ret; 56 57 might_sleep(); 58 59 if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN || 60 (sdata->vif.type == NL80211_IFTYPE_MONITOR && 61 !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) && 62 !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE)))) 63 return -EINVAL; 64 65 trace_drv_add_interface(local, sdata); 66 ret = local->ops->add_interface(&local->hw, &sdata->vif); 67 trace_drv_return_int(local, ret); 68 69 if (ret == 0) 70 sdata->flags |= IEEE80211_SDATA_IN_DRIVER; 71 72 return ret; 73 } 74 75 int drv_change_interface(struct ieee80211_local *local, 76 struct ieee80211_sub_if_data *sdata, 77 enum nl80211_iftype type, bool p2p) 78 { 79 int ret; 80 81 might_sleep(); 82 83 if (!check_sdata_in_driver(sdata)) 84 return -EIO; 85 86 trace_drv_change_interface(local, sdata, type, p2p); 87 ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p); 88 trace_drv_return_int(local, ret); 89 return ret; 90 } 91 92 void drv_remove_interface(struct ieee80211_local *local, 93 struct ieee80211_sub_if_data *sdata) 94 { 95 might_sleep(); 96 97 if (!check_sdata_in_driver(sdata)) 98 return; 99 100 trace_drv_remove_interface(local, sdata); 101 local->ops->remove_interface(&local->hw, &sdata->vif); 102 sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; 103 trace_drv_return_void(local); 104 } 105 106 __must_check 107 int drv_sta_state(struct ieee80211_local *local, 108 struct ieee80211_sub_if_data *sdata, 109 struct sta_info *sta, 110 enum ieee80211_sta_state old_state, 111 enum ieee80211_sta_state new_state) 112 { 113 int ret = 0; 114 115 might_sleep(); 116 117 sdata = get_bss_sdata(sdata); 118 if (!check_sdata_in_driver(sdata)) 119 return -EIO; 120 121 trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state); 122 if (local->ops->sta_state) { 123 ret = local->ops->sta_state(&local->hw, &sdata->vif, &sta->sta, 124 old_state, new_state); 125 } else if (old_state == IEEE80211_STA_AUTH && 126 new_state == IEEE80211_STA_ASSOC) { 127 ret = drv_sta_add(local, sdata, &sta->sta); 128 if (ret == 0) 129 sta->uploaded = true; 130 } else if (old_state == IEEE80211_STA_ASSOC && 131 new_state == IEEE80211_STA_AUTH) { 132 drv_sta_remove(local, sdata, &sta->sta); 133 } 134 trace_drv_return_int(local, ret); 135 return ret; 136 } 137 138 __must_check 139 int drv_sta_set_txpwr(struct ieee80211_local *local, 140 struct ieee80211_sub_if_data *sdata, 141 struct sta_info *sta) 142 { 143 int ret = -EOPNOTSUPP; 144 145 might_sleep(); 146 147 sdata = get_bss_sdata(sdata); 148 if (!check_sdata_in_driver(sdata)) 149 return -EIO; 150 151 trace_drv_sta_set_txpwr(local, sdata, &sta->sta); 152 if (local->ops->sta_set_txpwr) 153 ret = local->ops->sta_set_txpwr(&local->hw, &sdata->vif, 154 &sta->sta); 155 trace_drv_return_int(local, ret); 156 return ret; 157 } 158 159 void drv_sta_rc_update(struct ieee80211_local *local, 160 struct ieee80211_sub_if_data *sdata, 161 struct ieee80211_sta *sta, u32 changed) 162 { 163 sdata = get_bss_sdata(sdata); 164 if (!check_sdata_in_driver(sdata)) 165 return; 166 167 WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED && 168 (sdata->vif.type != NL80211_IFTYPE_ADHOC && 169 sdata->vif.type != NL80211_IFTYPE_MESH_POINT)); 170 171 trace_drv_sta_rc_update(local, sdata, sta, changed); 172 if (local->ops->sta_rc_update) 173 local->ops->sta_rc_update(&local->hw, &sdata->vif, 174 sta, changed); 175 176 trace_drv_return_void(local); 177 } 178 179 int drv_conf_tx(struct ieee80211_local *local, 180 struct ieee80211_sub_if_data *sdata, u16 ac, 181 const struct ieee80211_tx_queue_params *params) 182 { 183 int ret = -EOPNOTSUPP; 184 185 might_sleep(); 186 187 if (!check_sdata_in_driver(sdata)) 188 return -EIO; 189 190 if (WARN_ONCE(params->cw_min == 0 || 191 params->cw_min > params->cw_max, 192 "%s: invalid CW_min/CW_max: %d/%d\n", 193 sdata->name, params->cw_min, params->cw_max)) 194 return -EINVAL; 195 196 trace_drv_conf_tx(local, sdata, ac, params); 197 if (local->ops->conf_tx) 198 ret = local->ops->conf_tx(&local->hw, &sdata->vif, 199 ac, params); 200 trace_drv_return_int(local, ret); 201 return ret; 202 } 203 204 u64 drv_get_tsf(struct ieee80211_local *local, 205 struct ieee80211_sub_if_data *sdata) 206 { 207 u64 ret = -1ULL; 208 209 might_sleep(); 210 211 if (!check_sdata_in_driver(sdata)) 212 return ret; 213 214 trace_drv_get_tsf(local, sdata); 215 if (local->ops->get_tsf) 216 ret = local->ops->get_tsf(&local->hw, &sdata->vif); 217 trace_drv_return_u64(local, ret); 218 return ret; 219 } 220 221 void drv_set_tsf(struct ieee80211_local *local, 222 struct ieee80211_sub_if_data *sdata, 223 u64 tsf) 224 { 225 might_sleep(); 226 227 if (!check_sdata_in_driver(sdata)) 228 return; 229 230 trace_drv_set_tsf(local, sdata, tsf); 231 if (local->ops->set_tsf) 232 local->ops->set_tsf(&local->hw, &sdata->vif, tsf); 233 trace_drv_return_void(local); 234 } 235 236 void drv_offset_tsf(struct ieee80211_local *local, 237 struct ieee80211_sub_if_data *sdata, 238 s64 offset) 239 { 240 might_sleep(); 241 242 if (!check_sdata_in_driver(sdata)) 243 return; 244 245 trace_drv_offset_tsf(local, sdata, offset); 246 if (local->ops->offset_tsf) 247 local->ops->offset_tsf(&local->hw, &sdata->vif, offset); 248 trace_drv_return_void(local); 249 } 250 251 void drv_reset_tsf(struct ieee80211_local *local, 252 struct ieee80211_sub_if_data *sdata) 253 { 254 might_sleep(); 255 256 if (!check_sdata_in_driver(sdata)) 257 return; 258 259 trace_drv_reset_tsf(local, sdata); 260 if (local->ops->reset_tsf) 261 local->ops->reset_tsf(&local->hw, &sdata->vif); 262 trace_drv_return_void(local); 263 } 264 265 int drv_switch_vif_chanctx(struct ieee80211_local *local, 266 struct ieee80211_vif_chanctx_switch *vifs, 267 int n_vifs, enum ieee80211_chanctx_switch_mode mode) 268 { 269 int ret = 0; 270 int i; 271 272 might_sleep(); 273 274 if (!local->ops->switch_vif_chanctx) 275 return -EOPNOTSUPP; 276 277 for (i = 0; i < n_vifs; i++) { 278 struct ieee80211_chanctx *new_ctx = 279 container_of(vifs[i].new_ctx, 280 struct ieee80211_chanctx, 281 conf); 282 struct ieee80211_chanctx *old_ctx = 283 container_of(vifs[i].old_ctx, 284 struct ieee80211_chanctx, 285 conf); 286 287 WARN_ON_ONCE(!old_ctx->driver_present); 288 WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS && 289 new_ctx->driver_present) || 290 (mode == CHANCTX_SWMODE_REASSIGN_VIF && 291 !new_ctx->driver_present)); 292 } 293 294 trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode); 295 ret = local->ops->switch_vif_chanctx(&local->hw, 296 vifs, n_vifs, mode); 297 trace_drv_return_int(local, ret); 298 299 if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) { 300 for (i = 0; i < n_vifs; i++) { 301 struct ieee80211_chanctx *new_ctx = 302 container_of(vifs[i].new_ctx, 303 struct ieee80211_chanctx, 304 conf); 305 struct ieee80211_chanctx *old_ctx = 306 container_of(vifs[i].old_ctx, 307 struct ieee80211_chanctx, 308 conf); 309 310 new_ctx->driver_present = true; 311 old_ctx->driver_present = false; 312 } 313 } 314 315 return ret; 316 } 317 318 int drv_ampdu_action(struct ieee80211_local *local, 319 struct ieee80211_sub_if_data *sdata, 320 struct ieee80211_ampdu_params *params) 321 { 322 int ret = -EOPNOTSUPP; 323 324 might_sleep(); 325 326 sdata = get_bss_sdata(sdata); 327 if (!check_sdata_in_driver(sdata)) 328 return -EIO; 329 330 trace_drv_ampdu_action(local, sdata, params); 331 332 if (local->ops->ampdu_action) 333 ret = local->ops->ampdu_action(&local->hw, &sdata->vif, params); 334 335 trace_drv_return_int(local, ret); 336 337 return ret; 338 } 339