blob: 203fbebf09eb472e8fa49a9178264181c0b15b3b [file] [log] [blame]
Luciano Coelhof5fc0f82009-08-06 16:25:28 +03001/*
2 * This file is part of wl1271
3 *
4 * Copyright (C) 2009 Nokia Corporation
5 *
6 * Contact: Luciano Coelho <luciano.coelho@nokia.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 *
22 */
23
24#include <linux/kernel.h>
25#include <linux/module.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090026#include <linux/slab.h>
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030027
Luciano Coelho0f4e3122011-10-07 11:02:42 +030028#include "debug.h"
Shahar Levi00d20102010-11-08 11:20:10 +000029#include "init.h"
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030030#include "wl12xx_80211.h"
Shahar Levi00d20102010-11-08 11:20:10 +000031#include "acx.h"
32#include "cmd.h"
33#include "reg.h"
Arik Nemtsove0fe3712010-10-16 18:19:53 +020034#include "tx.h"
Shahar Levi48a61472011-03-06 16:32:08 +020035#include "io.h"
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030036
Eliad Peller92c77c72011-10-05 11:55:40 +020037int wl1271_init_templates_config(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030038{
Juuso Oikarinenbfb24c92010-03-26 12:53:31 +020039 int ret, i;
Eliad Peller5ec8a442012-02-02 12:22:09 +020040 size_t max_size;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030041
42 /* send empty templates for fw memory reservation */
Eliad Pellercdaac622012-01-31 11:57:16 +020043 ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
44 CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL,
Ido Reisc08e3712012-02-02 13:54:27 +020045 WL1271_CMD_TEMPL_MAX_SIZE,
Juuso Oikarinen606c1482010-04-01 11:38:21 +030046 0, WL1271_RATE_AUTOMATIC);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030047 if (ret < 0)
48 return ret;
49
Eliad Pellercdaac622012-01-31 11:57:16 +020050 ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
51 CMD_TEMPL_CFG_PROBE_REQ_5,
Ido Reisc08e3712012-02-02 13:54:27 +020052 NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0,
Juuso Oikarinen11eb5422010-08-24 06:28:03 +030053 WL1271_RATE_AUTOMATIC);
54 if (ret < 0)
55 return ret;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +030056
Eliad Pellercdaac622012-01-31 11:57:16 +020057 ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
58 CMD_TEMPL_NULL_DATA, NULL,
Juuso Oikarinenbfb24c92010-03-26 12:53:31 +020059 sizeof(struct wl12xx_null_data_template),
Juuso Oikarinen606c1482010-04-01 11:38:21 +030060 0, WL1271_RATE_AUTOMATIC);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030061 if (ret < 0)
62 return ret;
63
Eliad Pellercdaac622012-01-31 11:57:16 +020064 ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
65 CMD_TEMPL_PS_POLL, NULL,
Juuso Oikarinenbfb24c92010-03-26 12:53:31 +020066 sizeof(struct wl12xx_ps_poll_template),
Juuso Oikarinen606c1482010-04-01 11:38:21 +030067 0, WL1271_RATE_AUTOMATIC);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030068 if (ret < 0)
69 return ret;
70
Eliad Pellercdaac622012-01-31 11:57:16 +020071 ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
72 CMD_TEMPL_QOS_NULL_DATA, NULL,
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030073 sizeof
Eliad Peller97127e62011-11-09 13:12:45 +020074 (struct ieee80211_qos_hdr),
Juuso Oikarinen606c1482010-04-01 11:38:21 +030075 0, WL1271_RATE_AUTOMATIC);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030076 if (ret < 0)
77 return ret;
78
Eliad Pellercdaac622012-01-31 11:57:16 +020079 ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
80 CMD_TEMPL_PROBE_RESPONSE, NULL,
Eliad Peller154037d2011-08-14 13:17:12 +030081 WL1271_CMD_TEMPL_DFLT_SIZE,
Juuso Oikarinen606c1482010-04-01 11:38:21 +030082 0, WL1271_RATE_AUTOMATIC);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030083 if (ret < 0)
84 return ret;
85
Eliad Pellercdaac622012-01-31 11:57:16 +020086 ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
87 CMD_TEMPL_BEACON, NULL,
Eliad Peller154037d2011-08-14 13:17:12 +030088 WL1271_CMD_TEMPL_DFLT_SIZE,
Juuso Oikarinen606c1482010-04-01 11:38:21 +030089 0, WL1271_RATE_AUTOMATIC);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030090 if (ret < 0)
91 return ret;
92
Eliad Peller5ec8a442012-02-02 12:22:09 +020093 max_size = sizeof(struct wl12xx_arp_rsp_template) +
94 WL1271_EXTRA_SPACE_MAX;
Eliad Pellercdaac622012-01-31 11:57:16 +020095 ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
96 CMD_TEMPL_ARP_RSP, NULL,
Eliad Peller5ec8a442012-02-02 12:22:09 +020097 max_size,
Eliad Pellerc5312772010-12-09 11:31:27 +020098 0, WL1271_RATE_AUTOMATIC);
99 if (ret < 0)
100 return ret;
101
Eliad Peller92c77c72011-10-05 11:55:40 +0200102 /*
103 * Put very large empty placeholders for all templates. These
104 * reserve memory for later.
105 */
Eliad Pellercdaac622012-01-31 11:57:16 +0200106 ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
107 CMD_TEMPL_AP_PROBE_RESPONSE, NULL,
Eliad Peller92c77c72011-10-05 11:55:40 +0200108 WL1271_CMD_TEMPL_MAX_SIZE,
109 0, WL1271_RATE_AUTOMATIC);
110 if (ret < 0)
111 return ret;
112
Eliad Pellercdaac622012-01-31 11:57:16 +0200113 ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
114 CMD_TEMPL_AP_BEACON, NULL,
Eliad Peller92c77c72011-10-05 11:55:40 +0200115 WL1271_CMD_TEMPL_MAX_SIZE,
116 0, WL1271_RATE_AUTOMATIC);
117 if (ret < 0)
118 return ret;
119
Eliad Pellercdaac622012-01-31 11:57:16 +0200120 ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
121 CMD_TEMPL_DEAUTH_AP, NULL,
Eliad Peller92c77c72011-10-05 11:55:40 +0200122 sizeof
123 (struct wl12xx_disconn_template),
124 0, WL1271_RATE_AUTOMATIC);
125 if (ret < 0)
126 return ret;
127
Juuso Oikarinenbfb24c92010-03-26 12:53:31 +0200128 for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) {
Eliad Pellercdaac622012-01-31 11:57:16 +0200129 ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID,
130 CMD_TEMPL_KLV, NULL,
Eliad Peller97127e62011-11-09 13:12:45 +0200131 sizeof(struct ieee80211_qos_hdr),
132 i, WL1271_RATE_AUTOMATIC);
Juuso Oikarinenbfb24c92010-03-26 12:53:31 +0200133 if (ret < 0)
134 return ret;
135 }
136
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300137 return 0;
138}
139
Eliad Peller87fbcb02011-10-05 11:55:41 +0200140static int wl1271_ap_init_deauth_template(struct wl1271 *wl,
141 struct wl12xx_vif *wlvif)
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200142{
143 struct wl12xx_disconn_template *tmpl;
144 int ret;
Eliad Pelleraf7fbb22011-09-19 13:51:42 +0300145 u32 rate;
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200146
147 tmpl = kzalloc(sizeof(*tmpl), GFP_KERNEL);
148 if (!tmpl) {
149 ret = -ENOMEM;
150 goto out;
151 }
152
153 tmpl->header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT |
154 IEEE80211_STYPE_DEAUTH);
155
Eliad Peller87fbcb02011-10-05 11:55:41 +0200156 rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
Eliad Pellercdaac622012-01-31 11:57:16 +0200157 ret = wl1271_cmd_template_set(wl, wlvif->role_id,
158 CMD_TEMPL_DEAUTH_AP,
Eliad Pelleraf7fbb22011-09-19 13:51:42 +0300159 tmpl, sizeof(*tmpl), 0, rate);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200160
161out:
162 kfree(tmpl);
163 return ret;
164}
165
Eliad Peller784f6942011-10-05 11:55:39 +0200166static int wl1271_ap_init_null_template(struct wl1271 *wl,
167 struct ieee80211_vif *vif)
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200168{
Eliad Peller87fbcb02011-10-05 11:55:41 +0200169 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200170 struct ieee80211_hdr_3addr *nullfunc;
171 int ret;
Eliad Pelleraf7fbb22011-09-19 13:51:42 +0300172 u32 rate;
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200173
174 nullfunc = kzalloc(sizeof(*nullfunc), GFP_KERNEL);
175 if (!nullfunc) {
176 ret = -ENOMEM;
177 goto out;
178 }
179
180 nullfunc->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
181 IEEE80211_STYPE_NULLFUNC |
182 IEEE80211_FCTL_FROMDS);
183
184 /* nullfunc->addr1 is filled by FW */
185
Eliad Peller784f6942011-10-05 11:55:39 +0200186 memcpy(nullfunc->addr2, vif->addr, ETH_ALEN);
187 memcpy(nullfunc->addr3, vif->addr, ETH_ALEN);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200188
Eliad Peller87fbcb02011-10-05 11:55:41 +0200189 rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
Eliad Pellercdaac622012-01-31 11:57:16 +0200190 ret = wl1271_cmd_template_set(wl, wlvif->role_id,
191 CMD_TEMPL_NULL_DATA, nullfunc,
Eliad Pelleraf7fbb22011-09-19 13:51:42 +0300192 sizeof(*nullfunc), 0, rate);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200193
194out:
195 kfree(nullfunc);
196 return ret;
197}
198
Eliad Peller784f6942011-10-05 11:55:39 +0200199static int wl1271_ap_init_qos_null_template(struct wl1271 *wl,
200 struct ieee80211_vif *vif)
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200201{
Eliad Peller87fbcb02011-10-05 11:55:41 +0200202 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200203 struct ieee80211_qos_hdr *qosnull;
204 int ret;
Eliad Pelleraf7fbb22011-09-19 13:51:42 +0300205 u32 rate;
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200206
207 qosnull = kzalloc(sizeof(*qosnull), GFP_KERNEL);
208 if (!qosnull) {
209 ret = -ENOMEM;
210 goto out;
211 }
212
213 qosnull->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
214 IEEE80211_STYPE_QOS_NULLFUNC |
215 IEEE80211_FCTL_FROMDS);
216
217 /* qosnull->addr1 is filled by FW */
218
Eliad Peller784f6942011-10-05 11:55:39 +0200219 memcpy(qosnull->addr2, vif->addr, ETH_ALEN);
220 memcpy(qosnull->addr3, vif->addr, ETH_ALEN);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200221
Eliad Peller87fbcb02011-10-05 11:55:41 +0200222 rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
Eliad Pellercdaac622012-01-31 11:57:16 +0200223 ret = wl1271_cmd_template_set(wl, wlvif->role_id,
224 CMD_TEMPL_QOS_NULL_DATA, qosnull,
Eliad Pelleraf7fbb22011-09-19 13:51:42 +0300225 sizeof(*qosnull), 0, rate);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200226
227out:
228 kfree(qosnull);
229 return ret;
230}
231
Eliad Peller08c1d1c2011-08-14 13:17:04 +0300232static int wl12xx_init_rx_config(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300233{
234 int ret;
235
Juuso Oikarinen8793f9b2009-10-13 12:47:40 +0300236 ret = wl1271_acx_rx_msdu_life_time(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300237 if (ret < 0)
238 return ret;
239
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300240 return 0;
241}
242
Eliad Peller0603d892011-10-05 11:55:51 +0200243static int wl12xx_init_phy_vif_config(struct wl1271 *wl,
244 struct wl12xx_vif *wlvif)
Eliad Peller92c77c72011-10-05 11:55:40 +0200245{
246 int ret;
247
Eliad Peller0603d892011-10-05 11:55:51 +0200248 ret = wl1271_acx_slot(wl, wlvif, DEFAULT_SLOT_TIME);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300249 if (ret < 0)
250 return ret;
251
Eliad Peller0603d892011-10-05 11:55:51 +0200252 ret = wl1271_acx_service_period_timeout(wl, wlvif);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300253 if (ret < 0)
254 return ret;
255
Eliad Peller0603d892011-10-05 11:55:51 +0200256 ret = wl1271_acx_rts_threshold(wl, wlvif, wl->hw->wiphy->rts_threshold);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300257 if (ret < 0)
258 return ret;
259
260 return 0;
261}
262
Arik Nemtsova6935342011-10-24 17:25:20 +0200263static int wl1271_init_sta_beacon_filter(struct wl1271 *wl,
264 struct wl12xx_vif *wlvif)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300265{
266 int ret;
267
Arik Nemtsova6935342011-10-24 17:25:20 +0200268 ret = wl1271_acx_beacon_filter_table(wl, wlvif);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300269 if (ret < 0)
270 return ret;
271
Arik Nemtsova6935342011-10-24 17:25:20 +0200272 /* enable beacon filtering */
273 ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300274 if (ret < 0)
275 return ret;
276
277 return 0;
278}
279
Luciano Coelho12419cce2010-02-18 13:25:44 +0200280int wl1271_init_pta(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300281{
282 int ret;
283
Eliad Peller3be41122011-08-14 13:17:19 +0300284 ret = wl12xx_acx_sg_cfg(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300285 if (ret < 0)
286 return ret;
287
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +0200288 ret = wl1271_acx_sg_enable(wl, wl->sg_enabled);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300289 if (ret < 0)
290 return ret;
291
292 return 0;
293}
294
Luciano Coelho12419cce2010-02-18 13:25:44 +0200295int wl1271_init_energy_detection(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300296{
297 int ret;
298
299 ret = wl1271_acx_cca_threshold(wl);
300 if (ret < 0)
301 return ret;
302
303 return 0;
304}
305
Eliad Peller0603d892011-10-05 11:55:51 +0200306static int wl1271_init_beacon_broadcast(struct wl1271 *wl,
307 struct wl12xx_vif *wlvif)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300308{
309 int ret;
310
Eliad Peller0603d892011-10-05 11:55:51 +0200311 ret = wl1271_acx_bcn_dtim_options(wl, wlvif);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300312 if (ret < 0)
313 return ret;
314
315 return 0;
316}
317
Ido Yariv95dac04f2011-06-06 14:57:06 +0300318static int wl12xx_init_fwlog(struct wl1271 *wl)
319{
320 int ret;
321
322 if (wl->quirks & WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED)
323 return 0;
324
325 ret = wl12xx_cmd_config_fwlog(wl);
326 if (ret < 0)
327 return ret;
328
329 return 0;
330}
331
Eliad Peller92c77c72011-10-05 11:55:40 +0200332/* generic sta initialization (non vif-specific) */
Eliad Peller30d0c8f2011-10-05 11:55:42 +0200333static int wl1271_sta_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200334{
335 int ret;
336
Eliad Pellerc8bde242011-02-02 09:59:35 +0200337 /* PS config */
Eliad Pellerd2d66c52011-10-05 11:55:43 +0200338 ret = wl12xx_acx_config_ps(wl, wlvif);
Eliad Pellerc8bde242011-02-02 09:59:35 +0200339 if (ret < 0)
340 return ret;
341
Shahar Leviff868432011-04-11 15:41:46 +0300342 /* FM WLAN coexistence */
343 ret = wl1271_acx_fm_coex(wl);
344 if (ret < 0)
345 return ret;
346
Eliad Peller30d0c8f2011-10-05 11:55:42 +0200347 ret = wl1271_acx_sta_rate_policies(wl, wlvif);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200348 if (ret < 0)
349 return ret;
350
351 return 0;
352}
353
Eliad Peller0603d892011-10-05 11:55:51 +0200354static int wl1271_sta_hw_init_post_mem(struct wl1271 *wl,
355 struct ieee80211_vif *vif)
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200356{
Eliad Peller0603d892011-10-05 11:55:51 +0200357 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200358 int ret, i;
359
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200360 /* disable all keep-alive templates */
361 for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) {
Eliad Peller0603d892011-10-05 11:55:51 +0200362 ret = wl1271_acx_keep_alive_config(wl, wlvif, i,
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200363 ACX_KEEP_ALIVE_TPL_INVALID);
364 if (ret < 0)
365 return ret;
366 }
367
368 /* disable the keep-alive feature */
Eliad Peller0603d892011-10-05 11:55:51 +0200369 ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200370 if (ret < 0)
371 return ret;
372
373 return 0;
374}
375
Eliad Peller92c77c72011-10-05 11:55:40 +0200376/* generic ap initialization (non vif-specific) */
Eliad Peller87fbcb02011-10-05 11:55:41 +0200377static int wl1271_ap_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200378{
Arik Nemtsov70f47422011-04-18 14:15:25 +0300379 int ret;
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200380
Eliad Peller87fbcb02011-10-05 11:55:41 +0200381 ret = wl1271_init_ap_rates(wl, wlvif);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200382 if (ret < 0)
383 return ret;
384
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200385 return 0;
386}
387
Eliad Peller784f6942011-10-05 11:55:39 +0200388int wl1271_ap_init_templates(struct wl1271 *wl, struct ieee80211_vif *vif)
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200389{
Eliad Peller87fbcb02011-10-05 11:55:41 +0200390 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200391 int ret;
392
Eliad Peller87fbcb02011-10-05 11:55:41 +0200393 ret = wl1271_ap_init_deauth_template(wl, wlvif);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200394 if (ret < 0)
395 return ret;
396
Eliad Peller784f6942011-10-05 11:55:39 +0200397 ret = wl1271_ap_init_null_template(wl, vif);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200398 if (ret < 0)
399 return ret;
400
Eliad Peller784f6942011-10-05 11:55:39 +0200401 ret = wl1271_ap_init_qos_null_template(wl, vif);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200402 if (ret < 0)
403 return ret;
404
Arik Nemtsov521a4a22011-04-18 14:15:22 +0300405 /*
406 * when operating as AP we want to receive external beacons for
407 * configuring ERP protection.
408 */
Eliad Peller0603d892011-10-05 11:55:51 +0200409 ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
Arik Nemtsov521a4a22011-04-18 14:15:22 +0300410 if (ret < 0)
411 return ret;
412
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200413 return 0;
414}
415
Eliad Peller784f6942011-10-05 11:55:39 +0200416static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl,
417 struct ieee80211_vif *vif)
Arik Nemtsovc45a85b2011-04-18 14:15:26 +0300418{
Eliad Peller784f6942011-10-05 11:55:39 +0200419 return wl1271_ap_init_templates(wl, vif);
Arik Nemtsovc45a85b2011-04-18 14:15:26 +0300420}
421
Eliad Peller87fbcb02011-10-05 11:55:41 +0200422int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Arik Nemtsov70f47422011-04-18 14:15:25 +0300423{
424 int i, ret;
425 struct conf_tx_rate_class rc;
426 u32 supported_rates;
427
Eliad Peller87fbcb02011-10-05 11:55:41 +0200428 wl1271_debug(DEBUG_AP, "AP basic rate set: 0x%x",
429 wlvif->basic_rate_set);
Arik Nemtsov70f47422011-04-18 14:15:25 +0300430
Eliad Peller87fbcb02011-10-05 11:55:41 +0200431 if (wlvif->basic_rate_set == 0)
Arik Nemtsov70f47422011-04-18 14:15:25 +0300432 return -EINVAL;
433
Eliad Peller87fbcb02011-10-05 11:55:41 +0200434 rc.enabled_rates = wlvif->basic_rate_set;
Arik Nemtsov70f47422011-04-18 14:15:25 +0300435 rc.long_retry_limit = 10;
436 rc.short_retry_limit = 10;
437 rc.aflags = 0;
Eliad Pellere5a359f2011-10-10 10:13:15 +0200438 ret = wl1271_acx_ap_rate_policy(wl, &rc, wlvif->ap.mgmt_rate_idx);
Arik Nemtsov70f47422011-04-18 14:15:25 +0300439 if (ret < 0)
440 return ret;
441
442 /* use the min basic rate for AP broadcast/multicast */
Eliad Peller87fbcb02011-10-05 11:55:41 +0200443 rc.enabled_rates = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
Arik Nemtsov70f47422011-04-18 14:15:25 +0300444 rc.short_retry_limit = 10;
445 rc.long_retry_limit = 10;
446 rc.aflags = 0;
Eliad Pellere5a359f2011-10-10 10:13:15 +0200447 ret = wl1271_acx_ap_rate_policy(wl, &rc, wlvif->ap.bcast_rate_idx);
Arik Nemtsov70f47422011-04-18 14:15:25 +0300448 if (ret < 0)
449 return ret;
450
451 /*
452 * If the basic rates contain OFDM rates, use OFDM only
453 * rates for unicast TX as well. Else use all supported rates.
454 */
Eliad Peller87fbcb02011-10-05 11:55:41 +0200455 if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES))
Arik Nemtsov70f47422011-04-18 14:15:25 +0300456 supported_rates = CONF_TX_OFDM_RATES;
457 else
458 supported_rates = CONF_TX_AP_ENABLED_RATES;
459
Arik Nemtsov1a8adb62011-08-14 13:17:29 +0300460 /* unconditionally enable HT rates */
461 supported_rates |= CONF_TX_MCS_RATES;
462
Arik Nemtsov70f47422011-04-18 14:15:25 +0300463 /* configure unicast TX rate classes */
464 for (i = 0; i < wl->conf.tx.ac_conf_count; i++) {
465 rc.enabled_rates = supported_rates;
466 rc.short_retry_limit = 10;
467 rc.long_retry_limit = 10;
468 rc.aflags = 0;
Eliad Pellere5a359f2011-10-10 10:13:15 +0200469 ret = wl1271_acx_ap_rate_policy(wl, &rc,
470 wlvif->ap.ucast_rate_idx[i]);
Arik Nemtsov70f47422011-04-18 14:15:25 +0300471 if (ret < 0)
472 return ret;
473 }
474
475 return 0;
476}
477
Eliad Peller536129c2011-10-05 11:55:45 +0200478static int wl1271_set_ba_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100479{
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100480 /* Reset the BA RX indicators */
Eliad Pellerd0802ab2011-10-05 11:56:04 +0200481 wlvif->ba_allowed = true;
Arik Nemtsov0f9c8252011-08-17 10:45:49 +0300482 wl->ba_rx_session_count = 0;
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100483
Arik Nemtsov0f9c8252011-08-17 10:45:49 +0300484 /* BA is supported in STA/AP modes */
Eliad Peller536129c2011-10-05 11:55:45 +0200485 if (wlvif->bss_type != BSS_TYPE_AP_BSS &&
486 wlvif->bss_type != BSS_TYPE_STA_BSS) {
Eliad Pellerd0802ab2011-10-05 11:56:04 +0200487 wlvif->ba_support = false;
Arik Nemtsov0f9c8252011-08-17 10:45:49 +0300488 return 0;
489 }
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100490
Eliad Pellerd0802ab2011-10-05 11:56:04 +0200491 wlvif->ba_support = true;
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100492
Arik Nemtsov0f9c8252011-08-17 10:45:49 +0300493 /* 802.11n initiator BA session setting */
Eliad Peller0603d892011-10-05 11:55:51 +0200494 return wl12xx_acx_set_ba_initiator_policy(wl, wlvif);
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100495}
496
Shahar Levi48a61472011-03-06 16:32:08 +0200497int wl1271_chip_specific_init(struct wl1271 *wl)
498{
499 int ret = 0;
500
501 if (wl->chip.id == CHIP_ID_1283_PG20) {
502 u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE;
503
Luciano Coelhoce39def2011-11-03 08:44:41 +0200504 if (!(wl->quirks & WL12XX_QUIRK_NO_BLOCKSIZE_ALIGNMENT))
Shahar Levi48a61472011-03-06 16:32:08 +0200505 /* Enable SDIO padding */
506 host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK;
507
508 /* Must be before wl1271_acx_init_mem_config() */
509 ret = wl1271_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap);
510 if (ret < 0)
511 goto out;
512 }
513out:
514 return ret;
515}
516
Eliad Peller92c77c72011-10-05 11:55:40 +0200517/* vif-specifc initialization */
Eliad Peller0603d892011-10-05 11:55:51 +0200518static int wl12xx_init_sta_role(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Eliad Peller92c77c72011-10-05 11:55:40 +0200519{
520 int ret;
Shahar Levi48a61472011-03-06 16:32:08 +0200521
Eliad Peller0603d892011-10-05 11:55:51 +0200522 ret = wl1271_acx_group_address_tbl(wl, wlvif, true, NULL, 0);
Eliad Peller92c77c72011-10-05 11:55:40 +0200523 if (ret < 0)
524 return ret;
525
526 /* Initialize connection monitoring thresholds */
Eliad Peller0603d892011-10-05 11:55:51 +0200527 ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
Eliad Peller92c77c72011-10-05 11:55:40 +0200528 if (ret < 0)
529 return ret;
530
531 /* Beacon filtering */
Arik Nemtsova6935342011-10-24 17:25:20 +0200532 ret = wl1271_init_sta_beacon_filter(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200533 if (ret < 0)
534 return ret;
535
536 /* Beacons and broadcast settings */
Eliad Peller0603d892011-10-05 11:55:51 +0200537 ret = wl1271_init_beacon_broadcast(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200538 if (ret < 0)
539 return ret;
540
541 /* Configure rssi/snr averaging weights */
Eliad Peller0603d892011-10-05 11:55:51 +0200542 ret = wl1271_acx_rssi_snr_avg_weights(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200543 if (ret < 0)
544 return ret;
545
546 return 0;
547}
548
549/* vif-specific intialization */
Eliad Peller0603d892011-10-05 11:55:51 +0200550static int wl12xx_init_ap_role(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Eliad Peller92c77c72011-10-05 11:55:40 +0200551{
552 int ret;
553
Eliad Peller0603d892011-10-05 11:55:51 +0200554 ret = wl1271_acx_ap_max_tx_retry(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200555 if (ret < 0)
556 return ret;
557
558 /* initialize Tx power */
Eliad Peller6bd65022011-10-10 10:13:11 +0200559 ret = wl1271_acx_tx_power(wl, wlvif, wlvif->power_level);
Eliad Peller92c77c72011-10-05 11:55:40 +0200560 if (ret < 0)
561 return ret;
562
563 return 0;
564}
565
566int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300567{
Eliad Peller87fbcb02011-10-05 11:55:41 +0200568 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Kalle Valo243eeb52010-02-18 13:25:39 +0200569 struct conf_tx_ac_category *conf_ac;
Kalle Valof2054df2010-02-18 13:25:40 +0200570 struct conf_tx_tid *conf_tid;
Eliad Peller536129c2011-10-05 11:55:45 +0200571 bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
Eliad Peller92c77c72011-10-05 11:55:40 +0200572 int ret, i;
573
Eliad Pellera4e41302011-10-11 11:49:15 +0200574 /*
575 * consider all existing roles before configuring psm.
576 * TODO: reconfigure on interface removal.
577 */
578 if (!wl->ap_count) {
579 if (is_ap) {
580 /* Configure for power always on */
581 ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
582 if (ret < 0)
583 return ret;
584 } else if (!wl->sta_count) {
585 /* Configure for ELP power saving */
586 ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
587 if (ret < 0)
588 return ret;
589 }
590 }
591
Eliad Peller92c77c72011-10-05 11:55:40 +0200592 /* Mode specific init */
593 if (is_ap) {
Eliad Peller87fbcb02011-10-05 11:55:41 +0200594 ret = wl1271_ap_hw_init(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200595 if (ret < 0)
596 return ret;
597
Eliad Peller0603d892011-10-05 11:55:51 +0200598 ret = wl12xx_init_ap_role(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200599 if (ret < 0)
600 return ret;
601 } else {
Eliad Peller30d0c8f2011-10-05 11:55:42 +0200602 ret = wl1271_sta_hw_init(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200603 if (ret < 0)
604 return ret;
605
Eliad Peller0603d892011-10-05 11:55:51 +0200606 ret = wl12xx_init_sta_role(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200607 if (ret < 0)
608 return ret;
609 }
610
Eliad Peller0603d892011-10-05 11:55:51 +0200611 wl12xx_init_phy_vif_config(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200612
613 /* Default TID/AC configuration */
614 BUG_ON(wl->conf.tx.tid_conf_count != wl->conf.tx.ac_conf_count);
615 for (i = 0; i < wl->conf.tx.tid_conf_count; i++) {
616 conf_ac = &wl->conf.tx.ac_conf[i];
Eliad Peller0603d892011-10-05 11:55:51 +0200617 ret = wl1271_acx_ac_cfg(wl, wlvif, conf_ac->ac,
618 conf_ac->cw_min, conf_ac->cw_max,
619 conf_ac->aifsn, conf_ac->tx_op_limit);
Eliad Peller92c77c72011-10-05 11:55:40 +0200620 if (ret < 0)
621 return ret;
622
623 conf_tid = &wl->conf.tx.tid_conf[i];
Eliad Peller0603d892011-10-05 11:55:51 +0200624 ret = wl1271_acx_tid_cfg(wl, wlvif,
Eliad Peller92c77c72011-10-05 11:55:40 +0200625 conf_tid->queue_id,
626 conf_tid->channel_type,
627 conf_tid->tsid,
628 conf_tid->ps_scheme,
629 conf_tid->ack_policy,
630 conf_tid->apsd_conf[0],
631 conf_tid->apsd_conf[1]);
632 if (ret < 0)
633 return ret;
634 }
635
636 /* Configure HW encryption */
Eliad Peller0603d892011-10-05 11:55:51 +0200637 ret = wl1271_acx_feature_cfg(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200638 if (ret < 0)
639 return ret;
640
641 /* Mode specific init - post mem init */
642 if (is_ap)
643 ret = wl1271_ap_hw_init_post_mem(wl, vif);
644 else
Eliad Peller0603d892011-10-05 11:55:51 +0200645 ret = wl1271_sta_hw_init_post_mem(wl, vif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200646
647 if (ret < 0)
648 return ret;
649
650 /* Configure initiator BA sessions policies */
Eliad Peller536129c2011-10-05 11:55:45 +0200651 ret = wl1271_set_ba_policies(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200652 if (ret < 0)
653 return ret;
654
655 return 0;
656}
657
658int wl1271_hw_init(struct wl1271 *wl)
659{
660 int ret;
661
Luciano Coelho2c8f82e2011-12-13 11:39:50 +0200662 if (wl->chip.id == CHIP_ID_1283_PG20) {
Shahar Levi49d750ca2011-03-06 16:32:09 +0200663 ret = wl128x_cmd_general_parms(wl);
Luciano Coelho2c8f82e2011-12-13 11:39:50 +0200664 if (ret < 0)
665 return ret;
Shahar Levi49d750ca2011-03-06 16:32:09 +0200666 ret = wl128x_cmd_radio_parms(wl);
Luciano Coelho2c8f82e2011-12-13 11:39:50 +0200667 if (ret < 0)
668 return ret;
669 } else {
670 ret = wl1271_cmd_general_parms(wl);
671 if (ret < 0)
672 return ret;
Shahar Levi49d750ca2011-03-06 16:32:09 +0200673 ret = wl1271_cmd_radio_parms(wl);
Luciano Coelho2c8f82e2011-12-13 11:39:50 +0200674 if (ret < 0)
675 return ret;
676 ret = wl1271_cmd_ext_radio_parms(wl);
677 if (ret < 0)
678 return ret;
679 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300680
Shahar Levi48a61472011-03-06 16:32:08 +0200681 /* Chip-specific init */
682 ret = wl1271_chip_specific_init(wl);
683 if (ret < 0)
684 return ret;
685
Eliad Peller92c77c72011-10-05 11:55:40 +0200686 /* Init templates */
687 ret = wl1271_init_templates_config(wl);
688 if (ret < 0)
689 return ret;
Juuso Oikarinen644a4862010-10-05 13:11:56 +0200690
Eliad Peller92c77c72011-10-05 11:55:40 +0200691 ret = wl12xx_acx_mem_cfg(wl);
692 if (ret < 0)
693 return ret;
694
695 /* Configure the FW logger */
696 ret = wl12xx_init_fwlog(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300697 if (ret < 0)
698 return ret;
699
Arik Nemtsov801f8702011-04-18 14:15:20 +0300700 /* Bluetooth WLAN coexistence */
701 ret = wl1271_init_pta(wl);
702 if (ret < 0)
703 return ret;
704
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300705 /* Default memory configuration */
706 ret = wl1271_acx_init_mem_config(wl);
707 if (ret < 0)
708 return ret;
709
710 /* RX config */
Eliad Peller08c1d1c2011-08-14 13:17:04 +0300711 ret = wl12xx_init_rx_config(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300712 if (ret < 0)
713 goto out_free_memmap;
714
Luciano Coelho6e92b412009-12-11 15:40:50 +0200715 ret = wl1271_acx_dco_itrim_params(wl);
716 if (ret < 0)
717 goto out_free_memmap;
718
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300719 /* Configure TX patch complete interrupt behavior */
720 ret = wl1271_acx_tx_config_options(wl);
721 if (ret < 0)
722 goto out_free_memmap;
723
724 /* RX complete interrupt pacing */
725 ret = wl1271_acx_init_rx_interrupt(wl);
726 if (ret < 0)
727 goto out_free_memmap;
728
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300729 /* Energy detection */
730 ret = wl1271_init_energy_detection(wl);
731 if (ret < 0)
732 goto out_free_memmap;
733
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300734 /* Default fragmentation threshold */
Arik Nemtsov5f704d12011-04-18 14:15:21 +0300735 ret = wl1271_acx_frag_threshold(wl, wl->hw->wiphy->frag_threshold);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300736 if (ret < 0)
737 goto out_free_memmap;
738
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300739 /* Enable data path */
Luciano Coelho94210892009-12-11 15:40:55 +0200740 ret = wl1271_cmd_data_path(wl, 1);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300741 if (ret < 0)
742 goto out_free_memmap;
743
Juuso Oikarinen38ad2d82009-12-11 15:41:08 +0200744 /* configure PM */
745 ret = wl1271_acx_pm_config(wl);
746 if (ret < 0)
747 goto out_free_memmap;
748
Eliad Pellerfa6ad9f2011-08-14 13:17:14 +0300749 ret = wl12xx_acx_set_rate_mgmt_params(wl);
750 if (ret < 0)
751 goto out_free_memmap;
752
Eliad Peller94877752011-08-28 15:11:56 +0300753 /* configure hangover */
754 ret = wl12xx_acx_config_hangover(wl);
755 if (ret < 0)
756 goto out_free_memmap;
757
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300758 return 0;
759
760 out_free_memmap:
761 kfree(wl->target_mem_map);
Juuso Oikarinen34415232009-10-08 21:56:33 +0300762 wl->target_mem_map = NULL;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300763
764 return ret;
765}