linux/net/mac80211/driver-ops.c
<<
>>
Prefs
   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}
 347