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        } 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
 139int 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
 159void 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
 179int 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 (params->cw_min == 0 || params->cw_min > params->cw_max) {
 191                /*
 192                 * If we can't configure hardware anyway, don't warn. We may
 193                 * never have initialized the CW parameters.
 194                 */
 195                WARN_ONCE(local->ops->conf_tx,
 196                          "%s: invalid CW_min/CW_max: %d/%d\n",
 197                          sdata->name, params->cw_min, params->cw_max);
 198                return -EINVAL;
 199        }
 200
 201        trace_drv_conf_tx(local, sdata, ac, params);
 202        if (local->ops->conf_tx)
 203                ret = local->ops->conf_tx(&local->hw, &sdata->vif,
 204                                          ac, params);
 205        trace_drv_return_int(local, ret);
 206        return ret;
 207}
 208
 209u64 drv_get_tsf(struct ieee80211_local *local,
 210                struct ieee80211_sub_if_data *sdata)
 211{
 212        u64 ret = -1ULL;
 213
 214        might_sleep();
 215
 216        if (!check_sdata_in_driver(sdata))
 217                return ret;
 218
 219        trace_drv_get_tsf(local, sdata);
 220        if (local->ops->get_tsf)
 221                ret = local->ops->get_tsf(&local->hw, &sdata->vif);
 222        trace_drv_return_u64(local, ret);
 223        return ret;
 224}
 225
 226void drv_set_tsf(struct ieee80211_local *local,
 227                 struct ieee80211_sub_if_data *sdata,
 228                 u64 tsf)
 229{
 230        might_sleep();
 231
 232        if (!check_sdata_in_driver(sdata))
 233                return;
 234
 235        trace_drv_set_tsf(local, sdata, tsf);
 236        if (local->ops->set_tsf)
 237                local->ops->set_tsf(&local->hw, &sdata->vif, tsf);
 238        trace_drv_return_void(local);
 239}
 240
 241void drv_offset_tsf(struct ieee80211_local *local,
 242                    struct ieee80211_sub_if_data *sdata,
 243                    s64 offset)
 244{
 245        might_sleep();
 246
 247        if (!check_sdata_in_driver(sdata))
 248                return;
 249
 250        trace_drv_offset_tsf(local, sdata, offset);
 251        if (local->ops->offset_tsf)
 252                local->ops->offset_tsf(&local->hw, &sdata->vif, offset);
 253        trace_drv_return_void(local);
 254}
 255
 256void drv_reset_tsf(struct ieee80211_local *local,
 257                   struct ieee80211_sub_if_data *sdata)
 258{
 259        might_sleep();
 260
 261        if (!check_sdata_in_driver(sdata))
 262                return;
 263
 264        trace_drv_reset_tsf(local, sdata);
 265        if (local->ops->reset_tsf)
 266                local->ops->reset_tsf(&local->hw, &sdata->vif);
 267        trace_drv_return_void(local);
 268}
 269
 270int drv_switch_vif_chanctx(struct ieee80211_local *local,
 271                           struct ieee80211_vif_chanctx_switch *vifs,
 272                           int n_vifs, enum ieee80211_chanctx_switch_mode mode)
 273{
 274        int ret = 0;
 275        int i;
 276
 277        might_sleep();
 278
 279        if (!local->ops->switch_vif_chanctx)
 280                return -EOPNOTSUPP;
 281
 282        for (i = 0; i < n_vifs; i++) {
 283                struct ieee80211_chanctx *new_ctx =
 284                        container_of(vifs[i].new_ctx,
 285                                     struct ieee80211_chanctx,
 286                                     conf);
 287                struct ieee80211_chanctx *old_ctx =
 288                        container_of(vifs[i].old_ctx,
 289                                     struct ieee80211_chanctx,
 290                                     conf);
 291
 292                WARN_ON_ONCE(!old_ctx->driver_present);
 293                WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS &&
 294                              new_ctx->driver_present) ||
 295                             (mode == CHANCTX_SWMODE_REASSIGN_VIF &&
 296                              !new_ctx->driver_present));
 297        }
 298
 299        trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode);
 300        ret = local->ops->switch_vif_chanctx(&local->hw,
 301                                             vifs, n_vifs, mode);
 302        trace_drv_return_int(local, ret);
 303
 304        if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) {
 305                for (i = 0; i < n_vifs; i++) {
 306                        struct ieee80211_chanctx *new_ctx =
 307                                container_of(vifs[i].new_ctx,
 308                                             struct ieee80211_chanctx,
 309                                             conf);
 310                        struct ieee80211_chanctx *old_ctx =
 311                                container_of(vifs[i].old_ctx,
 312                                             struct ieee80211_chanctx,
 313                                             conf);
 314
 315                        new_ctx->driver_present = true;
 316                        old_ctx->driver_present = false;
 317                }
 318        }
 319
 320        return ret;
 321}
 322
 323int drv_ampdu_action(struct ieee80211_local *local,
 324                     struct ieee80211_sub_if_data *sdata,
 325                     struct ieee80211_ampdu_params *params)
 326{
 327        int ret = -EOPNOTSUPP;
 328
 329        might_sleep();
 330
 331        sdata = get_bss_sdata(sdata);
 332        if (!check_sdata_in_driver(sdata))
 333                return -EIO;
 334
 335        trace_drv_ampdu_action(local, sdata, params);
 336
 337        if (local->ops->ampdu_action)
 338                ret = local->ops->ampdu_action(&local->hw, &sdata->vif, params);
 339
 340        trace_drv_return_int(local, ret);
 341
 342        return ret;
 343}
 344