0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0018
0019 #include <linux/module.h>
0020 #include <linux/firmware.h>
0021 #include <linux/platform_device.h>
0022 #include <linux/of_address.h>
0023 #include <linux/of_device.h>
0024 #include <linux/of_irq.h>
0025 #include <linux/rpmsg.h>
0026 #include <linux/soc/qcom/smem_state.h>
0027 #include <linux/soc/qcom/wcnss_ctrl.h>
0028 #include <net/ipv6.h>
0029 #include "wcn36xx.h"
0030 #include "testmode.h"
0031 #include "firmware.h"
0032
0033 unsigned int wcn36xx_dbg_mask;
0034 module_param_named(debug_mask, wcn36xx_dbg_mask, uint, 0644);
0035 MODULE_PARM_DESC(debug_mask, "Debugging mask");
0036
0037 #define CHAN2G(_freq, _idx) { \
0038 .band = NL80211_BAND_2GHZ, \
0039 .center_freq = (_freq), \
0040 .hw_value = (_idx), \
0041 .max_power = 25, \
0042 }
0043
0044 #define CHAN5G(_freq, _idx, _phy_val) { \
0045 .band = NL80211_BAND_5GHZ, \
0046 .center_freq = (_freq), \
0047 .hw_value = (_phy_val) << HW_VALUE_PHY_SHIFT | HW_VALUE_CHANNEL(_idx), \
0048 .max_power = 25, \
0049 }
0050
0051
0052
0053 static struct ieee80211_channel wcn_2ghz_channels[] = {
0054 CHAN2G(2412, 1),
0055 CHAN2G(2417, 2),
0056 CHAN2G(2422, 3),
0057 CHAN2G(2427, 4),
0058 CHAN2G(2432, 5),
0059 CHAN2G(2437, 6),
0060 CHAN2G(2442, 7),
0061 CHAN2G(2447, 8),
0062 CHAN2G(2452, 9),
0063 CHAN2G(2457, 10),
0064 CHAN2G(2462, 11),
0065 CHAN2G(2467, 12),
0066 CHAN2G(2472, 13),
0067 CHAN2G(2484, 14)
0068
0069 };
0070
0071 static struct ieee80211_channel wcn_5ghz_channels[] = {
0072 CHAN5G(5180, 36, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW),
0073 CHAN5G(5200, 40, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW),
0074 CHAN5G(5220, 44, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH),
0075 CHAN5G(5240, 48, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH),
0076 CHAN5G(5260, 52, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW),
0077 CHAN5G(5280, 56, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW),
0078 CHAN5G(5300, 60, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH),
0079 CHAN5G(5320, 64, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH),
0080 CHAN5G(5500, 100, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW),
0081 CHAN5G(5520, 104, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW),
0082 CHAN5G(5540, 108, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH),
0083 CHAN5G(5560, 112, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH),
0084 CHAN5G(5580, 116, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW),
0085 CHAN5G(5600, 120, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW),
0086 CHAN5G(5620, 124, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH),
0087 CHAN5G(5640, 128, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH),
0088 CHAN5G(5660, 132, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW),
0089 CHAN5G(5680, 136, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW),
0090 CHAN5G(5700, 140, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH),
0091 CHAN5G(5720, 144, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH),
0092 CHAN5G(5745, 149, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW),
0093 CHAN5G(5765, 153, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW),
0094 CHAN5G(5785, 157, PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH),
0095 CHAN5G(5805, 161, PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH),
0096 CHAN5G(5825, 165, 0)
0097 };
0098
0099 #define RATE(_bitrate, _hw_rate, _flags) { \
0100 .bitrate = (_bitrate), \
0101 .flags = (_flags), \
0102 .hw_value = (_hw_rate), \
0103 .hw_value_short = (_hw_rate) \
0104 }
0105
0106 static struct ieee80211_rate wcn_2ghz_rates[] = {
0107 RATE(10, HW_RATE_INDEX_1MBPS, 0),
0108 RATE(20, HW_RATE_INDEX_2MBPS, IEEE80211_RATE_SHORT_PREAMBLE),
0109 RATE(55, HW_RATE_INDEX_5_5MBPS, IEEE80211_RATE_SHORT_PREAMBLE),
0110 RATE(110, HW_RATE_INDEX_11MBPS, IEEE80211_RATE_SHORT_PREAMBLE),
0111 RATE(60, HW_RATE_INDEX_6MBPS, 0),
0112 RATE(90, HW_RATE_INDEX_9MBPS, 0),
0113 RATE(120, HW_RATE_INDEX_12MBPS, 0),
0114 RATE(180, HW_RATE_INDEX_18MBPS, 0),
0115 RATE(240, HW_RATE_INDEX_24MBPS, 0),
0116 RATE(360, HW_RATE_INDEX_36MBPS, 0),
0117 RATE(480, HW_RATE_INDEX_48MBPS, 0),
0118 RATE(540, HW_RATE_INDEX_54MBPS, 0)
0119 };
0120
0121 static struct ieee80211_rate wcn_5ghz_rates[] = {
0122 RATE(60, HW_RATE_INDEX_6MBPS, 0),
0123 RATE(90, HW_RATE_INDEX_9MBPS, 0),
0124 RATE(120, HW_RATE_INDEX_12MBPS, 0),
0125 RATE(180, HW_RATE_INDEX_18MBPS, 0),
0126 RATE(240, HW_RATE_INDEX_24MBPS, 0),
0127 RATE(360, HW_RATE_INDEX_36MBPS, 0),
0128 RATE(480, HW_RATE_INDEX_48MBPS, 0),
0129 RATE(540, HW_RATE_INDEX_54MBPS, 0)
0130 };
0131
0132 static struct ieee80211_supported_band wcn_band_2ghz = {
0133 .channels = wcn_2ghz_channels,
0134 .n_channels = ARRAY_SIZE(wcn_2ghz_channels),
0135 .bitrates = wcn_2ghz_rates,
0136 .n_bitrates = ARRAY_SIZE(wcn_2ghz_rates),
0137 .ht_cap = {
0138 .cap = IEEE80211_HT_CAP_GRN_FLD |
0139 IEEE80211_HT_CAP_SGI_20 |
0140 IEEE80211_HT_CAP_DSSSCCK40 |
0141 IEEE80211_HT_CAP_LSIG_TXOP_PROT |
0142 IEEE80211_HT_CAP_SGI_40 |
0143 IEEE80211_HT_CAP_SUP_WIDTH_20_40,
0144 .ht_supported = true,
0145 .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
0146 .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
0147 .mcs = {
0148 .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
0149 .rx_highest = cpu_to_le16(72),
0150 .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
0151 }
0152 }
0153 };
0154
0155 static struct ieee80211_supported_band wcn_band_5ghz = {
0156 .channels = wcn_5ghz_channels,
0157 .n_channels = ARRAY_SIZE(wcn_5ghz_channels),
0158 .bitrates = wcn_5ghz_rates,
0159 .n_bitrates = ARRAY_SIZE(wcn_5ghz_rates),
0160 .ht_cap = {
0161 .cap = IEEE80211_HT_CAP_GRN_FLD |
0162 IEEE80211_HT_CAP_SGI_20 |
0163 IEEE80211_HT_CAP_DSSSCCK40 |
0164 IEEE80211_HT_CAP_LSIG_TXOP_PROT |
0165 IEEE80211_HT_CAP_SGI_40 |
0166 IEEE80211_HT_CAP_SUP_WIDTH_20_40,
0167 .ht_supported = true,
0168 .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
0169 .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
0170 .mcs = {
0171 .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
0172 .rx_highest = cpu_to_le16(150),
0173 .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
0174 }
0175 }
0176 };
0177
0178 #ifdef CONFIG_PM
0179
0180 static const struct wiphy_wowlan_support wowlan_support = {
0181 .flags = WIPHY_WOWLAN_ANY |
0182 WIPHY_WOWLAN_MAGIC_PKT |
0183 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY
0184 };
0185
0186 #endif
0187
0188 static inline u8 get_sta_index(struct ieee80211_vif *vif,
0189 struct wcn36xx_sta *sta_priv)
0190 {
0191 return NL80211_IFTYPE_STATION == vif->type ?
0192 sta_priv->bss_sta_index :
0193 sta_priv->sta_index;
0194 }
0195
0196 static void wcn36xx_feat_caps_info(struct wcn36xx *wcn)
0197 {
0198 int i;
0199
0200 for (i = 0; i < MAX_FEATURE_SUPPORTED; i++) {
0201 if (wcn36xx_firmware_get_feat_caps(wcn->fw_feat_caps, i)) {
0202 wcn36xx_dbg(WCN36XX_DBG_MAC, "FW Cap %s\n",
0203 wcn36xx_firmware_get_cap_name(i));
0204 }
0205 }
0206 }
0207
0208 static int wcn36xx_start(struct ieee80211_hw *hw)
0209 {
0210 struct wcn36xx *wcn = hw->priv;
0211 int ret;
0212
0213 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac start\n");
0214
0215
0216 ret = wcn36xx_smd_open(wcn);
0217 if (ret) {
0218 wcn36xx_err("Failed to open smd channel: %d\n", ret);
0219 goto out_err;
0220 }
0221
0222
0223 ret = wcn36xx_dxe_allocate_mem_pools(wcn);
0224 if (ret) {
0225 wcn36xx_err("Failed to alloc DXE mempool: %d\n", ret);
0226 goto out_smd_close;
0227 }
0228
0229 ret = wcn36xx_dxe_alloc_ctl_blks(wcn);
0230 if (ret) {
0231 wcn36xx_err("Failed to alloc DXE ctl blocks: %d\n", ret);
0232 goto out_free_dxe_pool;
0233 }
0234
0235 ret = wcn36xx_smd_load_nv(wcn);
0236 if (ret) {
0237 wcn36xx_err("Failed to push NV to chip\n");
0238 goto out_free_dxe_ctl;
0239 }
0240
0241 ret = wcn36xx_smd_start(wcn);
0242 if (ret) {
0243 wcn36xx_err("Failed to start chip\n");
0244 goto out_free_dxe_ctl;
0245 }
0246
0247 if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) {
0248 ret = wcn36xx_smd_feature_caps_exchange(wcn);
0249 if (ret)
0250 wcn36xx_warn("Exchange feature caps failed\n");
0251 else
0252 wcn36xx_feat_caps_info(wcn);
0253 }
0254
0255
0256 ret = wcn36xx_dxe_init(wcn);
0257 if (ret) {
0258 wcn36xx_err("DXE init failed\n");
0259 goto out_smd_stop;
0260 }
0261
0262 wcn36xx_debugfs_init(wcn);
0263
0264 INIT_LIST_HEAD(&wcn->vif_list);
0265 spin_lock_init(&wcn->dxe_lock);
0266 spin_lock_init(&wcn->survey_lock);
0267
0268 return 0;
0269
0270 out_smd_stop:
0271 wcn36xx_smd_stop(wcn);
0272 out_free_dxe_ctl:
0273 wcn36xx_dxe_free_ctl_blks(wcn);
0274 out_free_dxe_pool:
0275 wcn36xx_dxe_free_mem_pools(wcn);
0276 out_smd_close:
0277 wcn36xx_smd_close(wcn);
0278 out_err:
0279 return ret;
0280 }
0281
0282 static void wcn36xx_stop(struct ieee80211_hw *hw)
0283 {
0284 struct wcn36xx *wcn = hw->priv;
0285
0286 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac stop\n");
0287
0288 mutex_lock(&wcn->scan_lock);
0289 if (wcn->scan_req) {
0290 struct cfg80211_scan_info scan_info = {
0291 .aborted = true,
0292 };
0293
0294 ieee80211_scan_completed(wcn->hw, &scan_info);
0295 }
0296 wcn->scan_req = NULL;
0297 mutex_unlock(&wcn->scan_lock);
0298
0299 wcn36xx_debugfs_exit(wcn);
0300 wcn36xx_smd_stop(wcn);
0301 wcn36xx_dxe_deinit(wcn);
0302 wcn36xx_smd_close(wcn);
0303
0304 wcn36xx_dxe_free_mem_pools(wcn);
0305 wcn36xx_dxe_free_ctl_blks(wcn);
0306 }
0307
0308 static void wcn36xx_change_ps(struct wcn36xx *wcn, bool enable)
0309 {
0310 struct ieee80211_vif *vif = NULL;
0311 struct wcn36xx_vif *tmp;
0312
0313 list_for_each_entry(tmp, &wcn->vif_list, list) {
0314 vif = wcn36xx_priv_to_vif(tmp);
0315 if (enable && !wcn->sw_scan) {
0316 if (vif->cfg.ps)
0317 wcn36xx_pmc_enter_bmps_state(wcn, vif);
0318 } else {
0319 wcn36xx_pmc_exit_bmps_state(wcn, vif);
0320 }
0321 }
0322 }
0323
0324 static void wcn36xx_change_opchannel(struct wcn36xx *wcn, int ch)
0325 {
0326 struct ieee80211_vif *vif = NULL;
0327 struct wcn36xx_vif *tmp;
0328 struct ieee80211_supported_band *band;
0329 struct ieee80211_channel *channel = NULL;
0330 unsigned long flags;
0331 int i, j;
0332
0333 for (i = 0; i < ARRAY_SIZE(wcn->hw->wiphy->bands); i++) {
0334 band = wcn->hw->wiphy->bands[i];
0335 if (!band)
0336 break;
0337 for (j = 0; j < band->n_channels; j++) {
0338 if (HW_VALUE_CHANNEL(band->channels[j].hw_value) == ch) {
0339 channel = &band->channels[j];
0340 break;
0341 }
0342 }
0343 if (channel)
0344 break;
0345 }
0346
0347 if (!channel) {
0348 wcn36xx_err("Cannot tune to channel %d\n", ch);
0349 return;
0350 }
0351
0352 spin_lock_irqsave(&wcn->survey_lock, flags);
0353 wcn->band = band;
0354 wcn->channel = channel;
0355 spin_unlock_irqrestore(&wcn->survey_lock, flags);
0356
0357 list_for_each_entry(tmp, &wcn->vif_list, list) {
0358 vif = wcn36xx_priv_to_vif(tmp);
0359 wcn36xx_smd_switch_channel(wcn, vif, ch);
0360 }
0361
0362 return;
0363 }
0364
0365 static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed)
0366 {
0367 struct wcn36xx *wcn = hw->priv;
0368 int ret;
0369
0370 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac config changed 0x%08x\n", changed);
0371
0372 mutex_lock(&wcn->conf_mutex);
0373
0374 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
0375 int ch = WCN36XX_HW_CHANNEL(wcn);
0376 wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n",
0377 ch);
0378
0379 if (wcn->sw_scan_opchannel == ch && wcn->sw_scan_channel) {
0380
0381
0382
0383
0384 if (wcn->sw_scan_channel)
0385 wcn36xx_smd_end_scan(wcn, wcn->sw_scan_channel);
0386 if (wcn->sw_scan_init) {
0387 wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN,
0388 wcn->sw_scan_vif);
0389 }
0390 } else if (wcn->sw_scan) {
0391
0392
0393
0394 if (wcn->sw_scan_channel)
0395 wcn36xx_smd_end_scan(wcn, wcn->sw_scan_channel);
0396 if (!wcn->sw_scan_init) {
0397
0398
0399
0400 ret = wcn36xx_smd_init_scan(wcn,
0401 HAL_SYS_MODE_SCAN,
0402 wcn->sw_scan_vif);
0403 if (ret) {
0404 mutex_unlock(&wcn->conf_mutex);
0405 return -EIO;
0406 }
0407 }
0408 wcn36xx_smd_start_scan(wcn, ch);
0409 } else {
0410 wcn36xx_change_opchannel(wcn, ch);
0411 }
0412 }
0413
0414 if (changed & IEEE80211_CONF_CHANGE_PS)
0415 wcn36xx_change_ps(wcn, hw->conf.flags & IEEE80211_CONF_PS);
0416
0417 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
0418 if (hw->conf.flags & IEEE80211_CONF_IDLE)
0419 wcn36xx_smd_enter_imps(wcn);
0420 else
0421 wcn36xx_smd_exit_imps(wcn);
0422 }
0423
0424 mutex_unlock(&wcn->conf_mutex);
0425
0426 return 0;
0427 }
0428
0429 static void wcn36xx_configure_filter(struct ieee80211_hw *hw,
0430 unsigned int changed,
0431 unsigned int *total, u64 multicast)
0432 {
0433 struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp;
0434 struct wcn36xx *wcn = hw->priv;
0435 struct wcn36xx_vif *tmp;
0436 struct ieee80211_vif *vif = NULL;
0437
0438 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac configure filter\n");
0439
0440 mutex_lock(&wcn->conf_mutex);
0441
0442 *total &= FIF_ALLMULTI;
0443
0444 fp = (void *)(unsigned long)multicast;
0445 list_for_each_entry(tmp, &wcn->vif_list, list) {
0446 vif = wcn36xx_priv_to_vif(tmp);
0447
0448
0449 if (*total & FIF_ALLMULTI)
0450 wcn36xx_smd_set_mc_list(wcn, vif, NULL);
0451 else if (NL80211_IFTYPE_STATION == vif->type && tmp->sta_assoc)
0452 wcn36xx_smd_set_mc_list(wcn, vif, fp);
0453 }
0454
0455 mutex_unlock(&wcn->conf_mutex);
0456 kfree(fp);
0457 }
0458
0459 static u64 wcn36xx_prepare_multicast(struct ieee80211_hw *hw,
0460 struct netdev_hw_addr_list *mc_list)
0461 {
0462 struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp;
0463 struct netdev_hw_addr *ha;
0464
0465 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac prepare multicast list\n");
0466 fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
0467 if (!fp) {
0468 wcn36xx_err("Out of memory setting filters.\n");
0469 return 0;
0470 }
0471
0472 fp->mc_addr_count = 0;
0473
0474 if (netdev_hw_addr_list_count(mc_list) <=
0475 WCN36XX_HAL_MAX_NUM_MULTICAST_ADDRESS) {
0476 netdev_hw_addr_list_for_each(ha, mc_list) {
0477 memcpy(fp->mc_addr[fp->mc_addr_count],
0478 ha->addr, ETH_ALEN);
0479 fp->mc_addr_count++;
0480 }
0481 }
0482
0483 return (u64)(unsigned long)fp;
0484 }
0485
0486 static void wcn36xx_tx(struct ieee80211_hw *hw,
0487 struct ieee80211_tx_control *control,
0488 struct sk_buff *skb)
0489 {
0490 struct wcn36xx *wcn = hw->priv;
0491 struct wcn36xx_sta *sta_priv = NULL;
0492
0493 if (control->sta)
0494 sta_priv = wcn36xx_sta_to_priv(control->sta);
0495
0496 if (wcn36xx_start_tx(wcn, sta_priv, skb))
0497 ieee80211_free_txskb(wcn->hw, skb);
0498 }
0499
0500 static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
0501 struct ieee80211_vif *vif,
0502 struct ieee80211_sta *sta,
0503 struct ieee80211_key_conf *key_conf)
0504 {
0505 struct wcn36xx *wcn = hw->priv;
0506 struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
0507 struct wcn36xx_sta *sta_priv = sta ? wcn36xx_sta_to_priv(sta) : NULL;
0508 int ret = 0;
0509 u8 key[WLAN_MAX_KEY_LEN];
0510
0511 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 set key\n");
0512 wcn36xx_dbg(WCN36XX_DBG_MAC, "Key: cmd=0x%x algo:0x%x, id:%d, len:%d flags 0x%x\n",
0513 cmd, key_conf->cipher, key_conf->keyidx,
0514 key_conf->keylen, key_conf->flags);
0515 wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "KEY: ",
0516 key_conf->key,
0517 key_conf->keylen);
0518
0519 mutex_lock(&wcn->conf_mutex);
0520
0521 switch (key_conf->cipher) {
0522 case WLAN_CIPHER_SUITE_WEP40:
0523 vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40;
0524 break;
0525 case WLAN_CIPHER_SUITE_WEP104:
0526 vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP104;
0527 break;
0528 case WLAN_CIPHER_SUITE_CCMP:
0529 vif_priv->encrypt_type = WCN36XX_HAL_ED_CCMP;
0530 break;
0531 case WLAN_CIPHER_SUITE_TKIP:
0532 vif_priv->encrypt_type = WCN36XX_HAL_ED_TKIP;
0533 break;
0534 default:
0535 wcn36xx_err("Unsupported key type 0x%x\n",
0536 key_conf->cipher);
0537 ret = -EOPNOTSUPP;
0538 goto out;
0539 }
0540
0541 switch (cmd) {
0542 case SET_KEY:
0543 if (WCN36XX_HAL_ED_TKIP == vif_priv->encrypt_type) {
0544
0545
0546
0547
0548
0549
0550
0551 memcpy(key, key_conf->key, 16);
0552 memcpy(key + 16, key_conf->key + 24, 8);
0553 memcpy(key + 24, key_conf->key + 16, 8);
0554 } else {
0555 memcpy(key, key_conf->key, key_conf->keylen);
0556 }
0557
0558 if (IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags) {
0559 sta_priv->is_data_encrypted = true;
0560
0561 if (NL80211_IFTYPE_STATION == vif->type) {
0562 wcn36xx_smd_config_bss(wcn,
0563 vif,
0564 sta,
0565 sta->addr,
0566 true);
0567 wcn36xx_smd_config_sta(wcn, vif, sta);
0568 }
0569
0570 wcn36xx_smd_set_stakey(wcn,
0571 vif_priv->encrypt_type,
0572 key_conf->keyidx,
0573 key_conf->keylen,
0574 key,
0575 get_sta_index(vif, sta_priv));
0576 } else {
0577 wcn36xx_smd_set_bsskey(wcn,
0578 vif_priv->encrypt_type,
0579 vif_priv->bss_index,
0580 key_conf->keyidx,
0581 key_conf->keylen,
0582 key);
0583
0584 if ((WLAN_CIPHER_SUITE_WEP40 == key_conf->cipher) ||
0585 (WLAN_CIPHER_SUITE_WEP104 == key_conf->cipher)) {
0586 list_for_each_entry(sta_priv,
0587 &vif_priv->sta_list, list) {
0588 sta_priv->is_data_encrypted = true;
0589 wcn36xx_smd_set_stakey(wcn,
0590 vif_priv->encrypt_type,
0591 key_conf->keyidx,
0592 key_conf->keylen,
0593 key,
0594 get_sta_index(vif, sta_priv));
0595 }
0596 }
0597 }
0598 break;
0599 case DISABLE_KEY:
0600 if (!(IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags)) {
0601 if (vif_priv->bss_index != WCN36XX_HAL_BSS_INVALID_IDX)
0602 wcn36xx_smd_remove_bsskey(wcn,
0603 vif_priv->encrypt_type,
0604 vif_priv->bss_index,
0605 key_conf->keyidx);
0606
0607 vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE;
0608 } else {
0609 sta_priv->is_data_encrypted = false;
0610
0611 if (sta_priv->aid)
0612 wcn36xx_smd_remove_stakey(wcn,
0613 vif_priv->encrypt_type,
0614 key_conf->keyidx,
0615 get_sta_index(vif, sta_priv));
0616 }
0617 break;
0618 default:
0619 wcn36xx_err("Unsupported key cmd 0x%x\n", cmd);
0620 ret = -EOPNOTSUPP;
0621 goto out;
0622 }
0623
0624 out:
0625 mutex_unlock(&wcn->conf_mutex);
0626
0627 return ret;
0628 }
0629
0630 static int wcn36xx_hw_scan(struct ieee80211_hw *hw,
0631 struct ieee80211_vif *vif,
0632 struct ieee80211_scan_request *hw_req)
0633 {
0634 struct wcn36xx *wcn = hw->priv;
0635
0636 if (!wcn36xx_firmware_get_feat_caps(wcn->fw_feat_caps, SCAN_OFFLOAD)) {
0637
0638 return 1;
0639 }
0640
0641
0642
0643
0644 if (hw_req->req.n_channels > 48) {
0645 wcn36xx_warn("Offload scan aborted, n_channels=%u",
0646 hw_req->req.n_channels);
0647 return 1;
0648 }
0649
0650 mutex_lock(&wcn->scan_lock);
0651 if (wcn->scan_req) {
0652 mutex_unlock(&wcn->scan_lock);
0653 return -EBUSY;
0654 }
0655
0656 wcn->scan_aborted = false;
0657 wcn->scan_req = &hw_req->req;
0658
0659 mutex_unlock(&wcn->scan_lock);
0660
0661 wcn36xx_smd_update_channel_list(wcn, &hw_req->req);
0662 return wcn36xx_smd_start_hw_scan(wcn, vif, &hw_req->req);
0663 }
0664
0665 static void wcn36xx_cancel_hw_scan(struct ieee80211_hw *hw,
0666 struct ieee80211_vif *vif)
0667 {
0668 struct wcn36xx *wcn = hw->priv;
0669
0670 mutex_lock(&wcn->scan_lock);
0671 wcn->scan_aborted = true;
0672 mutex_unlock(&wcn->scan_lock);
0673
0674 if (wcn36xx_firmware_get_feat_caps(wcn->fw_feat_caps, SCAN_OFFLOAD)) {
0675
0676
0677 wcn36xx_smd_stop_hw_scan(wcn);
0678 }
0679 }
0680
0681 static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw,
0682 struct ieee80211_vif *vif,
0683 const u8 *mac_addr)
0684 {
0685 struct wcn36xx *wcn = hw->priv;
0686 struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
0687
0688 wcn36xx_dbg(WCN36XX_DBG_MAC, "sw_scan_start");
0689
0690 wcn->sw_scan = true;
0691 wcn->sw_scan_vif = vif;
0692 wcn->sw_scan_channel = 0;
0693 if (vif_priv->sta_assoc)
0694 wcn->sw_scan_opchannel = WCN36XX_HW_CHANNEL(wcn);
0695 else
0696 wcn->sw_scan_opchannel = 0;
0697 }
0698
0699 static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw,
0700 struct ieee80211_vif *vif)
0701 {
0702 struct wcn36xx *wcn = hw->priv;
0703
0704 wcn36xx_dbg(WCN36XX_DBG_MAC, "sw_scan_complete");
0705
0706
0707 if (wcn->sw_scan_channel)
0708 wcn36xx_smd_end_scan(wcn, wcn->sw_scan_channel);
0709 if (wcn->sw_scan_init) {
0710 wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN,
0711 wcn->sw_scan_vif);
0712 }
0713 wcn->sw_scan = false;
0714 wcn->sw_scan_opchannel = 0;
0715 }
0716
0717 static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta,
0718 enum nl80211_band band)
0719 {
0720 int i, size;
0721 u16 *rates_table;
0722 struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
0723 u32 rates = sta->deflink.supp_rates[band];
0724
0725 memset(&sta_priv->supported_rates, 0,
0726 sizeof(sta_priv->supported_rates));
0727 sta_priv->supported_rates.op_rate_mode = STA_11n;
0728
0729 size = ARRAY_SIZE(sta_priv->supported_rates.dsss_rates);
0730 rates_table = sta_priv->supported_rates.dsss_rates;
0731 if (band == NL80211_BAND_2GHZ) {
0732 for (i = 0; i < size; i++) {
0733 if (rates & 0x01) {
0734 rates_table[i] = wcn_2ghz_rates[i].hw_value;
0735 rates = rates >> 1;
0736 }
0737 }
0738 }
0739
0740 size = ARRAY_SIZE(sta_priv->supported_rates.ofdm_rates);
0741 rates_table = sta_priv->supported_rates.ofdm_rates;
0742 for (i = 0; i < size; i++) {
0743 if (rates & 0x01) {
0744 rates_table[i] = wcn_5ghz_rates[i].hw_value;
0745 rates = rates >> 1;
0746 }
0747 }
0748
0749 if (sta->deflink.ht_cap.ht_supported) {
0750 BUILD_BUG_ON(sizeof(sta->deflink.ht_cap.mcs.rx_mask) >
0751 sizeof(sta_priv->supported_rates.supported_mcs_set));
0752 memcpy(sta_priv->supported_rates.supported_mcs_set,
0753 sta->deflink.ht_cap.mcs.rx_mask,
0754 sizeof(sta->deflink.ht_cap.mcs.rx_mask));
0755 }
0756
0757 if (sta->deflink.vht_cap.vht_supported) {
0758 sta_priv->supported_rates.op_rate_mode = STA_11ac;
0759 sta_priv->supported_rates.vht_rx_mcs_map =
0760 sta->deflink.vht_cap.vht_mcs.rx_mcs_map;
0761 sta_priv->supported_rates.vht_tx_mcs_map =
0762 sta->deflink.vht_cap.vht_mcs.tx_mcs_map;
0763 }
0764 }
0765
0766 void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates)
0767 {
0768 u16 ofdm_rates[WCN36XX_HAL_NUM_OFDM_RATES] = {
0769 HW_RATE_INDEX_6MBPS,
0770 HW_RATE_INDEX_9MBPS,
0771 HW_RATE_INDEX_12MBPS,
0772 HW_RATE_INDEX_18MBPS,
0773 HW_RATE_INDEX_24MBPS,
0774 HW_RATE_INDEX_36MBPS,
0775 HW_RATE_INDEX_48MBPS,
0776 HW_RATE_INDEX_54MBPS
0777 };
0778 u16 dsss_rates[WCN36XX_HAL_NUM_DSSS_RATES] = {
0779 HW_RATE_INDEX_1MBPS,
0780 HW_RATE_INDEX_2MBPS,
0781 HW_RATE_INDEX_5_5MBPS,
0782 HW_RATE_INDEX_11MBPS
0783 };
0784
0785 rates->op_rate_mode = STA_11n;
0786 memcpy(rates->dsss_rates, dsss_rates,
0787 sizeof(*dsss_rates) * WCN36XX_HAL_NUM_DSSS_RATES);
0788 memcpy(rates->ofdm_rates, ofdm_rates,
0789 sizeof(*ofdm_rates) * WCN36XX_HAL_NUM_OFDM_RATES);
0790 rates->supported_mcs_set[0] = 0xFF;
0791 }
0792
0793 void wcn36xx_set_default_rates_v1(struct wcn36xx_hal_supported_rates_v1 *rates)
0794 {
0795 rates->op_rate_mode = STA_11ac;
0796 rates->vht_rx_mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9;
0797 rates->vht_tx_mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9;
0798 }
0799
0800 static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
0801 struct ieee80211_vif *vif,
0802 struct ieee80211_bss_conf *bss_conf,
0803 u64 changed)
0804 {
0805 struct wcn36xx *wcn = hw->priv;
0806 struct sk_buff *skb = NULL;
0807 u16 tim_off, tim_len;
0808 enum wcn36xx_hal_link_state link_state;
0809 struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
0810
0811 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss info changed vif %p changed 0x%llx\n",
0812 vif, changed);
0813
0814 mutex_lock(&wcn->conf_mutex);
0815
0816 if (changed & BSS_CHANGED_BEACON_INFO) {
0817 wcn36xx_dbg(WCN36XX_DBG_MAC,
0818 "mac bss changed dtim period %d\n",
0819 bss_conf->dtim_period);
0820
0821 vif_priv->dtim_period = bss_conf->dtim_period;
0822 }
0823
0824 if (changed & BSS_CHANGED_BSSID) {
0825 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed_bssid %pM\n",
0826 bss_conf->bssid);
0827
0828 if (!is_zero_ether_addr(bss_conf->bssid)) {
0829 vif_priv->is_joining = true;
0830 vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
0831 wcn36xx_smd_set_link_st(wcn, bss_conf->bssid, vif->addr,
0832 WCN36XX_HAL_LINK_PREASSOC_STATE);
0833 wcn36xx_smd_join(wcn, bss_conf->bssid,
0834 vif->addr, WCN36XX_HW_CHANNEL(wcn));
0835 wcn36xx_smd_config_bss(wcn, vif, NULL,
0836 bss_conf->bssid, false);
0837 } else {
0838 vif_priv->is_joining = false;
0839 wcn36xx_smd_delete_bss(wcn, vif);
0840 wcn36xx_smd_set_link_st(wcn, bss_conf->bssid, vif->addr,
0841 WCN36XX_HAL_LINK_IDLE_STATE);
0842 vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE;
0843 }
0844 }
0845
0846 if (changed & BSS_CHANGED_SSID) {
0847 wcn36xx_dbg(WCN36XX_DBG_MAC,
0848 "mac bss changed ssid\n");
0849 wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "ssid ",
0850 vif->cfg.ssid, vif->cfg.ssid_len);
0851
0852 vif_priv->ssid.length = vif->cfg.ssid_len;
0853 memcpy(&vif_priv->ssid.ssid,
0854 vif->cfg.ssid,
0855 vif->cfg.ssid_len);
0856 }
0857
0858 if (changed & BSS_CHANGED_ASSOC) {
0859 vif_priv->is_joining = false;
0860 if (vif->cfg.assoc) {
0861 struct ieee80211_sta *sta;
0862 struct wcn36xx_sta *sta_priv;
0863
0864 wcn36xx_dbg(WCN36XX_DBG_MAC,
0865 "mac assoc bss %pM vif %pM AID=%d\n",
0866 bss_conf->bssid,
0867 vif->addr,
0868 vif->cfg.aid);
0869
0870 vif_priv->sta_assoc = true;
0871
0872
0873
0874
0875
0876
0877
0878 sta = ieee80211_find_sta(vif, bss_conf->bssid);
0879 if (!sta) {
0880 wcn36xx_err("sta %pM is not found\n",
0881 bss_conf->bssid);
0882 goto out;
0883 }
0884 sta_priv = wcn36xx_sta_to_priv(sta);
0885
0886 wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn));
0887
0888 wcn36xx_smd_set_link_st(wcn, bss_conf->bssid,
0889 vif->addr,
0890 WCN36XX_HAL_LINK_POSTASSOC_STATE);
0891 wcn36xx_smd_config_bss(wcn, vif, sta,
0892 bss_conf->bssid,
0893 true);
0894 sta_priv->aid = vif->cfg.aid;
0895
0896
0897
0898
0899 wcn36xx_smd_config_sta(wcn, vif, sta);
0900 if (vif->type == NL80211_IFTYPE_STATION)
0901 wcn36xx_smd_add_beacon_filter(wcn, vif);
0902 wcn36xx_enable_keep_alive_null_packet(wcn, vif);
0903 } else {
0904 wcn36xx_dbg(WCN36XX_DBG_MAC,
0905 "disassociated bss %pM vif %pM AID=%d\n",
0906 bss_conf->bssid,
0907 vif->addr,
0908 vif->cfg.aid);
0909 vif_priv->sta_assoc = false;
0910 wcn36xx_smd_set_link_st(wcn,
0911 bss_conf->bssid,
0912 vif->addr,
0913 WCN36XX_HAL_LINK_IDLE_STATE);
0914 }
0915 }
0916
0917 if (changed & BSS_CHANGED_AP_PROBE_RESP) {
0918 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed ap probe resp\n");
0919 skb = ieee80211_proberesp_get(hw, vif);
0920 if (!skb) {
0921 wcn36xx_err("failed to alloc probereq skb\n");
0922 goto out;
0923 }
0924
0925 wcn36xx_smd_update_proberesp_tmpl(wcn, vif, skb);
0926 dev_kfree_skb(skb);
0927 }
0928
0929 if (changed & BSS_CHANGED_BEACON_ENABLED ||
0930 changed & BSS_CHANGED_BEACON) {
0931 wcn36xx_dbg(WCN36XX_DBG_MAC,
0932 "mac bss changed beacon enabled %d\n",
0933 bss_conf->enable_beacon);
0934
0935 if (bss_conf->enable_beacon) {
0936 vif_priv->dtim_period = bss_conf->dtim_period;
0937 vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
0938 wcn36xx_smd_config_bss(wcn, vif, NULL,
0939 vif->addr, false);
0940 skb = ieee80211_beacon_get_tim(hw, vif, &tim_off,
0941 &tim_len, 0);
0942 if (!skb) {
0943 wcn36xx_err("failed to alloc beacon skb\n");
0944 goto out;
0945 }
0946 wcn36xx_smd_send_beacon(wcn, vif, skb, tim_off, 0);
0947 dev_kfree_skb(skb);
0948
0949 if (vif->type == NL80211_IFTYPE_ADHOC ||
0950 vif->type == NL80211_IFTYPE_MESH_POINT)
0951 link_state = WCN36XX_HAL_LINK_IBSS_STATE;
0952 else
0953 link_state = WCN36XX_HAL_LINK_AP_STATE;
0954
0955 wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr,
0956 link_state);
0957 } else {
0958 wcn36xx_smd_delete_bss(wcn, vif);
0959 wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr,
0960 WCN36XX_HAL_LINK_IDLE_STATE);
0961 }
0962 }
0963 out:
0964
0965 mutex_unlock(&wcn->conf_mutex);
0966 }
0967
0968
0969 static int wcn36xx_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
0970 {
0971 struct wcn36xx *wcn = hw->priv;
0972 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac set RTS threshold %d\n", value);
0973
0974 mutex_lock(&wcn->conf_mutex);
0975 wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_RTS_THRESHOLD, value);
0976 mutex_unlock(&wcn->conf_mutex);
0977
0978 return 0;
0979 }
0980
0981 static void wcn36xx_remove_interface(struct ieee80211_hw *hw,
0982 struct ieee80211_vif *vif)
0983 {
0984 struct wcn36xx *wcn = hw->priv;
0985 struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
0986 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac remove interface vif %p\n", vif);
0987
0988 mutex_lock(&wcn->conf_mutex);
0989
0990 list_del(&vif_priv->list);
0991 wcn36xx_smd_delete_sta_self(wcn, vif->addr);
0992
0993 mutex_unlock(&wcn->conf_mutex);
0994 }
0995
0996 static int wcn36xx_add_interface(struct ieee80211_hw *hw,
0997 struct ieee80211_vif *vif)
0998 {
0999 struct wcn36xx *wcn = hw->priv;
1000 struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
1001
1002 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac add interface vif %p type %d\n",
1003 vif, vif->type);
1004
1005 if (!(NL80211_IFTYPE_STATION == vif->type ||
1006 NL80211_IFTYPE_AP == vif->type ||
1007 NL80211_IFTYPE_ADHOC == vif->type ||
1008 NL80211_IFTYPE_MESH_POINT == vif->type)) {
1009 wcn36xx_warn("Unsupported interface type requested: %d\n",
1010 vif->type);
1011 return -EOPNOTSUPP;
1012 }
1013
1014 mutex_lock(&wcn->conf_mutex);
1015
1016 vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
1017 INIT_LIST_HEAD(&vif_priv->sta_list);
1018 list_add(&vif_priv->list, &wcn->vif_list);
1019 wcn36xx_smd_add_sta_self(wcn, vif);
1020
1021 mutex_unlock(&wcn->conf_mutex);
1022
1023 return 0;
1024 }
1025
1026 static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
1027 struct ieee80211_sta *sta)
1028 {
1029 struct wcn36xx *wcn = hw->priv;
1030 struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
1031 struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
1032 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n",
1033 vif, sta->addr);
1034
1035 mutex_lock(&wcn->conf_mutex);
1036
1037 spin_lock_init(&sta_priv->ampdu_lock);
1038 sta_priv->vif = vif_priv;
1039 list_add(&sta_priv->list, &vif_priv->sta_list);
1040
1041
1042
1043
1044
1045 if (NL80211_IFTYPE_STATION != vif->type) {
1046 wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn));
1047 sta_priv->aid = sta->aid;
1048 wcn36xx_smd_config_sta(wcn, vif, sta);
1049 }
1050
1051 mutex_unlock(&wcn->conf_mutex);
1052
1053 return 0;
1054 }
1055
1056 static int wcn36xx_sta_remove(struct ieee80211_hw *hw,
1057 struct ieee80211_vif *vif,
1058 struct ieee80211_sta *sta)
1059 {
1060 struct wcn36xx *wcn = hw->priv;
1061 struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(sta);
1062
1063 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta remove vif %p sta %pM index %d\n",
1064 vif, sta->addr, sta_priv->sta_index);
1065
1066 mutex_lock(&wcn->conf_mutex);
1067
1068 list_del(&sta_priv->list);
1069 wcn36xx_smd_delete_sta(wcn, sta_priv->sta_index);
1070 sta_priv->vif = NULL;
1071
1072 mutex_unlock(&wcn->conf_mutex);
1073
1074 return 0;
1075 }
1076
1077 #ifdef CONFIG_PM
1078
1079 static struct ieee80211_vif *wcn36xx_get_first_assoc_vif(struct wcn36xx *wcn)
1080 {
1081 struct wcn36xx_vif *vif_priv = NULL;
1082 struct ieee80211_vif *vif = NULL;
1083
1084 list_for_each_entry(vif_priv, &wcn->vif_list, list) {
1085 if (vif_priv->sta_assoc) {
1086 vif = wcn36xx_priv_to_vif(vif_priv);
1087 break;
1088 }
1089 }
1090 return vif;
1091 }
1092
1093 static int wcn36xx_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wow)
1094 {
1095 struct wcn36xx *wcn = hw->priv;
1096 struct ieee80211_vif *vif = NULL;
1097 int ret = 0;
1098
1099 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac suspend\n");
1100
1101 mutex_lock(&wcn->conf_mutex);
1102
1103 vif = wcn36xx_get_first_assoc_vif(wcn);
1104 if (vif) {
1105 ret = wcn36xx_smd_arp_offload(wcn, vif, true);
1106 if (ret)
1107 goto out;
1108 ret = wcn36xx_smd_ipv6_ns_offload(wcn, vif, true);
1109 if (ret)
1110 goto out;
1111 ret = wcn36xx_smd_gtk_offload(wcn, vif, true);
1112 if (ret)
1113 goto out;
1114 ret = wcn36xx_smd_set_power_params(wcn, true);
1115 if (ret)
1116 goto out;
1117 ret = wcn36xx_smd_wlan_host_suspend_ind(wcn);
1118 }
1119
1120
1121
1122
1123 disable_irq(wcn->tx_irq);
1124 disable_irq(wcn->rx_irq);
1125
1126 out:
1127 mutex_unlock(&wcn->conf_mutex);
1128 return ret;
1129 }
1130
1131 static int wcn36xx_resume(struct ieee80211_hw *hw)
1132 {
1133 struct wcn36xx *wcn = hw->priv;
1134 struct ieee80211_vif *vif = NULL;
1135
1136 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac resume\n");
1137
1138 mutex_lock(&wcn->conf_mutex);
1139 vif = wcn36xx_get_first_assoc_vif(wcn);
1140 if (vif) {
1141 wcn36xx_smd_host_resume(wcn);
1142 wcn36xx_smd_set_power_params(wcn, false);
1143 wcn36xx_smd_gtk_offload_get_info(wcn, vif);
1144 wcn36xx_smd_gtk_offload(wcn, vif, false);
1145 wcn36xx_smd_ipv6_ns_offload(wcn, vif, false);
1146 wcn36xx_smd_arp_offload(wcn, vif, false);
1147 }
1148
1149 enable_irq(wcn->tx_irq);
1150 enable_irq(wcn->rx_irq);
1151
1152 mutex_unlock(&wcn->conf_mutex);
1153
1154 return 0;
1155 }
1156
1157 static void wcn36xx_set_rekey_data(struct ieee80211_hw *hw,
1158 struct ieee80211_vif *vif,
1159 struct cfg80211_gtk_rekey_data *data)
1160 {
1161 struct wcn36xx *wcn = hw->priv;
1162 struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
1163
1164 mutex_lock(&wcn->conf_mutex);
1165
1166 memcpy(vif_priv->rekey_data.kek, data->kek, NL80211_KEK_LEN);
1167 memcpy(vif_priv->rekey_data.kck, data->kck, NL80211_KCK_LEN);
1168 vif_priv->rekey_data.replay_ctr =
1169 cpu_to_le64(be64_to_cpup((__be64 *)data->replay_ctr));
1170 vif_priv->rekey_data.valid = true;
1171
1172 mutex_unlock(&wcn->conf_mutex);
1173 }
1174
1175 #endif
1176
1177 static int wcn36xx_ampdu_action(struct ieee80211_hw *hw,
1178 struct ieee80211_vif *vif,
1179 struct ieee80211_ampdu_params *params)
1180 {
1181 struct wcn36xx *wcn = hw->priv;
1182 struct wcn36xx_sta *sta_priv = wcn36xx_sta_to_priv(params->sta);
1183 struct ieee80211_sta *sta = params->sta;
1184 enum ieee80211_ampdu_mlme_action action = params->action;
1185 u16 tid = params->tid;
1186 u16 *ssn = ¶ms->ssn;
1187 int ret = 0;
1188 int session;
1189
1190 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n",
1191 action, tid);
1192
1193 mutex_lock(&wcn->conf_mutex);
1194
1195 switch (action) {
1196 case IEEE80211_AMPDU_RX_START:
1197 sta_priv->tid = tid;
1198 session = wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 0,
1199 get_sta_index(vif, sta_priv));
1200 if (session < 0) {
1201 ret = session;
1202 goto out;
1203 }
1204 wcn36xx_smd_add_ba(wcn, session);
1205 break;
1206 case IEEE80211_AMPDU_RX_STOP:
1207 wcn36xx_smd_del_ba(wcn, tid, 0, get_sta_index(vif, sta_priv));
1208 break;
1209 case IEEE80211_AMPDU_TX_START:
1210 spin_lock_bh(&sta_priv->ampdu_lock);
1211 sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START;
1212 spin_unlock_bh(&sta_priv->ampdu_lock);
1213
1214
1215 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu ssn = %u\n", *ssn);
1216 wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv), tid, ssn);
1217 wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu fw-ssn = %u\n", *ssn);
1218
1219
1220 session = wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1,
1221 get_sta_index(vif, sta_priv));
1222 if (session < 0) {
1223 ret = session;
1224 goto out;
1225 }
1226 ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;
1227 break;
1228 case IEEE80211_AMPDU_TX_OPERATIONAL:
1229 spin_lock_bh(&sta_priv->ampdu_lock);
1230 sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_OPERATIONAL;
1231 spin_unlock_bh(&sta_priv->ampdu_lock);
1232
1233 break;
1234 case IEEE80211_AMPDU_TX_STOP_FLUSH:
1235 case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
1236 case IEEE80211_AMPDU_TX_STOP_CONT:
1237 spin_lock_bh(&sta_priv->ampdu_lock);
1238 sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_NONE;
1239 spin_unlock_bh(&sta_priv->ampdu_lock);
1240
1241 wcn36xx_smd_del_ba(wcn, tid, 1, get_sta_index(vif, sta_priv));
1242 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1243 break;
1244 default:
1245 wcn36xx_err("Unknown AMPDU action\n");
1246 }
1247
1248 out:
1249 mutex_unlock(&wcn->conf_mutex);
1250
1251 return ret;
1252 }
1253
1254 #if IS_ENABLED(CONFIG_IPV6)
1255 static void wcn36xx_ipv6_addr_change(struct ieee80211_hw *hw,
1256 struct ieee80211_vif *vif,
1257 struct inet6_dev *idev)
1258 {
1259 struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
1260 struct inet6_ifaddr *ifa;
1261 int idx = 0;
1262
1263 memset(vif_priv->tentative_addrs, 0, sizeof(vif_priv->tentative_addrs));
1264
1265 read_lock_bh(&idev->lock);
1266 list_for_each_entry(ifa, &idev->addr_list, if_list) {
1267 vif_priv->target_ipv6_addrs[idx] = ifa->addr;
1268 if (ifa->flags & IFA_F_TENTATIVE)
1269 __set_bit(idx, vif_priv->tentative_addrs);
1270 idx++;
1271 if (idx >= WCN36XX_HAL_IPV6_OFFLOAD_ADDR_MAX)
1272 break;
1273 wcn36xx_dbg(WCN36XX_DBG_MAC, "%pI6 %s\n", &ifa->addr,
1274 (ifa->flags & IFA_F_TENTATIVE) ? "tentative" : NULL);
1275 }
1276 read_unlock_bh(&idev->lock);
1277
1278 vif_priv->num_target_ipv6_addrs = idx;
1279 }
1280 #endif
1281
1282 static void wcn36xx_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
1283 u32 queues, bool drop)
1284 {
1285 struct wcn36xx *wcn = hw->priv;
1286
1287 if (wcn36xx_dxe_tx_flush(wcn)) {
1288 wcn36xx_err("Failed to flush hardware tx queues\n");
1289 }
1290 }
1291
1292 static int wcn36xx_get_survey(struct ieee80211_hw *hw, int idx,
1293 struct survey_info *survey)
1294 {
1295 struct wcn36xx *wcn = hw->priv;
1296 struct ieee80211_supported_band *sband;
1297 struct wcn36xx_chan_survey *chan_survey;
1298 int band_idx;
1299 unsigned long flags;
1300
1301 sband = wcn->hw->wiphy->bands[NL80211_BAND_2GHZ];
1302 band_idx = idx;
1303 if (band_idx >= sband->n_channels) {
1304 band_idx -= sband->n_channels;
1305 sband = wcn->hw->wiphy->bands[NL80211_BAND_5GHZ];
1306 }
1307
1308 if (!sband || band_idx >= sband->n_channels)
1309 return -ENOENT;
1310
1311 spin_lock_irqsave(&wcn->survey_lock, flags);
1312
1313 chan_survey = &wcn->chan_survey[idx];
1314 survey->channel = &sband->channels[band_idx];
1315 survey->noise = chan_survey->rssi - chan_survey->snr;
1316 survey->filled = 0;
1317
1318 if (chan_survey->rssi > -100 && chan_survey->rssi < 0)
1319 survey->filled |= SURVEY_INFO_NOISE_DBM;
1320
1321 if (survey->channel == wcn->channel)
1322 survey->filled |= SURVEY_INFO_IN_USE;
1323
1324 spin_unlock_irqrestore(&wcn->survey_lock, flags);
1325
1326 wcn36xx_dbg(WCN36XX_DBG_MAC,
1327 "ch %d rssi %d snr %d noise %d filled %x freq %d\n",
1328 HW_VALUE_CHANNEL(survey->channel->hw_value),
1329 chan_survey->rssi, chan_survey->snr, survey->noise,
1330 survey->filled, survey->channel->center_freq);
1331
1332 return 0;
1333 }
1334
1335 static void wcn36xx_sta_statistics(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
1336 struct ieee80211_sta *sta, struct station_info *sinfo)
1337 {
1338 struct wcn36xx *wcn;
1339 u8 sta_index;
1340 int status;
1341
1342 wcn = hw->priv;
1343 sta_index = get_sta_index(vif, wcn36xx_sta_to_priv(sta));
1344 status = wcn36xx_smd_get_stats(wcn, sta_index, HAL_GLOBAL_CLASS_A_STATS_INFO, sinfo);
1345
1346 if (status)
1347 wcn36xx_err("wcn36xx_smd_get_stats failed\n");
1348 }
1349
1350 static const struct ieee80211_ops wcn36xx_ops = {
1351 .start = wcn36xx_start,
1352 .stop = wcn36xx_stop,
1353 .add_interface = wcn36xx_add_interface,
1354 .remove_interface = wcn36xx_remove_interface,
1355 #ifdef CONFIG_PM
1356 .suspend = wcn36xx_suspend,
1357 .resume = wcn36xx_resume,
1358 .set_rekey_data = wcn36xx_set_rekey_data,
1359 #endif
1360 .config = wcn36xx_config,
1361 .prepare_multicast = wcn36xx_prepare_multicast,
1362 .configure_filter = wcn36xx_configure_filter,
1363 .tx = wcn36xx_tx,
1364 .set_key = wcn36xx_set_key,
1365 .hw_scan = wcn36xx_hw_scan,
1366 .cancel_hw_scan = wcn36xx_cancel_hw_scan,
1367 .sw_scan_start = wcn36xx_sw_scan_start,
1368 .sw_scan_complete = wcn36xx_sw_scan_complete,
1369 .bss_info_changed = wcn36xx_bss_info_changed,
1370 .set_rts_threshold = wcn36xx_set_rts_threshold,
1371 .sta_add = wcn36xx_sta_add,
1372 .sta_remove = wcn36xx_sta_remove,
1373 .sta_statistics = wcn36xx_sta_statistics,
1374 .ampdu_action = wcn36xx_ampdu_action,
1375 #if IS_ENABLED(CONFIG_IPV6)
1376 .ipv6_addr_change = wcn36xx_ipv6_addr_change,
1377 #endif
1378 .flush = wcn36xx_flush,
1379 .get_survey = wcn36xx_get_survey,
1380
1381 CFG80211_TESTMODE_CMD(wcn36xx_tm_cmd)
1382 };
1383
1384 static void
1385 wcn36xx_set_ieee80211_vht_caps(struct ieee80211_sta_vht_cap *vht_cap)
1386 {
1387 vht_cap->vht_supported = true;
1388
1389 vht_cap->cap = (IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895 |
1390 IEEE80211_VHT_CAP_SHORT_GI_80 |
1391 IEEE80211_VHT_CAP_RXSTBC_1 |
1392 IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
1393 IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE |
1394 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT |
1395 7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT);
1396
1397 vht_cap->vht_mcs.rx_mcs_map =
1398 cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 |
1399 IEEE80211_VHT_MCS_NOT_SUPPORTED << 2 |
1400 IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 |
1401 IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 |
1402 IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 |
1403 IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 |
1404 IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 |
1405 IEEE80211_VHT_MCS_NOT_SUPPORTED << 14);
1406
1407 vht_cap->vht_mcs.rx_highest = cpu_to_le16(433);
1408 vht_cap->vht_mcs.tx_highest = vht_cap->vht_mcs.rx_highest;
1409
1410 vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map;
1411 }
1412
1413 static int wcn36xx_init_ieee80211(struct wcn36xx *wcn)
1414 {
1415 static const u32 cipher_suites[] = {
1416 WLAN_CIPHER_SUITE_WEP40,
1417 WLAN_CIPHER_SUITE_WEP104,
1418 WLAN_CIPHER_SUITE_TKIP,
1419 WLAN_CIPHER_SUITE_CCMP,
1420 };
1421
1422 ieee80211_hw_set(wcn->hw, TIMING_BEACON_ONLY);
1423 ieee80211_hw_set(wcn->hw, AMPDU_AGGREGATION);
1424 ieee80211_hw_set(wcn->hw, SUPPORTS_PS);
1425 ieee80211_hw_set(wcn->hw, SIGNAL_DBM);
1426 ieee80211_hw_set(wcn->hw, HAS_RATE_CONTROL);
1427 ieee80211_hw_set(wcn->hw, SINGLE_SCAN_ON_ALL_BANDS);
1428 ieee80211_hw_set(wcn->hw, REPORTS_TX_ACK_STATUS);
1429
1430 wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
1431 BIT(NL80211_IFTYPE_AP) |
1432 BIT(NL80211_IFTYPE_ADHOC) |
1433 BIT(NL80211_IFTYPE_MESH_POINT);
1434
1435 wcn->hw->wiphy->bands[NL80211_BAND_2GHZ] = &wcn_band_2ghz;
1436 if (wcn->rf_id != RF_IRIS_WCN3620)
1437 wcn->hw->wiphy->bands[NL80211_BAND_5GHZ] = &wcn_band_5ghz;
1438
1439 if (wcn->rf_id == RF_IRIS_WCN3680)
1440 wcn36xx_set_ieee80211_vht_caps(&wcn_band_5ghz.vht_cap);
1441
1442 wcn->hw->wiphy->max_scan_ssids = WCN36XX_MAX_SCAN_SSIDS;
1443 wcn->hw->wiphy->max_scan_ie_len = WCN36XX_MAX_SCAN_IE_LEN;
1444
1445 wcn->hw->wiphy->cipher_suites = cipher_suites;
1446 wcn->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
1447
1448 #ifdef CONFIG_PM
1449 wcn->hw->wiphy->wowlan = &wowlan_support;
1450 #endif
1451
1452 wcn->hw->max_listen_interval = 200;
1453
1454 wcn->hw->queues = 4;
1455
1456 SET_IEEE80211_DEV(wcn->hw, wcn->dev);
1457
1458 wcn->hw->sta_data_size = sizeof(struct wcn36xx_sta);
1459 wcn->hw->vif_data_size = sizeof(struct wcn36xx_vif);
1460
1461 wiphy_ext_feature_set(wcn->hw->wiphy,
1462 NL80211_EXT_FEATURE_CQM_RSSI_LIST);
1463
1464 return 0;
1465 }
1466
1467 static int wcn36xx_platform_get_resources(struct wcn36xx *wcn,
1468 struct platform_device *pdev)
1469 {
1470 struct device_node *mmio_node;
1471 struct device_node *iris_node;
1472 int index;
1473 int ret;
1474
1475
1476 ret = platform_get_irq_byname(pdev, "tx");
1477 if (ret < 0)
1478 return ret;
1479 wcn->tx_irq = ret;
1480
1481
1482 ret = platform_get_irq_byname(pdev, "rx");
1483 if (ret < 0)
1484 return ret;
1485 wcn->rx_irq = ret;
1486
1487
1488 wcn->tx_enable_state = qcom_smem_state_get(&pdev->dev,
1489 "tx-enable", &wcn->tx_enable_state_bit);
1490 if (IS_ERR(wcn->tx_enable_state)) {
1491 wcn36xx_err("failed to get tx-enable state\n");
1492 return PTR_ERR(wcn->tx_enable_state);
1493 }
1494
1495
1496 wcn->tx_rings_empty_state = qcom_smem_state_get(&pdev->dev,
1497 "tx-rings-empty", &wcn->tx_rings_empty_state_bit);
1498 if (IS_ERR(wcn->tx_rings_empty_state)) {
1499 wcn36xx_err("failed to get tx-rings-empty state\n");
1500 return PTR_ERR(wcn->tx_rings_empty_state);
1501 }
1502
1503 mmio_node = of_parse_phandle(pdev->dev.parent->of_node, "qcom,mmio", 0);
1504 if (!mmio_node) {
1505 wcn36xx_err("failed to acquire qcom,mmio reference\n");
1506 return -EINVAL;
1507 }
1508
1509 wcn->is_pronto = !!of_device_is_compatible(mmio_node, "qcom,pronto");
1510
1511
1512 index = of_property_match_string(mmio_node, "reg-names", "ccu");
1513 wcn->ccu_base = of_iomap(mmio_node, index);
1514 if (!wcn->ccu_base) {
1515 wcn36xx_err("failed to map ccu memory\n");
1516 ret = -ENOMEM;
1517 goto put_mmio_node;
1518 }
1519
1520
1521 index = of_property_match_string(mmio_node, "reg-names", "dxe");
1522 wcn->dxe_base = of_iomap(mmio_node, index);
1523 if (!wcn->dxe_base) {
1524 wcn36xx_err("failed to map dxe memory\n");
1525 ret = -ENOMEM;
1526 goto unmap_ccu;
1527 }
1528
1529
1530 iris_node = of_get_child_by_name(mmio_node, "iris");
1531 if (iris_node) {
1532 if (of_device_is_compatible(iris_node, "qcom,wcn3620"))
1533 wcn->rf_id = RF_IRIS_WCN3620;
1534 if (of_device_is_compatible(iris_node, "qcom,wcn3660") ||
1535 of_device_is_compatible(iris_node, "qcom,wcn3660b"))
1536 wcn->rf_id = RF_IRIS_WCN3660;
1537 if (of_device_is_compatible(iris_node, "qcom,wcn3680"))
1538 wcn->rf_id = RF_IRIS_WCN3680;
1539 of_node_put(iris_node);
1540 }
1541
1542 of_node_put(mmio_node);
1543 return 0;
1544
1545 unmap_ccu:
1546 iounmap(wcn->ccu_base);
1547 put_mmio_node:
1548 of_node_put(mmio_node);
1549 return ret;
1550 }
1551
1552 static int wcn36xx_probe(struct platform_device *pdev)
1553 {
1554 struct ieee80211_hw *hw;
1555 struct wcn36xx *wcn;
1556 void *wcnss;
1557 int ret;
1558 const u8 *addr;
1559 int n_channels;
1560
1561 wcn36xx_dbg(WCN36XX_DBG_MAC, "platform probe\n");
1562
1563 wcnss = dev_get_drvdata(pdev->dev.parent);
1564
1565 hw = ieee80211_alloc_hw(sizeof(struct wcn36xx), &wcn36xx_ops);
1566 if (!hw) {
1567 wcn36xx_err("failed to alloc hw\n");
1568 ret = -ENOMEM;
1569 goto out_err;
1570 }
1571 platform_set_drvdata(pdev, hw);
1572 wcn = hw->priv;
1573 wcn->hw = hw;
1574 wcn->dev = &pdev->dev;
1575 wcn->first_boot = true;
1576 mutex_init(&wcn->conf_mutex);
1577 mutex_init(&wcn->hal_mutex);
1578 mutex_init(&wcn->scan_lock);
1579 __skb_queue_head_init(&wcn->amsdu);
1580
1581 wcn->hal_buf = devm_kmalloc(wcn->dev, WCN36XX_HAL_BUF_SIZE, GFP_KERNEL);
1582 if (!wcn->hal_buf) {
1583 ret = -ENOMEM;
1584 goto out_wq;
1585 }
1586
1587 n_channels = wcn_band_2ghz.n_channels + wcn_band_5ghz.n_channels;
1588 wcn->chan_survey = devm_kmalloc(wcn->dev, n_channels, GFP_KERNEL);
1589 if (!wcn->chan_survey) {
1590 ret = -ENOMEM;
1591 goto out_wq;
1592 }
1593
1594 ret = dma_set_mask_and_coherent(wcn->dev, DMA_BIT_MASK(32));
1595 if (ret < 0) {
1596 wcn36xx_err("failed to set DMA mask: %d\n", ret);
1597 goto out_wq;
1598 }
1599
1600 wcn->nv_file = WLAN_NV_FILE;
1601 ret = of_property_read_string(wcn->dev->parent->of_node, "firmware-name", &wcn->nv_file);
1602 if (ret < 0 && ret != -EINVAL) {
1603 wcn36xx_err("failed to read \"firmware-name\" property: %d\n", ret);
1604 goto out_wq;
1605 }
1606
1607 wcn->smd_channel = qcom_wcnss_open_channel(wcnss, "WLAN_CTRL", wcn36xx_smd_rsp_process, hw);
1608 if (IS_ERR(wcn->smd_channel)) {
1609 wcn36xx_err("failed to open WLAN_CTRL channel\n");
1610 ret = PTR_ERR(wcn->smd_channel);
1611 goto out_wq;
1612 }
1613
1614 addr = of_get_property(pdev->dev.of_node, "local-mac-address", &ret);
1615 if (addr && ret != ETH_ALEN) {
1616 wcn36xx_err("invalid local-mac-address\n");
1617 ret = -EINVAL;
1618 goto out_destroy_ept;
1619 } else if (addr) {
1620 wcn36xx_info("mac address: %pM\n", addr);
1621 SET_IEEE80211_PERM_ADDR(wcn->hw, addr);
1622 }
1623
1624 ret = wcn36xx_platform_get_resources(wcn, pdev);
1625 if (ret)
1626 goto out_destroy_ept;
1627
1628 wcn36xx_init_ieee80211(wcn);
1629 ret = ieee80211_register_hw(wcn->hw);
1630 if (ret)
1631 goto out_unmap;
1632
1633 return 0;
1634
1635 out_unmap:
1636 iounmap(wcn->ccu_base);
1637 iounmap(wcn->dxe_base);
1638 out_destroy_ept:
1639 rpmsg_destroy_ept(wcn->smd_channel);
1640 out_wq:
1641 ieee80211_free_hw(hw);
1642 out_err:
1643 return ret;
1644 }
1645
1646 static int wcn36xx_remove(struct platform_device *pdev)
1647 {
1648 struct ieee80211_hw *hw = platform_get_drvdata(pdev);
1649 struct wcn36xx *wcn = hw->priv;
1650 wcn36xx_dbg(WCN36XX_DBG_MAC, "platform remove\n");
1651
1652 release_firmware(wcn->nv);
1653
1654 ieee80211_unregister_hw(hw);
1655
1656 qcom_smem_state_put(wcn->tx_enable_state);
1657 qcom_smem_state_put(wcn->tx_rings_empty_state);
1658
1659 rpmsg_destroy_ept(wcn->smd_channel);
1660
1661 iounmap(wcn->dxe_base);
1662 iounmap(wcn->ccu_base);
1663
1664 __skb_queue_purge(&wcn->amsdu);
1665
1666 mutex_destroy(&wcn->hal_mutex);
1667 ieee80211_free_hw(hw);
1668
1669 return 0;
1670 }
1671
1672 static const struct of_device_id wcn36xx_of_match[] = {
1673 { .compatible = "qcom,wcnss-wlan" },
1674 {}
1675 };
1676 MODULE_DEVICE_TABLE(of, wcn36xx_of_match);
1677
1678 static struct platform_driver wcn36xx_driver = {
1679 .probe = wcn36xx_probe,
1680 .remove = wcn36xx_remove,
1681 .driver = {
1682 .name = "wcn36xx",
1683 .of_match_table = wcn36xx_of_match,
1684 },
1685 };
1686
1687 module_platform_driver(wcn36xx_driver);
1688
1689 MODULE_LICENSE("Dual BSD/GPL");
1690 MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com");
1691 MODULE_FIRMWARE(WLAN_NV_FILE);