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