linux/drivers/net/wireless/ath/wcn36xx/main.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
   3 *
   4 * Permission to use, copy, modify, and/or distribute this software for any
   5 * purpose with or without fee is hereby granted, provided that the above
   6 * copyright notice and this permission notice appear in all copies.
   7 *
   8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15 */
  16
  17#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  18
  19#include <linux/module.h>
  20#include <linux/firmware.h>
  21#include <linux/platform_device.h>
  22#include <linux/of_address.h>
  23#include <linux/of_device.h>
  24#include "wcn36xx.h"
  25
  26unsigned int wcn36xx_dbg_mask;
  27module_param_named(debug_mask, wcn36xx_dbg_mask, uint, 0644);
  28MODULE_PARM_DESC(debug_mask, "Debugging mask");
  29
  30#define CHAN2G(_freq, _idx) { \
  31        .band = NL80211_BAND_2GHZ, \
  32        .center_freq = (_freq), \
  33        .hw_value = (_idx), \
  34        .max_power = 25, \
  35}
  36
  37#define CHAN5G(_freq, _idx) { \
  38        .band = NL80211_BAND_5GHZ, \
  39        .center_freq = (_freq), \
  40        .hw_value = (_idx), \
  41        .max_power = 25, \
  42}
  43
  44/* The wcn firmware expects channel values to matching
  45 * their mnemonic values. So use these for .hw_value. */
  46static struct ieee80211_channel wcn_2ghz_channels[] = {
  47        CHAN2G(2412, 1), /* Channel 1 */
  48        CHAN2G(2417, 2), /* Channel 2 */
  49        CHAN2G(2422, 3), /* Channel 3 */
  50        CHAN2G(2427, 4), /* Channel 4 */
  51        CHAN2G(2432, 5), /* Channel 5 */
  52        CHAN2G(2437, 6), /* Channel 6 */
  53        CHAN2G(2442, 7), /* Channel 7 */
  54        CHAN2G(2447, 8), /* Channel 8 */
  55        CHAN2G(2452, 9), /* Channel 9 */
  56        CHAN2G(2457, 10), /* Channel 10 */
  57        CHAN2G(2462, 11), /* Channel 11 */
  58        CHAN2G(2467, 12), /* Channel 12 */
  59        CHAN2G(2472, 13), /* Channel 13 */
  60        CHAN2G(2484, 14)  /* Channel 14 */
  61
  62};
  63
  64static struct ieee80211_channel wcn_5ghz_channels[] = {
  65        CHAN5G(5180, 36),
  66        CHAN5G(5200, 40),
  67        CHAN5G(5220, 44),
  68        CHAN5G(5240, 48),
  69        CHAN5G(5260, 52),
  70        CHAN5G(5280, 56),
  71        CHAN5G(5300, 60),
  72        CHAN5G(5320, 64),
  73        CHAN5G(5500, 100),
  74        CHAN5G(5520, 104),
  75        CHAN5G(5540, 108),
  76        CHAN5G(5560, 112),
  77        CHAN5G(5580, 116),
  78        CHAN5G(5600, 120),
  79        CHAN5G(5620, 124),
  80        CHAN5G(5640, 128),
  81        CHAN5G(5660, 132),
  82        CHAN5G(5700, 140),
  83        CHAN5G(5745, 149),
  84        CHAN5G(5765, 153),
  85        CHAN5G(5785, 157),
  86        CHAN5G(5805, 161),
  87        CHAN5G(5825, 165)
  88};
  89
  90#define RATE(_bitrate, _hw_rate, _flags) { \
  91        .bitrate        = (_bitrate),                   \
  92        .flags          = (_flags),                     \
  93        .hw_value       = (_hw_rate),                   \
  94        .hw_value_short = (_hw_rate)  \
  95}
  96
  97static struct ieee80211_rate wcn_2ghz_rates[] = {
  98        RATE(10, HW_RATE_INDEX_1MBPS, 0),
  99        RATE(20, HW_RATE_INDEX_2MBPS, IEEE80211_RATE_SHORT_PREAMBLE),
 100        RATE(55, HW_RATE_INDEX_5_5MBPS, IEEE80211_RATE_SHORT_PREAMBLE),
 101        RATE(110, HW_RATE_INDEX_11MBPS, IEEE80211_RATE_SHORT_PREAMBLE),
 102        RATE(60, HW_RATE_INDEX_6MBPS, 0),
 103        RATE(90, HW_RATE_INDEX_9MBPS, 0),
 104        RATE(120, HW_RATE_INDEX_12MBPS, 0),
 105        RATE(180, HW_RATE_INDEX_18MBPS, 0),
 106        RATE(240, HW_RATE_INDEX_24MBPS, 0),
 107        RATE(360, HW_RATE_INDEX_36MBPS, 0),
 108        RATE(480, HW_RATE_INDEX_48MBPS, 0),
 109        RATE(540, HW_RATE_INDEX_54MBPS, 0)
 110};
 111
 112static struct ieee80211_rate wcn_5ghz_rates[] = {
 113        RATE(60, HW_RATE_INDEX_6MBPS, 0),
 114        RATE(90, HW_RATE_INDEX_9MBPS, 0),
 115        RATE(120, HW_RATE_INDEX_12MBPS, 0),
 116        RATE(180, HW_RATE_INDEX_18MBPS, 0),
 117        RATE(240, HW_RATE_INDEX_24MBPS, 0),
 118        RATE(360, HW_RATE_INDEX_36MBPS, 0),
 119        RATE(480, HW_RATE_INDEX_48MBPS, 0),
 120        RATE(540, HW_RATE_INDEX_54MBPS, 0)
 121};
 122
 123static struct ieee80211_supported_band wcn_band_2ghz = {
 124        .channels       = wcn_2ghz_channels,
 125        .n_channels     = ARRAY_SIZE(wcn_2ghz_channels),
 126        .bitrates       = wcn_2ghz_rates,
 127        .n_bitrates     = ARRAY_SIZE(wcn_2ghz_rates),
 128        .ht_cap         = {
 129                .cap =  IEEE80211_HT_CAP_GRN_FLD |
 130                        IEEE80211_HT_CAP_SGI_20 |
 131                        IEEE80211_HT_CAP_DSSSCCK40 |
 132                        IEEE80211_HT_CAP_LSIG_TXOP_PROT,
 133                .ht_supported = true,
 134                .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
 135                .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
 136                .mcs = {
 137                        .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
 138                        .rx_highest = cpu_to_le16(72),
 139                        .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
 140                }
 141        }
 142};
 143
 144static struct ieee80211_supported_band wcn_band_5ghz = {
 145        .channels       = wcn_5ghz_channels,
 146        .n_channels     = ARRAY_SIZE(wcn_5ghz_channels),
 147        .bitrates       = wcn_5ghz_rates,
 148        .n_bitrates     = ARRAY_SIZE(wcn_5ghz_rates),
 149        .ht_cap         = {
 150                .cap =  IEEE80211_HT_CAP_GRN_FLD |
 151                        IEEE80211_HT_CAP_SGI_20 |
 152                        IEEE80211_HT_CAP_DSSSCCK40 |
 153                        IEEE80211_HT_CAP_LSIG_TXOP_PROT |
 154                        IEEE80211_HT_CAP_SGI_40 |
 155                        IEEE80211_HT_CAP_SUP_WIDTH_20_40,
 156                .ht_supported = true,
 157                .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
 158                .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
 159                .mcs = {
 160                        .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
 161                        .rx_highest = cpu_to_le16(72),
 162                        .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
 163                }
 164        }
 165};
 166
 167#ifdef CONFIG_PM
 168
 169static const struct wiphy_wowlan_support wowlan_support = {
 170        .flags = WIPHY_WOWLAN_ANY
 171};
 172
 173#endif
 174
 175static inline u8 get_sta_index(struct ieee80211_vif *vif,
 176                               struct wcn36xx_sta *sta_priv)
 177{
 178        return NL80211_IFTYPE_STATION == vif->type ?
 179               sta_priv->bss_sta_index :
 180               sta_priv->sta_index;
 181}
 182
 183static const char * const wcn36xx_caps_names[] = {
 184        "MCC",                          /* 0 */
 185        "P2P",                          /* 1 */
 186        "DOT11AC",                      /* 2 */
 187        "SLM_SESSIONIZATION",           /* 3 */
 188        "DOT11AC_OPMODE",               /* 4 */
 189        "SAP32STA",                     /* 5 */
 190        "TDLS",                         /* 6 */
 191        "P2P_GO_NOA_DECOUPLE_INIT_SCAN",/* 7 */
 192        "WLANACTIVE_OFFLOAD",           /* 8 */
 193        "BEACON_OFFLOAD",               /* 9 */
 194        "SCAN_OFFLOAD",                 /* 10 */
 195        "ROAM_OFFLOAD",                 /* 11 */
 196        "BCN_MISS_OFFLOAD",             /* 12 */
 197        "STA_POWERSAVE",                /* 13 */
 198        "STA_ADVANCED_PWRSAVE",         /* 14 */
 199        "AP_UAPSD",                     /* 15 */
 200        "AP_DFS",                       /* 16 */
 201        "BLOCKACK",                     /* 17 */
 202        "PHY_ERR",                      /* 18 */
 203        "BCN_FILTER",                   /* 19 */
 204        "RTT",                          /* 20 */
 205        "RATECTRL",                     /* 21 */
 206        "WOW",                          /* 22 */
 207        "WLAN_ROAM_SCAN_OFFLOAD",       /* 23 */
 208        "SPECULATIVE_PS_POLL",          /* 24 */
 209        "SCAN_SCH",                     /* 25 */
 210        "IBSS_HEARTBEAT_OFFLOAD",       /* 26 */
 211        "WLAN_SCAN_OFFLOAD",            /* 27 */
 212        "WLAN_PERIODIC_TX_PTRN",        /* 28 */
 213        "ADVANCE_TDLS",                 /* 29 */
 214        "BATCH_SCAN",                   /* 30 */
 215        "FW_IN_TX_PATH",                /* 31 */
 216        "EXTENDED_NSOFFLOAD_SLOT",      /* 32 */
 217        "CH_SWITCH_V1",                 /* 33 */
 218        "HT40_OBSS_SCAN",               /* 34 */
 219        "UPDATE_CHANNEL_LIST",          /* 35 */
 220        "WLAN_MCADDR_FLT",              /* 36 */
 221        "WLAN_CH144",                   /* 37 */
 222        "NAN",                          /* 38 */
 223        "TDLS_SCAN_COEXISTENCE",        /* 39 */
 224        "LINK_LAYER_STATS_MEAS",        /* 40 */
 225        "MU_MIMO",                      /* 41 */
 226        "EXTENDED_SCAN",                /* 42 */
 227        "DYNAMIC_WMM_PS",               /* 43 */
 228        "MAC_SPOOFED_SCAN",             /* 44 */
 229        "BMU_ERROR_GENERIC_RECOVERY",   /* 45 */
 230        "DISA",                         /* 46 */
 231        "FW_STATS",                     /* 47 */
 232        "WPS_PRBRSP_TMPL",              /* 48 */
 233        "BCN_IE_FLT_DELTA",             /* 49 */
 234        "TDLS_OFF_CHANNEL",             /* 51 */
 235        "RTT3",                         /* 52 */
 236        "MGMT_FRAME_LOGGING",           /* 53 */
 237        "ENHANCED_TXBD_COMPLETION",     /* 54 */
 238        "LOGGING_ENHANCEMENT",          /* 55 */
 239        "EXT_SCAN_ENHANCED",            /* 56 */
 240        "MEMORY_DUMP_SUPPORTED",        /* 57 */
 241        "PER_PKT_STATS_SUPPORTED",      /* 58 */
 242        "EXT_LL_STAT",                  /* 60 */
 243        "WIFI_CONFIG",                  /* 61 */
 244        "ANTENNA_DIVERSITY_SELECTION",  /* 62 */
 245};
 246
 247static const char *wcn36xx_get_cap_name(enum place_holder_in_cap_bitmap x)
 248{
 249        if (x >= ARRAY_SIZE(wcn36xx_caps_names))
 250                return "UNKNOWN";
 251        return wcn36xx_caps_names[x];
 252}
 253
 254static void wcn36xx_feat_caps_info(struct wcn36xx *wcn)
 255{
 256        int i;
 257
 258        for (i = 0; i < MAX_FEATURE_SUPPORTED; i++) {
 259                if (get_feat_caps(wcn->fw_feat_caps, i))
 260                        wcn36xx_info("FW Cap %s\n", wcn36xx_get_cap_name(i));
 261        }
 262}
 263
 264static int wcn36xx_start(struct ieee80211_hw *hw)
 265{
 266        struct wcn36xx *wcn = hw->priv;
 267        int ret;
 268
 269        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac start\n");
 270
 271        /* SMD initialization */
 272        ret = wcn36xx_smd_open(wcn);
 273        if (ret) {
 274                wcn36xx_err("Failed to open smd channel: %d\n", ret);
 275                goto out_err;
 276        }
 277
 278        /* Allocate memory pools for Mgmt BD headers and Data BD headers */
 279        ret = wcn36xx_dxe_allocate_mem_pools(wcn);
 280        if (ret) {
 281                wcn36xx_err("Failed to alloc DXE mempool: %d\n", ret);
 282                goto out_smd_close;
 283        }
 284
 285        ret = wcn36xx_dxe_alloc_ctl_blks(wcn);
 286        if (ret) {
 287                wcn36xx_err("Failed to alloc DXE ctl blocks: %d\n", ret);
 288                goto out_free_dxe_pool;
 289        }
 290
 291        wcn->hal_buf = kmalloc(WCN36XX_HAL_BUF_SIZE, GFP_KERNEL);
 292        if (!wcn->hal_buf) {
 293                wcn36xx_err("Failed to allocate smd buf\n");
 294                ret = -ENOMEM;
 295                goto out_free_dxe_ctl;
 296        }
 297
 298        ret = wcn36xx_smd_load_nv(wcn);
 299        if (ret) {
 300                wcn36xx_err("Failed to push NV to chip\n");
 301                goto out_free_smd_buf;
 302        }
 303
 304        ret = wcn36xx_smd_start(wcn);
 305        if (ret) {
 306                wcn36xx_err("Failed to start chip\n");
 307                goto out_free_smd_buf;
 308        }
 309
 310        if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) {
 311                ret = wcn36xx_smd_feature_caps_exchange(wcn);
 312                if (ret)
 313                        wcn36xx_warn("Exchange feature caps failed\n");
 314                else
 315                        wcn36xx_feat_caps_info(wcn);
 316        }
 317
 318        /* DMA channel initialization */
 319        ret = wcn36xx_dxe_init(wcn);
 320        if (ret) {
 321                wcn36xx_err("DXE init failed\n");
 322                goto out_smd_stop;
 323        }
 324
 325        wcn36xx_debugfs_init(wcn);
 326
 327        INIT_LIST_HEAD(&wcn->vif_list);
 328        spin_lock_init(&wcn->dxe_lock);
 329
 330        return 0;
 331
 332out_smd_stop:
 333        wcn36xx_smd_stop(wcn);
 334out_free_smd_buf:
 335        kfree(wcn->hal_buf);
 336out_free_dxe_pool:
 337        wcn36xx_dxe_free_mem_pools(wcn);
 338out_free_dxe_ctl:
 339        wcn36xx_dxe_free_ctl_blks(wcn);
 340out_smd_close:
 341        wcn36xx_smd_close(wcn);
 342out_err:
 343        return ret;
 344}
 345
 346static void wcn36xx_stop(struct ieee80211_hw *hw)
 347{
 348        struct wcn36xx *wcn = hw->priv;
 349
 350        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac stop\n");
 351
 352        wcn36xx_debugfs_exit(wcn);
 353        wcn36xx_smd_stop(wcn);
 354        wcn36xx_dxe_deinit(wcn);
 355        wcn36xx_smd_close(wcn);
 356
 357        wcn36xx_dxe_free_mem_pools(wcn);
 358        wcn36xx_dxe_free_ctl_blks(wcn);
 359
 360        kfree(wcn->hal_buf);
 361}
 362
 363static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed)
 364{
 365        struct wcn36xx *wcn = hw->priv;
 366        struct ieee80211_vif *vif = NULL;
 367        struct wcn36xx_vif *tmp;
 368
 369        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac config changed 0x%08x\n", changed);
 370
 371        if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
 372                int ch = WCN36XX_HW_CHANNEL(wcn);
 373                wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n",
 374                            ch);
 375                list_for_each_entry(tmp, &wcn->vif_list, list) {
 376                        vif = wcn36xx_priv_to_vif(tmp);
 377                        wcn36xx_smd_switch_channel(wcn, vif, ch);
 378                }
 379        }
 380
 381        return 0;
 382}
 383
 384static void wcn36xx_configure_filter(struct ieee80211_hw *hw,
 385                                     unsigned int changed,
 386                                     unsigned int *total, u64 multicast)
 387{
 388        struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp;
 389        struct wcn36xx *wcn = hw->priv;
 390        struct wcn36xx_vif *tmp;
 391        struct ieee80211_vif *vif = NULL;
 392
 393        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac configure filter\n");
 394
 395        *total &= FIF_ALLMULTI;
 396
 397        fp = (void *)(unsigned long)multicast;
 398        list_for_each_entry(tmp, &wcn->vif_list, list) {
 399                vif = wcn36xx_priv_to_vif(tmp);
 400
 401                /* FW handles MC filtering only when connected as STA */
 402                if (*total & FIF_ALLMULTI)
 403                        wcn36xx_smd_set_mc_list(wcn, vif, NULL);
 404                else if (NL80211_IFTYPE_STATION == vif->type && tmp->sta_assoc)
 405                        wcn36xx_smd_set_mc_list(wcn, vif, fp);
 406        }
 407        kfree(fp);
 408}
 409
 410static u64 wcn36xx_prepare_multicast(struct ieee80211_hw *hw,
 411                                     struct netdev_hw_addr_list *mc_list)
 412{
 413        struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp;
 414        struct netdev_hw_addr *ha;
 415
 416        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac prepare multicast list\n");
 417        fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
 418        if (!fp) {
 419                wcn36xx_err("Out of memory setting filters.\n");
 420                return 0;
 421        }
 422
 423        fp->mc_addr_count = 0;
 424        /* update multicast filtering parameters */
 425        if (netdev_hw_addr_list_count(mc_list) <=
 426            WCN36XX_HAL_MAX_NUM_MULTICAST_ADDRESS) {
 427                netdev_hw_addr_list_for_each(ha, mc_list) {
 428                        memcpy(fp->mc_addr[fp->mc_addr_count],
 429                                        ha->addr, ETH_ALEN);
 430                        fp->mc_addr_count++;
 431                }
 432        }
 433
 434        return (u64)(unsigned long)fp;
 435}
 436
 437static void wcn36xx_tx(struct ieee80211_hw *hw,
 438                       struct ieee80211_tx_control *control,
 439                       struct sk_buff *skb)
 440{
 441        struct wcn36xx *wcn = hw->priv;
 442        struct wcn36xx_sta *sta_priv = NULL;
 443
 444        if (control->sta)
 445                sta_priv = wcn36xx_sta_to_priv(control->sta);
 446
 447        if (wcn36xx_start_tx(wcn, sta_priv, skb))
 448                ieee80211_free_txskb(wcn->hw, skb);
 449}
 450
 451static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 452                           struct ieee80211_vif *vif,
 453                           struct ieee80211_sta *sta,
 454                           struct ieee80211_key_conf *key_conf)
 455{
 456        struct wcn36xx *wcn = hw->priv;
 457        struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
 458        struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
 459        int ret = 0;
 460        u8 key[WLAN_MAX_KEY_LEN];
 461
 462        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 set key\n");
 463        wcn36xx_dbg(WCN36XX_DBG_MAC, "Key: cmd=0x%x algo:0x%x, id:%d, len:%d flags 0x%x\n",
 464                    cmd, key_conf->cipher, key_conf->keyidx,
 465                    key_conf->keylen, key_conf->flags);
 466        wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "KEY: ",
 467                         key_conf->key,
 468                         key_conf->keylen);
 469
 470        switch (key_conf->cipher) {
 471        case WLAN_CIPHER_SUITE_WEP40:
 472                vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40;
 473                break;
 474        case WLAN_CIPHER_SUITE_WEP104:
 475                vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40;
 476                break;
 477        case WLAN_CIPHER_SUITE_CCMP:
 478                vif_priv->encrypt_type = WCN36XX_HAL_ED_CCMP;
 479                break;
 480        case WLAN_CIPHER_SUITE_TKIP:
 481                vif_priv->encrypt_type = WCN36XX_HAL_ED_TKIP;
 482                break;
 483        default:
 484                wcn36xx_err("Unsupported key type 0x%x\n",
 485                              key_conf->cipher);
 486                ret = -EOPNOTSUPP;
 487                goto out;
 488        }
 489
 490        switch (cmd) {
 491        case SET_KEY:
 492                if (WCN36XX_HAL_ED_TKIP == vif_priv->encrypt_type) {
 493                        /*
 494                         * Supplicant is sending key in the wrong order:
 495                         * Temporal Key (16 b) - TX MIC (8 b) - RX MIC (8 b)
 496                         * but HW expects it to be in the order as described in
 497                         * IEEE 802.11 spec (see chapter 11.7) like this:
 498                         * Temporal Key (16 b) - RX MIC (8 b) - TX MIC (8 b)
 499                         */
 500                        memcpy(key, key_conf->key, 16);
 501                        memcpy(key + 16, key_conf->key + 24, 8);
 502                        memcpy(key + 24, key_conf->key + 16, 8);
 503                } else {
 504                        memcpy(key, key_conf->key, key_conf->keylen);
 505                }
 506
 507                if (IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags) {
 508                        sta_priv->is_data_encrypted = true;
 509                        /* Reconfigure bss with encrypt_type */
 510                        if (NL80211_IFTYPE_STATION == vif->type)
 511                                wcn36xx_smd_config_bss(wcn,
 512                                                       vif,
 513                                                       sta,
 514                                                       sta->addr,
 515                                                       true);
 516
 517                        wcn36xx_smd_set_stakey(wcn,
 518                                vif_priv->encrypt_type,
 519                                key_conf->keyidx,
 520                                key_conf->keylen,
 521                                key,
 522                                get_sta_index(vif, sta_priv));
 523                } else {
 524                        wcn36xx_smd_set_bsskey(wcn,
 525                                vif_priv->encrypt_type,
 526                                key_conf->keyidx,
 527                                key_conf->keylen,
 528                                key);
 529                        if ((WLAN_CIPHER_SUITE_WEP40 == key_conf->cipher) ||
 530                            (WLAN_CIPHER_SUITE_WEP104 == key_conf->cipher)) {
 531                                sta_priv->is_data_encrypted = true;
 532                                wcn36xx_smd_set_stakey(wcn,
 533                                        vif_priv->encrypt_type,
 534                                        key_conf->keyidx,
 535                                        key_conf->keylen,
 536                                        key,
 537                                        get_sta_index(vif, sta_priv));
 538                        }
 539                }
 540                break;
 541        case DISABLE_KEY:
 542                if (!(IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags)) {
 543                        vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE;
 544                        wcn36xx_smd_remove_bsskey(wcn,
 545                                vif_priv->encrypt_type,
 546                                key_conf->keyidx);
 547                } else {
 548                        sta_priv->is_data_encrypted = false;
 549                        /* do not remove key if disassociated */
 550                        if (sta_priv->aid)
 551                                wcn36xx_smd_remove_stakey(wcn,
 552                                        vif_priv->encrypt_type,
 553                                        key_conf->keyidx,
 554                                        get_sta_index(vif, sta_priv));
 555                }
 556                break;
 557        default:
 558                wcn36xx_err("Unsupported key cmd 0x%x\n", cmd);
 559                ret = -EOPNOTSUPP;
 560                goto out;
 561        }
 562
 563out:
 564        return ret;
 565}
 566
 567static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw,
 568                                  struct ieee80211_vif *vif,
 569                                  const u8 *mac_addr)
 570{
 571        struct wcn36xx *wcn = hw->priv;
 572
 573        wcn36xx_smd_init_scan(wcn, HAL_SYS_MODE_SCAN);
 574        wcn36xx_smd_start_scan(wcn);
 575}
 576
 577static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw,
 578                                     struct ieee80211_vif *vif)
 579{
 580        struct wcn36xx *wcn = hw->priv;
 581
 582        wcn36xx_smd_end_scan(wcn);
 583        wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN);
 584}
 585
 586static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta,
 587                                         enum nl80211_band band)
 588{
 589        int i, size;
 590        u16 *rates_table;
 591        struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
 592        u32 rates = sta->supp_rates[band];
 593
 594        memset(&sta_priv->supported_rates, 0,
 595                sizeof(sta_priv->supported_rates));
 596        sta_priv->supported_rates.op_rate_mode = STA_11n;
 597
 598        size = ARRAY_SIZE(sta_priv->supported_rates.dsss_rates);
 599        rates_table = sta_priv->supported_rates.dsss_rates;
 600        if (band == NL80211_BAND_2GHZ) {
 601                for (i = 0; i < size; i++) {
 602                        if (rates & 0x01) {
 603                                rates_table[i] = wcn_2ghz_rates[i].hw_value;
 604                                rates = rates >> 1;
 605                        }
 606                }
 607        }
 608
 609        size = ARRAY_SIZE(sta_priv->supported_rates.ofdm_rates);
 610        rates_table = sta_priv->supported_rates.ofdm_rates;
 611        for (i = 0; i < size; i++) {
 612                if (rates & 0x01) {
 613                        rates_table[i] = wcn_5ghz_rates[i].hw_value;
 614                        rates = rates >> 1;
 615                }
 616        }
 617
 618        if (sta->ht_cap.ht_supported) {
 619                BUILD_BUG_ON(sizeof(sta->ht_cap.mcs.rx_mask) >
 620                        sizeof(sta_priv->supported_rates.supported_mcs_set));
 621                memcpy(sta_priv->supported_rates.supported_mcs_set,
 622                       sta->ht_cap.mcs.rx_mask,
 623                       sizeof(sta->ht_cap.mcs.rx_mask));
 624        }
 625}
 626void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates)
 627{
 628        u16 ofdm_rates[WCN36XX_HAL_NUM_OFDM_RATES] = {
 629                HW_RATE_INDEX_6MBPS,
 630                HW_RATE_INDEX_9MBPS,
 631                HW_RATE_INDEX_12MBPS,
 632                HW_RATE_INDEX_18MBPS,
 633                HW_RATE_INDEX_24MBPS,
 634                HW_RATE_INDEX_36MBPS,
 635                HW_RATE_INDEX_48MBPS,
 636                HW_RATE_INDEX_54MBPS
 637        };
 638        u16 dsss_rates[WCN36XX_HAL_NUM_DSSS_RATES] = {
 639                HW_RATE_INDEX_1MBPS,
 640                HW_RATE_INDEX_2MBPS,
 641                HW_RATE_INDEX_5_5MBPS,
 642                HW_RATE_INDEX_11MBPS
 643        };
 644
 645        rates->op_rate_mode = STA_11n;
 646        memcpy(rates->dsss_rates, dsss_rates,
 647                sizeof(*dsss_rates) * WCN36XX_HAL_NUM_DSSS_RATES);
 648        memcpy(rates->ofdm_rates, ofdm_rates,
 649                sizeof(*ofdm_rates) * WCN36XX_HAL_NUM_OFDM_RATES);
 650        rates->supported_mcs_set[0] = 0xFF;
 651}
 652static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
 653                                     struct ieee80211_vif *vif,
 654                                     struct ieee80211_bss_conf *bss_conf,
 655                                     u32 changed)
 656{
 657        struct wcn36xx *wcn = hw->priv;
 658        struct sk_buff *skb = NULL;
 659        u16 tim_off, tim_len;
 660        enum wcn36xx_hal_link_state link_state;
 661        struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
 662
 663        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss info changed vif %p changed 0x%08x\n",
 664                    vif, changed);
 665
 666        if (changed & BSS_CHANGED_BEACON_INFO) {
 667                wcn36xx_dbg(WCN36XX_DBG_MAC,
 668                            "mac bss changed dtim period %d\n",
 669                            bss_conf->dtim_period);
 670
 671                vif_priv->dtim_period = bss_conf->dtim_period;
 672        }
 673
 674        if (changed & BSS_CHANGED_PS) {
 675                wcn36xx_dbg(WCN36XX_DBG_MAC,
 676                            "mac bss PS set %d\n",
 677                            bss_conf->ps);
 678                if (bss_conf->ps) {
 679                        wcn36xx_pmc_enter_bmps_state(wcn, vif);
 680                } else {
 681                        wcn36xx_pmc_exit_bmps_state(wcn, vif);
 682                }
 683        }
 684
 685        if (changed & BSS_CHANGED_BSSID) {
 686                wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed_bssid %pM\n",
 687                            bss_conf->bssid);
 688
 689                if (!is_zero_ether_addr(bss_conf->bssid)) {
 690                        vif_priv->is_joining = true;
 691                        vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
 692                        wcn36xx_smd_join(wcn, bss_conf->bssid,
 693                                         vif->addr, WCN36XX_HW_CHANNEL(wcn));
 694                        wcn36xx_smd_config_bss(wcn, vif, NULL,
 695                                               bss_conf->bssid, false);
 696                } else {
 697                        vif_priv->is_joining = false;
 698                        wcn36xx_smd_delete_bss(wcn, vif);
 699                        vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE;
 700                }
 701        }
 702
 703        if (changed & BSS_CHANGED_SSID) {
 704                wcn36xx_dbg(WCN36XX_DBG_MAC,
 705                            "mac bss changed ssid\n");
 706                wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "ssid ",
 707                                 bss_conf->ssid, bss_conf->ssid_len);
 708
 709                vif_priv->ssid.length = bss_conf->ssid_len;
 710                memcpy(&vif_priv->ssid.ssid,
 711                       bss_conf->ssid,
 712                       bss_conf->ssid_len);
 713        }
 714
 715        if (changed & BSS_CHANGED_ASSOC) {
 716                vif_priv->is_joining = false;
 717                if (bss_conf->assoc) {
 718                        struct ieee80211_sta *sta;
 719                        struct wcn36xx_sta *sta_priv;
 720
 721                        wcn36xx_dbg(WCN36XX_DBG_MAC,
 722                                    "mac assoc bss %pM vif %pM AID=%d\n",
 723                                     bss_conf->bssid,
 724                                     vif->addr,
 725                                     bss_conf->aid);
 726
 727                        vif_priv->sta_assoc = true;
 728                        rcu_read_lock();
 729                        sta = ieee80211_find_sta(vif, bss_conf->bssid);
 730                        if (!sta) {
 731                                wcn36xx_err("sta %pM is not found\n",
 732                                              bss_conf->bssid);
 733                                rcu_read_unlock();
 734                                goto out;
 735                        }
 736                        sta_priv = wcn36xx_sta_to_priv(sta);
 737
 738                        wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn));
 739
 740                        wcn36xx_smd_set_link_st(wcn, bss_conf->bssid,
 741                                vif->addr,
 742                                WCN36XX_HAL_LINK_POSTASSOC_STATE);
 743                        wcn36xx_smd_config_bss(wcn, vif, sta,
 744                                               bss_conf->bssid,
 745                                               true);
 746                        sta_priv->aid = bss_conf->aid;
 747                        /*
 748                         * config_sta must be called from  because this is the
 749                         * place where AID is available.
 750                         */
 751                        wcn36xx_smd_config_sta(wcn, vif, sta);
 752                        rcu_read_unlock();
 753                } else {
 754                        wcn36xx_dbg(WCN36XX_DBG_MAC,
 755                                    "disassociated bss %pM vif %pM AID=%d\n",
 756                                    bss_conf->bssid,
 757                                    vif->addr,
 758                                    bss_conf->aid);
 759                        vif_priv->sta_assoc = false;
 760                        wcn36xx_smd_set_link_st(wcn,
 761                                                bss_conf->bssid,
 762                                                vif->addr,
 763                                                WCN36XX_HAL_LINK_IDLE_STATE);
 764                }
 765        }
 766
 767        if (changed & BSS_CHANGED_AP_PROBE_RESP) {
 768                wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed ap probe resp\n");
 769                skb = ieee80211_proberesp_get(hw, vif);
 770                if (!skb) {
 771                        wcn36xx_err("failed to alloc probereq skb\n");
 772                        goto out;
 773                }
 774
 775                wcn36xx_smd_update_proberesp_tmpl(wcn, vif, skb);
 776                dev_kfree_skb(skb);
 777        }
 778
 779        if (changed & BSS_CHANGED_BEACON_ENABLED ||
 780            changed & BSS_CHANGED_BEACON) {
 781                wcn36xx_dbg(WCN36XX_DBG_MAC,
 782                            "mac bss changed beacon enabled %d\n",
 783                            bss_conf->enable_beacon);
 784
 785                if (bss_conf->enable_beacon) {
 786                        vif_priv->dtim_period = bss_conf->dtim_period;
 787                        vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
 788                        wcn36xx_smd_config_bss(wcn, vif, NULL,
 789                                               vif->addr, false);
 790                        skb = ieee80211_beacon_get_tim(hw, vif, &tim_off,
 791                                                       &tim_len);
 792                        if (!skb) {
 793                                wcn36xx_err("failed to alloc beacon skb\n");
 794                                goto out;
 795                        }
 796                        wcn36xx_smd_send_beacon(wcn, vif, skb, tim_off, 0);
 797                        dev_kfree_skb(skb);
 798
 799                        if (vif->type == NL80211_IFTYPE_ADHOC ||
 800                            vif->type == NL80211_IFTYPE_MESH_POINT)
 801                                link_state = WCN36XX_HAL_LINK_IBSS_STATE;
 802                        else
 803                                link_state = WCN36XX_HAL_LINK_AP_STATE;
 804
 805                        wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr,
 806                                                link_state);
 807                } else {
 808                        wcn36xx_smd_delete_bss(wcn, vif);
 809                        wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr,
 810                                                WCN36XX_HAL_LINK_IDLE_STATE);
 811                }
 812        }
 813out:
 814        return;
 815}
 816
 817/* this is required when using IEEE80211_HW_HAS_RATE_CONTROL */
 818static int wcn36xx_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
 819{
 820        struct wcn36xx *wcn = hw->priv;
 821        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac set RTS threshold %d\n", value);
 822
 823        wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_RTS_THRESHOLD, value);
 824        return 0;
 825}
 826
 827static void wcn36xx_remove_interface(struct ieee80211_hw *hw,
 828                                     struct ieee80211_vif *vif)
 829{
 830        struct wcn36xx *wcn = hw->priv;
 831        struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
 832        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac remove interface vif %p\n", vif);
 833
 834        list_del(&vif_priv->list);
 835        wcn36xx_smd_delete_sta_self(wcn, vif->addr);
 836}
 837
 838static int wcn36xx_add_interface(struct ieee80211_hw *hw,
 839                                 struct ieee80211_vif *vif)
 840{
 841        struct wcn36xx *wcn = hw->priv;
 842        struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
 843
 844        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac add interface vif %p type %d\n",
 845                    vif, vif->type);
 846
 847        if (!(NL80211_IFTYPE_STATION == vif->type ||
 848              NL80211_IFTYPE_AP == vif->type ||
 849              NL80211_IFTYPE_ADHOC == vif->type ||
 850              NL80211_IFTYPE_MESH_POINT == vif->type)) {
 851                wcn36xx_warn("Unsupported interface type requested: %d\n",
 852                             vif->type);
 853                return -EOPNOTSUPP;
 854        }
 855
 856        list_add(&vif_priv->list, &wcn->vif_list);
 857        wcn36xx_smd_add_sta_self(wcn, vif);
 858
 859        return 0;
 860}
 861
 862static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 863                           struct ieee80211_sta *sta)
 864{
 865        struct wcn36xx *wcn = hw->priv;
 866        struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
 867        struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
 868        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n",
 869                    vif, sta->addr);
 870
 871        spin_lock_init(&sta_priv->ampdu_lock);
 872        sta_priv->vif = vif_priv;
 873        /*
 874         * For STA mode HW will be configured on BSS_CHANGED_ASSOC because
 875         * at this stage AID is not available yet.
 876         */
 877        if (NL80211_IFTYPE_STATION != vif->type) {
 878                wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn));
 879                sta_priv->aid = sta->aid;
 880                wcn36xx_smd_config_sta(wcn, vif, sta);
 881        }
 882        return 0;
 883}
 884
 885static int wcn36xx_sta_remove(struct ieee80211_hw *hw,
 886                              struct ieee80211_vif *vif,
 887                              struct ieee80211_sta *sta)
 888{
 889        struct wcn36xx *wcn = hw->priv;
 890        struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
 891
 892        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta remove vif %p sta %pM index %d\n",
 893                    vif, sta->addr, sta_priv->sta_index);
 894
 895        wcn36xx_smd_delete_sta(wcn, sta_priv->sta_index);
 896        sta_priv->vif = NULL;
 897        return 0;
 898}
 899
 900#ifdef CONFIG_PM
 901
 902static int wcn36xx_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wow)
 903{
 904        struct wcn36xx *wcn = hw->priv;
 905
 906        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac suspend\n");
 907
 908        flush_workqueue(wcn->hal_ind_wq);
 909        wcn36xx_smd_set_power_params(wcn, true);
 910        return 0;
 911}
 912
 913static int wcn36xx_resume(struct ieee80211_hw *hw)
 914{
 915        struct wcn36xx *wcn = hw->priv;
 916
 917        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac resume\n");
 918
 919        flush_workqueue(wcn->hal_ind_wq);
 920        wcn36xx_smd_set_power_params(wcn, false);
 921        return 0;
 922}
 923
 924#endif
 925
 926static int wcn36xx_ampdu_action(struct ieee80211_hw *hw,
 927                    struct ieee80211_vif *vif,
 928                    struct ieee80211_ampdu_params *params)
 929{
 930        struct wcn36xx *wcn = hw->priv;
 931        struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(params->sta);
 932        struct ieee80211_sta *sta = params->sta;
 933        enum ieee80211_ampdu_mlme_action action = params->action;
 934        u16 tid = params->tid;
 935        u16 *ssn = &params->ssn;
 936
 937        wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n",
 938                    action, tid);
 939
 940        switch (action) {
 941        case IEEE80211_AMPDU_RX_START:
 942                sta_priv->tid = tid;
 943                wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 0,
 944                        get_sta_index(vif, sta_priv));
 945                wcn36xx_smd_add_ba(wcn);
 946                wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv));
 947                break;
 948        case IEEE80211_AMPDU_RX_STOP:
 949                wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv));
 950                break;
 951        case IEEE80211_AMPDU_TX_START:
 952                spin_lock_bh(&sta_priv->ampdu_lock);
 953                sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START;
 954                spin_unlock_bh(&sta_priv->ampdu_lock);
 955
 956                ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
 957                break;
 958        case IEEE80211_AMPDU_TX_OPERATIONAL:
 959                spin_lock_bh(&sta_priv->ampdu_lock);
 960                sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_OPERATIONAL;
 961                spin_unlock_bh(&sta_priv->ampdu_lock);
 962
 963                wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1,
 964                        get_sta_index(vif, sta_priv));
 965                break;
 966        case IEEE80211_AMPDU_TX_STOP_FLUSH:
 967        case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
 968        case IEEE80211_AMPDU_TX_STOP_CONT:
 969                spin_lock_bh(&sta_priv->ampdu_lock);
 970                sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_NONE;
 971                spin_unlock_bh(&sta_priv->ampdu_lock);
 972
 973                ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
 974                break;
 975        default:
 976                wcn36xx_err("Unknown AMPDU action\n");
 977        }
 978
 979        return 0;
 980}
 981
 982static const struct ieee80211_ops wcn36xx_ops = {
 983        .start                  = wcn36xx_start,
 984        .stop                   = wcn36xx_stop,
 985        .add_interface          = wcn36xx_add_interface,
 986        .remove_interface       = wcn36xx_remove_interface,
 987#ifdef CONFIG_PM
 988        .suspend                = wcn36xx_suspend,
 989        .resume                 = wcn36xx_resume,
 990#endif
 991        .config                 = wcn36xx_config,
 992        .prepare_multicast      = wcn36xx_prepare_multicast,
 993        .configure_filter       = wcn36xx_configure_filter,
 994        .tx                     = wcn36xx_tx,
 995        .set_key                = wcn36xx_set_key,
 996        .sw_scan_start          = wcn36xx_sw_scan_start,
 997        .sw_scan_complete       = wcn36xx_sw_scan_complete,
 998        .bss_info_changed       = wcn36xx_bss_info_changed,
 999        .set_rts_threshold      = wcn36xx_set_rts_threshold,
1000        .sta_add                = wcn36xx_sta_add,
1001        .sta_remove             = wcn36xx_sta_remove,
1002        .ampdu_action           = wcn36xx_ampdu_action,
1003};
1004
1005static int wcn36xx_init_ieee80211(struct wcn36xx *wcn)
1006{
1007        int ret = 0;
1008
1009        static const u32 cipher_suites[] = {
1010                WLAN_CIPHER_SUITE_WEP40,
1011                WLAN_CIPHER_SUITE_WEP104,
1012                WLAN_CIPHER_SUITE_TKIP,
1013                WLAN_CIPHER_SUITE_CCMP,
1014        };
1015
1016        ieee80211_hw_set(wcn->hw, TIMING_BEACON_ONLY);
1017        ieee80211_hw_set(wcn->hw, AMPDU_AGGREGATION);
1018        ieee80211_hw_set(wcn->hw, CONNECTION_MONITOR);
1019        ieee80211_hw_set(wcn->hw, SUPPORTS_PS);
1020        ieee80211_hw_set(wcn->hw, SIGNAL_DBM);
1021        ieee80211_hw_set(wcn->hw, HAS_RATE_CONTROL);
1022
1023        wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
1024                BIT(NL80211_IFTYPE_AP) |
1025                BIT(NL80211_IFTYPE_ADHOC) |
1026                BIT(NL80211_IFTYPE_MESH_POINT);
1027
1028        wcn->hw->wiphy->bands[NL80211_BAND_2GHZ] = &wcn_band_2ghz;
1029        wcn->hw->wiphy->bands[NL80211_BAND_5GHZ] = &wcn_band_5ghz;
1030
1031        wcn->hw->wiphy->cipher_suites = cipher_suites;
1032        wcn->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
1033
1034        wcn->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
1035
1036#ifdef CONFIG_PM
1037        wcn->hw->wiphy->wowlan = &wowlan_support;
1038#endif
1039
1040        wcn->hw->max_listen_interval = 200;
1041
1042        wcn->hw->queues = 4;
1043
1044        SET_IEEE80211_DEV(wcn->hw, wcn->dev);
1045
1046        wcn->hw->sta_data_size = sizeof(struct wcn36xx_sta);
1047        wcn->hw->vif_data_size = sizeof(struct wcn36xx_vif);
1048
1049        return ret;
1050}
1051
1052static int wcn36xx_platform_get_resources(struct wcn36xx *wcn,
1053                                          struct platform_device *pdev)
1054{
1055        struct device_node *mmio_node;
1056        struct resource *res;
1057        int index;
1058        int ret;
1059
1060        /* Set TX IRQ */
1061        res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
1062                                           "wcnss_wlantx_irq");
1063        if (!res) {
1064                wcn36xx_err("failed to get tx_irq\n");
1065                return -ENOENT;
1066        }
1067        wcn->tx_irq = res->start;
1068
1069        /* Set RX IRQ */
1070        res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
1071                                           "wcnss_wlanrx_irq");
1072        if (!res) {
1073                wcn36xx_err("failed to get rx_irq\n");
1074                return -ENOENT;
1075        }
1076        wcn->rx_irq = res->start;
1077
1078        mmio_node = of_parse_phandle(pdev->dev.parent->of_node, "qcom,mmio", 0);
1079        if (!mmio_node) {
1080                wcn36xx_err("failed to acquire qcom,mmio reference\n");
1081                return -EINVAL;
1082        }
1083
1084        wcn->is_pronto = !!of_device_is_compatible(mmio_node, "qcom,pronto");
1085
1086        /* Map the CCU memory */
1087        index = of_property_match_string(mmio_node, "reg-names", "ccu");
1088        wcn->ccu_base = of_iomap(mmio_node, index);
1089        if (!wcn->ccu_base) {
1090                wcn36xx_err("failed to map ccu memory\n");
1091                ret = -ENOMEM;
1092                goto put_mmio_node;
1093        }
1094
1095        /* Map the DXE memory */
1096        index = of_property_match_string(mmio_node, "reg-names", "dxe");
1097        wcn->dxe_base = of_iomap(mmio_node, index);
1098        if (!wcn->dxe_base) {
1099                wcn36xx_err("failed to map dxe memory\n");
1100                ret = -ENOMEM;
1101                goto unmap_ccu;
1102        }
1103
1104        of_node_put(mmio_node);
1105        return 0;
1106
1107unmap_ccu:
1108        iounmap(wcn->ccu_base);
1109put_mmio_node:
1110        of_node_put(mmio_node);
1111        return ret;
1112}
1113
1114static int wcn36xx_probe(struct platform_device *pdev)
1115{
1116        struct ieee80211_hw *hw;
1117        struct wcn36xx *wcn;
1118        int ret;
1119        u8 addr[ETH_ALEN];
1120
1121        wcn36xx_dbg(WCN36XX_DBG_MAC, "platform probe\n");
1122
1123        hw = ieee80211_alloc_hw(sizeof(struct wcn36xx), &wcn36xx_ops);
1124        if (!hw) {
1125                wcn36xx_err("failed to alloc hw\n");
1126                ret = -ENOMEM;
1127                goto out_err;
1128        }
1129        platform_set_drvdata(pdev, hw);
1130        wcn = hw->priv;
1131        wcn->hw = hw;
1132        wcn->dev = &pdev->dev;
1133        wcn->ctrl_ops = pdev->dev.platform_data;
1134
1135        mutex_init(&wcn->hal_mutex);
1136
1137        if (!wcn->ctrl_ops->get_hw_mac(addr)) {
1138                wcn36xx_info("mac address: %pM\n", addr);
1139                SET_IEEE80211_PERM_ADDR(wcn->hw, addr);
1140        }
1141
1142        ret = wcn36xx_platform_get_resources(wcn, pdev);
1143        if (ret)
1144                goto out_wq;
1145
1146        wcn36xx_init_ieee80211(wcn);
1147        ret = ieee80211_register_hw(wcn->hw);
1148        if (ret)
1149                goto out_unmap;
1150
1151        return 0;
1152
1153out_unmap:
1154        iounmap(wcn->ccu_base);
1155        iounmap(wcn->dxe_base);
1156out_wq:
1157        ieee80211_free_hw(hw);
1158out_err:
1159        return ret;
1160}
1161static int wcn36xx_remove(struct platform_device *pdev)
1162{
1163        struct ieee80211_hw *hw = platform_get_drvdata(pdev);
1164        struct wcn36xx *wcn = hw->priv;
1165        wcn36xx_dbg(WCN36XX_DBG_MAC, "platform remove\n");
1166
1167        release_firmware(wcn->nv);
1168        mutex_destroy(&wcn->hal_mutex);
1169
1170        ieee80211_unregister_hw(hw);
1171        iounmap(wcn->dxe_base);
1172        iounmap(wcn->ccu_base);
1173        ieee80211_free_hw(hw);
1174
1175        return 0;
1176}
1177static const struct platform_device_id wcn36xx_platform_id_table[] = {
1178        {
1179                .name = "wcn36xx",
1180                .driver_data = 0
1181        },
1182        {}
1183};
1184MODULE_DEVICE_TABLE(platform, wcn36xx_platform_id_table);
1185
1186static struct platform_driver wcn36xx_driver = {
1187        .probe      = wcn36xx_probe,
1188        .remove     = wcn36xx_remove,
1189        .driver         = {
1190                .name   = "wcn36xx",
1191        },
1192        .id_table    = wcn36xx_platform_id_table,
1193};
1194
1195static int __init wcn36xx_init(void)
1196{
1197        platform_driver_register(&wcn36xx_driver);
1198        return 0;
1199}
1200module_init(wcn36xx_init);
1201
1202static void __exit wcn36xx_exit(void)
1203{
1204        platform_driver_unregister(&wcn36xx_driver);
1205}
1206module_exit(wcn36xx_exit);
1207
1208MODULE_LICENSE("Dual BSD/GPL");
1209MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com");
1210MODULE_FIRMWARE(WLAN_NV_FILE);
1211