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