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 void drv_sta_rc_update(struct ieee80211_local *local, 142 struct ieee80211_sub_if_data *sdata, 143 struct ieee80211_sta *sta, u32 changed) 144 { 145 sdata = get_bss_sdata(sdata); 146 if (!check_sdata_in_driver(sdata)) 147 return; 148 149 WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED && 150 (sdata->vif.type != NL80211_IFTYPE_ADHOC && 151 sdata->vif.type != NL80211_IFTYPE_MESH_POINT)); 152 153 trace_drv_sta_rc_update(local, sdata, sta, changed); 154 if (local->ops->sta_rc_update) 155 local->ops->sta_rc_update(&local->hw, &sdata->vif, 156 sta, changed); 157 158 trace_drv_return_void(local); 159 } 160 161 int drv_conf_tx(struct ieee80211_local *local, 162 struct ieee80211_sub_if_data *sdata, u16 ac, 163 const struct ieee80211_tx_queue_params *params) 164 { 165 int ret = -EOPNOTSUPP; 166 167 might_sleep(); 168 169 if (!check_sdata_in_driver(sdata)) 170 return -EIO; 171 172 if (WARN_ONCE(params->cw_min == 0 || 173 params->cw_min > params->cw_max, 174 "%s: invalid CW_min/CW_max: %d/%d\n", 175 sdata->name, params->cw_min, params->cw_max)) 176 return -EINVAL; 177 178 trace_drv_conf_tx(local, sdata, ac, params); 179 if (local->ops->conf_tx) 180 ret = local->ops->conf_tx(&local->hw, &sdata->vif, 181 ac, params); 182 trace_drv_return_int(local, ret); 183 return ret; 184 } 185 186 u64 drv_get_tsf(struct ieee80211_local *local, 187 struct ieee80211_sub_if_data *sdata) 188 { 189 u64 ret = -1ULL; 190 191 might_sleep(); 192 193 if (!check_sdata_in_driver(sdata)) 194 return ret; 195 196 trace_drv_get_tsf(local, sdata); 197 if (local->ops->get_tsf) 198 ret = local->ops->get_tsf(&local->hw, &sdata->vif); 199 trace_drv_return_u64(local, ret); 200 return ret; 201 } 202 203 void drv_set_tsf(struct ieee80211_local *local, 204 struct ieee80211_sub_if_data *sdata, 205 u64 tsf) 206 { 207 might_sleep(); 208 209 if (!check_sdata_in_driver(sdata)) 210 return; 211 212 trace_drv_set_tsf(local, sdata, tsf); 213 if (local->ops->set_tsf) 214 local->ops->set_tsf(&local->hw, &sdata->vif, tsf); 215 trace_drv_return_void(local); 216 } 217 218 void drv_reset_tsf(struct ieee80211_local *local, 219 struct ieee80211_sub_if_data *sdata) 220 { 221 might_sleep(); 222 223 if (!check_sdata_in_driver(sdata)) 224 return; 225 226 trace_drv_reset_tsf(local, sdata); 227 if (local->ops->reset_tsf) 228 local->ops->reset_tsf(&local->hw, &sdata->vif); 229 trace_drv_return_void(local); 230 } 231 232 int drv_switch_vif_chanctx(struct ieee80211_local *local, 233 struct ieee80211_vif_chanctx_switch *vifs, 234 int n_vifs, enum ieee80211_chanctx_switch_mode mode) 235 { 236 int ret = 0; 237 int i; 238 239 might_sleep(); 240 241 if (!local->ops->switch_vif_chanctx) 242 return -EOPNOTSUPP; 243 244 for (i = 0; i < n_vifs; i++) { 245 struct ieee80211_chanctx *new_ctx = 246 container_of(vifs[i].new_ctx, 247 struct ieee80211_chanctx, 248 conf); 249 struct ieee80211_chanctx *old_ctx = 250 container_of(vifs[i].old_ctx, 251 struct ieee80211_chanctx, 252 conf); 253 254 WARN_ON_ONCE(!old_ctx->driver_present); 255 WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS && 256 new_ctx->driver_present) || 257 (mode == CHANCTX_SWMODE_REASSIGN_VIF && 258 !new_ctx->driver_present)); 259 } 260 261 trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode); 262 ret = local->ops->switch_vif_chanctx(&local->hw, 263 vifs, n_vifs, mode); 264 trace_drv_return_int(local, ret); 265 266 if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) { 267 for (i = 0; i < n_vifs; i++) { 268 struct ieee80211_chanctx *new_ctx = 269 container_of(vifs[i].new_ctx, 270 struct ieee80211_chanctx, 271 conf); 272 struct ieee80211_chanctx *old_ctx = 273 container_of(vifs[i].old_ctx, 274 struct ieee80211_chanctx, 275 conf); 276 277 new_ctx->driver_present = true; 278 old_ctx->driver_present = false; 279 } 280 } 281 282 return ret; 283 } 284 285 int drv_ampdu_action(struct ieee80211_local *local, 286 struct ieee80211_sub_if_data *sdata, 287 enum ieee80211_ampdu_mlme_action action, 288 struct ieee80211_sta *sta, u16 tid, 289 u16 *ssn, u8 buf_size, bool amsdu) 290 { 291 int ret = -EOPNOTSUPP; 292 293 might_sleep(); 294 295 sdata = get_bss_sdata(sdata); 296 if (!check_sdata_in_driver(sdata)) 297 return -EIO; 298 299 trace_drv_ampdu_action(local, sdata, action, sta, tid, 300 ssn, buf_size, amsdu); 301 302 if (local->ops->ampdu_action) 303 ret = local->ops->ampdu_action(&local->hw, &sdata->vif, action, 304 sta, tid, ssn, buf_size, amsdu); 305 306 trace_drv_return_int(local, ret); 307 308 return ret; 309 } 310