driver-ops.c (8179B)
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 10int 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 32void 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 52int 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 75int 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 92void 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 107int 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 if (rcu_access_pointer(sta->sta.rates)) 131 drv_sta_rate_tbl_update(local, sdata, &sta->sta); 132 } 133 } else if (old_state == IEEE80211_STA_ASSOC && 134 new_state == IEEE80211_STA_AUTH) { 135 drv_sta_remove(local, sdata, &sta->sta); 136 } 137 trace_drv_return_int(local, ret); 138 return ret; 139} 140 141__must_check 142int drv_sta_set_txpwr(struct ieee80211_local *local, 143 struct ieee80211_sub_if_data *sdata, 144 struct sta_info *sta) 145{ 146 int ret = -EOPNOTSUPP; 147 148 might_sleep(); 149 150 sdata = get_bss_sdata(sdata); 151 if (!check_sdata_in_driver(sdata)) 152 return -EIO; 153 154 trace_drv_sta_set_txpwr(local, sdata, &sta->sta); 155 if (local->ops->sta_set_txpwr) 156 ret = local->ops->sta_set_txpwr(&local->hw, &sdata->vif, 157 &sta->sta); 158 trace_drv_return_int(local, ret); 159 return ret; 160} 161 162void drv_sta_rc_update(struct ieee80211_local *local, 163 struct ieee80211_sub_if_data *sdata, 164 struct ieee80211_sta *sta, u32 changed) 165{ 166 sdata = get_bss_sdata(sdata); 167 if (!check_sdata_in_driver(sdata)) 168 return; 169 170 WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED && 171 (sdata->vif.type != NL80211_IFTYPE_ADHOC && 172 sdata->vif.type != NL80211_IFTYPE_MESH_POINT)); 173 174 trace_drv_sta_rc_update(local, sdata, sta, changed); 175 if (local->ops->sta_rc_update) 176 local->ops->sta_rc_update(&local->hw, &sdata->vif, 177 sta, changed); 178 179 trace_drv_return_void(local); 180} 181 182int drv_conf_tx(struct ieee80211_local *local, 183 struct ieee80211_sub_if_data *sdata, u16 ac, 184 const struct ieee80211_tx_queue_params *params) 185{ 186 int ret = -EOPNOTSUPP; 187 188 might_sleep(); 189 190 if (!check_sdata_in_driver(sdata)) 191 return -EIO; 192 193 if (params->cw_min == 0 || params->cw_min > params->cw_max) { 194 /* 195 * If we can't configure hardware anyway, don't warn. We may 196 * never have initialized the CW parameters. 197 */ 198 WARN_ONCE(local->ops->conf_tx, 199 "%s: invalid CW_min/CW_max: %d/%d\n", 200 sdata->name, params->cw_min, params->cw_max); 201 return -EINVAL; 202 } 203 204 trace_drv_conf_tx(local, sdata, ac, params); 205 if (local->ops->conf_tx) 206 ret = local->ops->conf_tx(&local->hw, &sdata->vif, 207 ac, params); 208 trace_drv_return_int(local, ret); 209 return ret; 210} 211 212u64 drv_get_tsf(struct ieee80211_local *local, 213 struct ieee80211_sub_if_data *sdata) 214{ 215 u64 ret = -1ULL; 216 217 might_sleep(); 218 219 if (!check_sdata_in_driver(sdata)) 220 return ret; 221 222 trace_drv_get_tsf(local, sdata); 223 if (local->ops->get_tsf) 224 ret = local->ops->get_tsf(&local->hw, &sdata->vif); 225 trace_drv_return_u64(local, ret); 226 return ret; 227} 228 229void drv_set_tsf(struct ieee80211_local *local, 230 struct ieee80211_sub_if_data *sdata, 231 u64 tsf) 232{ 233 might_sleep(); 234 235 if (!check_sdata_in_driver(sdata)) 236 return; 237 238 trace_drv_set_tsf(local, sdata, tsf); 239 if (local->ops->set_tsf) 240 local->ops->set_tsf(&local->hw, &sdata->vif, tsf); 241 trace_drv_return_void(local); 242} 243 244void drv_offset_tsf(struct ieee80211_local *local, 245 struct ieee80211_sub_if_data *sdata, 246 s64 offset) 247{ 248 might_sleep(); 249 250 if (!check_sdata_in_driver(sdata)) 251 return; 252 253 trace_drv_offset_tsf(local, sdata, offset); 254 if (local->ops->offset_tsf) 255 local->ops->offset_tsf(&local->hw, &sdata->vif, offset); 256 trace_drv_return_void(local); 257} 258 259void drv_reset_tsf(struct ieee80211_local *local, 260 struct ieee80211_sub_if_data *sdata) 261{ 262 might_sleep(); 263 264 if (!check_sdata_in_driver(sdata)) 265 return; 266 267 trace_drv_reset_tsf(local, sdata); 268 if (local->ops->reset_tsf) 269 local->ops->reset_tsf(&local->hw, &sdata->vif); 270 trace_drv_return_void(local); 271} 272 273int drv_switch_vif_chanctx(struct ieee80211_local *local, 274 struct ieee80211_vif_chanctx_switch *vifs, 275 int n_vifs, enum ieee80211_chanctx_switch_mode mode) 276{ 277 int ret = 0; 278 int i; 279 280 might_sleep(); 281 282 if (!local->ops->switch_vif_chanctx) 283 return -EOPNOTSUPP; 284 285 for (i = 0; i < n_vifs; i++) { 286 struct ieee80211_chanctx *new_ctx = 287 container_of(vifs[i].new_ctx, 288 struct ieee80211_chanctx, 289 conf); 290 struct ieee80211_chanctx *old_ctx = 291 container_of(vifs[i].old_ctx, 292 struct ieee80211_chanctx, 293 conf); 294 295 WARN_ON_ONCE(!old_ctx->driver_present); 296 WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS && 297 new_ctx->driver_present) || 298 (mode == CHANCTX_SWMODE_REASSIGN_VIF && 299 !new_ctx->driver_present)); 300 } 301 302 trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode); 303 ret = local->ops->switch_vif_chanctx(&local->hw, 304 vifs, n_vifs, mode); 305 trace_drv_return_int(local, ret); 306 307 if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) { 308 for (i = 0; i < n_vifs; i++) { 309 struct ieee80211_chanctx *new_ctx = 310 container_of(vifs[i].new_ctx, 311 struct ieee80211_chanctx, 312 conf); 313 struct ieee80211_chanctx *old_ctx = 314 container_of(vifs[i].old_ctx, 315 struct ieee80211_chanctx, 316 conf); 317 318 new_ctx->driver_present = true; 319 old_ctx->driver_present = false; 320 } 321 } 322 323 return ret; 324} 325 326int drv_ampdu_action(struct ieee80211_local *local, 327 struct ieee80211_sub_if_data *sdata, 328 struct ieee80211_ampdu_params *params) 329{ 330 int ret = -EOPNOTSUPP; 331 332 might_sleep(); 333 334 sdata = get_bss_sdata(sdata); 335 if (!check_sdata_in_driver(sdata)) 336 return -EIO; 337 338 trace_drv_ampdu_action(local, sdata, params); 339 340 if (local->ops->ampdu_action) 341 ret = local->ops->ampdu_action(&local->hw, &sdata->vif, params); 342 343 trace_drv_return_int(local, ret); 344 345 return ret; 346}