blob: ca7ee59e4505b297c104197bfde176e34336458b [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;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030040
41 /* send empty templates for fw memory reservation */
42 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL,
Eliad Peller154037d2011-08-14 13:17:12 +030043 WL1271_CMD_TEMPL_DFLT_SIZE,
Juuso Oikarinen606c1482010-04-01 11:38:21 +030044 0, WL1271_RATE_AUTOMATIC);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030045 if (ret < 0)
46 return ret;
47
Juuso Oikarinen11eb5422010-08-24 06:28:03 +030048 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5,
Eliad Peller154037d2011-08-14 13:17:12 +030049 NULL, WL1271_CMD_TEMPL_DFLT_SIZE, 0,
Juuso Oikarinen11eb5422010-08-24 06:28:03 +030050 WL1271_RATE_AUTOMATIC);
51 if (ret < 0)
52 return ret;
Teemu Paasikiviabb0b3b2009-10-13 12:47:50 +030053
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030054 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, NULL,
Juuso Oikarinenbfb24c92010-03-26 12:53:31 +020055 sizeof(struct wl12xx_null_data_template),
Juuso Oikarinen606c1482010-04-01 11:38:21 +030056 0, WL1271_RATE_AUTOMATIC);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030057 if (ret < 0)
58 return ret;
59
60 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, NULL,
Juuso Oikarinenbfb24c92010-03-26 12:53:31 +020061 sizeof(struct wl12xx_ps_poll_template),
Juuso Oikarinen606c1482010-04-01 11:38:21 +030062 0, WL1271_RATE_AUTOMATIC);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030063 if (ret < 0)
64 return ret;
65
66 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, NULL,
67 sizeof
Eliad Peller97127e62011-11-09 13:12:45 +020068 (struct ieee80211_qos_hdr),
Juuso Oikarinen606c1482010-04-01 11:38:21 +030069 0, WL1271_RATE_AUTOMATIC);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030070 if (ret < 0)
71 return ret;
72
73 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE, NULL,
Eliad Peller154037d2011-08-14 13:17:12 +030074 WL1271_CMD_TEMPL_DFLT_SIZE,
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
79 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, NULL,
Eliad Peller154037d2011-08-14 13:17:12 +030080 WL1271_CMD_TEMPL_DFLT_SIZE,
Juuso Oikarinen606c1482010-04-01 11:38:21 +030081 0, WL1271_RATE_AUTOMATIC);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +030082 if (ret < 0)
83 return ret;
84
Eliad Pellerc5312772010-12-09 11:31:27 +020085 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_ARP_RSP, NULL,
86 sizeof
87 (struct wl12xx_arp_rsp_template),
88 0, WL1271_RATE_AUTOMATIC);
89 if (ret < 0)
90 return ret;
91
Eliad Peller92c77c72011-10-05 11:55:40 +020092 /*
93 * Put very large empty placeholders for all templates. These
94 * reserve memory for later.
95 */
96 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_AP_PROBE_RESPONSE, NULL,
97 WL1271_CMD_TEMPL_MAX_SIZE,
98 0, WL1271_RATE_AUTOMATIC);
99 if (ret < 0)
100 return ret;
101
102 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_AP_BEACON, NULL,
103 WL1271_CMD_TEMPL_MAX_SIZE,
104 0, WL1271_RATE_AUTOMATIC);
105 if (ret < 0)
106 return ret;
107
108 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_DEAUTH_AP, NULL,
109 sizeof
110 (struct wl12xx_disconn_template),
111 0, WL1271_RATE_AUTOMATIC);
112 if (ret < 0)
113 return ret;
114
Juuso Oikarinenbfb24c92010-03-26 12:53:31 +0200115 for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) {
116 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV, NULL,
Eliad Peller97127e62011-11-09 13:12:45 +0200117 sizeof(struct ieee80211_qos_hdr),
118 i, WL1271_RATE_AUTOMATIC);
Juuso Oikarinenbfb24c92010-03-26 12:53:31 +0200119 if (ret < 0)
120 return ret;
121 }
122
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300123 return 0;
124}
125
Eliad Peller87fbcb02011-10-05 11:55:41 +0200126static int wl1271_ap_init_deauth_template(struct wl1271 *wl,
127 struct wl12xx_vif *wlvif)
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200128{
129 struct wl12xx_disconn_template *tmpl;
130 int ret;
Eliad Pelleraf7fbb22011-09-19 13:51:42 +0300131 u32 rate;
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200132
133 tmpl = kzalloc(sizeof(*tmpl), GFP_KERNEL);
134 if (!tmpl) {
135 ret = -ENOMEM;
136 goto out;
137 }
138
139 tmpl->header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT |
140 IEEE80211_STYPE_DEAUTH);
141
Eliad Peller87fbcb02011-10-05 11:55:41 +0200142 rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200143 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_DEAUTH_AP,
Eliad Pelleraf7fbb22011-09-19 13:51:42 +0300144 tmpl, sizeof(*tmpl), 0, rate);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200145
146out:
147 kfree(tmpl);
148 return ret;
149}
150
Eliad Peller784f6942011-10-05 11:55:39 +0200151static int wl1271_ap_init_null_template(struct wl1271 *wl,
152 struct ieee80211_vif *vif)
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200153{
Eliad Peller87fbcb02011-10-05 11:55:41 +0200154 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200155 struct ieee80211_hdr_3addr *nullfunc;
156 int ret;
Eliad Pelleraf7fbb22011-09-19 13:51:42 +0300157 u32 rate;
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200158
159 nullfunc = kzalloc(sizeof(*nullfunc), GFP_KERNEL);
160 if (!nullfunc) {
161 ret = -ENOMEM;
162 goto out;
163 }
164
165 nullfunc->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
166 IEEE80211_STYPE_NULLFUNC |
167 IEEE80211_FCTL_FROMDS);
168
169 /* nullfunc->addr1 is filled by FW */
170
Eliad Peller784f6942011-10-05 11:55:39 +0200171 memcpy(nullfunc->addr2, vif->addr, ETH_ALEN);
172 memcpy(nullfunc->addr3, vif->addr, ETH_ALEN);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200173
Eliad Peller87fbcb02011-10-05 11:55:41 +0200174 rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200175 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, nullfunc,
Eliad Pelleraf7fbb22011-09-19 13:51:42 +0300176 sizeof(*nullfunc), 0, rate);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200177
178out:
179 kfree(nullfunc);
180 return ret;
181}
182
Eliad Peller784f6942011-10-05 11:55:39 +0200183static int wl1271_ap_init_qos_null_template(struct wl1271 *wl,
184 struct ieee80211_vif *vif)
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200185{
Eliad Peller87fbcb02011-10-05 11:55:41 +0200186 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200187 struct ieee80211_qos_hdr *qosnull;
188 int ret;
Eliad Pelleraf7fbb22011-09-19 13:51:42 +0300189 u32 rate;
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200190
191 qosnull = kzalloc(sizeof(*qosnull), GFP_KERNEL);
192 if (!qosnull) {
193 ret = -ENOMEM;
194 goto out;
195 }
196
197 qosnull->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
198 IEEE80211_STYPE_QOS_NULLFUNC |
199 IEEE80211_FCTL_FROMDS);
200
201 /* qosnull->addr1 is filled by FW */
202
Eliad Peller784f6942011-10-05 11:55:39 +0200203 memcpy(qosnull->addr2, vif->addr, ETH_ALEN);
204 memcpy(qosnull->addr3, vif->addr, ETH_ALEN);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200205
Eliad Peller87fbcb02011-10-05 11:55:41 +0200206 rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200207 ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, qosnull,
Eliad Pelleraf7fbb22011-09-19 13:51:42 +0300208 sizeof(*qosnull), 0, rate);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200209
210out:
211 kfree(qosnull);
212 return ret;
213}
214
Eliad Peller08c1d1c2011-08-14 13:17:04 +0300215static int wl12xx_init_rx_config(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300216{
217 int ret;
218
Juuso Oikarinen8793f9b2009-10-13 12:47:40 +0300219 ret = wl1271_acx_rx_msdu_life_time(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300220 if (ret < 0)
221 return ret;
222
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300223 return 0;
224}
225
Eliad Peller0603d892011-10-05 11:55:51 +0200226static int wl12xx_init_phy_vif_config(struct wl1271 *wl,
227 struct wl12xx_vif *wlvif)
Eliad Peller92c77c72011-10-05 11:55:40 +0200228{
229 int ret;
230
Eliad Peller0603d892011-10-05 11:55:51 +0200231 ret = wl1271_acx_slot(wl, wlvif, DEFAULT_SLOT_TIME);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300232 if (ret < 0)
233 return ret;
234
Eliad Peller0603d892011-10-05 11:55:51 +0200235 ret = wl1271_acx_service_period_timeout(wl, wlvif);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300236 if (ret < 0)
237 return ret;
238
Eliad Peller0603d892011-10-05 11:55:51 +0200239 ret = wl1271_acx_rts_threshold(wl, wlvif, wl->hw->wiphy->rts_threshold);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300240 if (ret < 0)
241 return ret;
242
243 return 0;
244}
245
Arik Nemtsova6935342011-10-24 17:25:20 +0200246static int wl1271_init_sta_beacon_filter(struct wl1271 *wl,
247 struct wl12xx_vif *wlvif)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300248{
249 int ret;
250
Arik Nemtsova6935342011-10-24 17:25:20 +0200251 ret = wl1271_acx_beacon_filter_table(wl, wlvif);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300252 if (ret < 0)
253 return ret;
254
Arik Nemtsova6935342011-10-24 17:25:20 +0200255 /* enable beacon filtering */
256 ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300257 if (ret < 0)
258 return ret;
259
260 return 0;
261}
262
Luciano Coelho12419cc2010-02-18 13:25:44 +0200263int wl1271_init_pta(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300264{
265 int ret;
266
Eliad Peller3be41122011-08-14 13:17:19 +0300267 ret = wl12xx_acx_sg_cfg(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300268 if (ret < 0)
269 return ret;
270
Juuso Oikarinen7fc3a862010-03-18 12:26:32 +0200271 ret = wl1271_acx_sg_enable(wl, wl->sg_enabled);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300272 if (ret < 0)
273 return ret;
274
275 return 0;
276}
277
Luciano Coelho12419cc2010-02-18 13:25:44 +0200278int wl1271_init_energy_detection(struct wl1271 *wl)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300279{
280 int ret;
281
282 ret = wl1271_acx_cca_threshold(wl);
283 if (ret < 0)
284 return ret;
285
286 return 0;
287}
288
Eliad Peller0603d892011-10-05 11:55:51 +0200289static int wl1271_init_beacon_broadcast(struct wl1271 *wl,
290 struct wl12xx_vif *wlvif)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300291{
292 int ret;
293
Eliad Peller0603d892011-10-05 11:55:51 +0200294 ret = wl1271_acx_bcn_dtim_options(wl, wlvif);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300295 if (ret < 0)
296 return ret;
297
298 return 0;
299}
300
Ido Yariv95dac04f2011-06-06 14:57:06 +0300301static int wl12xx_init_fwlog(struct wl1271 *wl)
302{
303 int ret;
304
305 if (wl->quirks & WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED)
306 return 0;
307
308 ret = wl12xx_cmd_config_fwlog(wl);
309 if (ret < 0)
310 return ret;
311
312 return 0;
313}
314
Eliad Peller92c77c72011-10-05 11:55:40 +0200315/* generic sta initialization (non vif-specific) */
Eliad Peller30d0c8f2011-10-05 11:55:42 +0200316static int wl1271_sta_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200317{
318 int ret;
319
Eliad Pellerc8bde242011-02-02 09:59:35 +0200320 /* PS config */
Eliad Pellerd2d66c52011-10-05 11:55:43 +0200321 ret = wl12xx_acx_config_ps(wl, wlvif);
Eliad Pellerc8bde242011-02-02 09:59:35 +0200322 if (ret < 0)
323 return ret;
324
Shahar Leviff868432011-04-11 15:41:46 +0300325 /* FM WLAN coexistence */
326 ret = wl1271_acx_fm_coex(wl);
327 if (ret < 0)
328 return ret;
329
Eliad Peller30d0c8f2011-10-05 11:55:42 +0200330 ret = wl1271_acx_sta_rate_policies(wl, wlvif);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200331 if (ret < 0)
332 return ret;
333
334 return 0;
335}
336
Eliad Peller0603d892011-10-05 11:55:51 +0200337static int wl1271_sta_hw_init_post_mem(struct wl1271 *wl,
338 struct ieee80211_vif *vif)
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200339{
Eliad Peller0603d892011-10-05 11:55:51 +0200340 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200341 int ret, i;
342
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200343 /* disable all keep-alive templates */
344 for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) {
Eliad Peller0603d892011-10-05 11:55:51 +0200345 ret = wl1271_acx_keep_alive_config(wl, wlvif, i,
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200346 ACX_KEEP_ALIVE_TPL_INVALID);
347 if (ret < 0)
348 return ret;
349 }
350
351 /* disable the keep-alive feature */
Eliad Peller0603d892011-10-05 11:55:51 +0200352 ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200353 if (ret < 0)
354 return ret;
355
356 return 0;
357}
358
Eliad Peller92c77c72011-10-05 11:55:40 +0200359/* generic ap initialization (non vif-specific) */
Eliad Peller87fbcb02011-10-05 11:55:41 +0200360static int wl1271_ap_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200361{
Arik Nemtsov70f47422011-04-18 14:15:25 +0300362 int ret;
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200363
Eliad Peller87fbcb02011-10-05 11:55:41 +0200364 ret = wl1271_init_ap_rates(wl, wlvif);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200365 if (ret < 0)
366 return ret;
367
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200368 return 0;
369}
370
Eliad Peller784f6942011-10-05 11:55:39 +0200371int wl1271_ap_init_templates(struct wl1271 *wl, struct ieee80211_vif *vif)
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200372{
Eliad Peller87fbcb02011-10-05 11:55:41 +0200373 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200374 int ret;
375
Eliad Peller87fbcb02011-10-05 11:55:41 +0200376 ret = wl1271_ap_init_deauth_template(wl, wlvif);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200377 if (ret < 0)
378 return ret;
379
Eliad Peller784f6942011-10-05 11:55:39 +0200380 ret = wl1271_ap_init_null_template(wl, vif);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200381 if (ret < 0)
382 return ret;
383
Eliad Peller784f6942011-10-05 11:55:39 +0200384 ret = wl1271_ap_init_qos_null_template(wl, vif);
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200385 if (ret < 0)
386 return ret;
387
Arik Nemtsov521a4a22011-04-18 14:15:22 +0300388 /*
389 * when operating as AP we want to receive external beacons for
390 * configuring ERP protection.
391 */
Eliad Peller0603d892011-10-05 11:55:51 +0200392 ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
Arik Nemtsov521a4a22011-04-18 14:15:22 +0300393 if (ret < 0)
394 return ret;
395
Arik Nemtsove0fe3712010-10-16 18:19:53 +0200396 return 0;
397}
398
Eliad Peller784f6942011-10-05 11:55:39 +0200399static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl,
400 struct ieee80211_vif *vif)
Arik Nemtsovc45a85b2011-04-18 14:15:26 +0300401{
Eliad Peller784f6942011-10-05 11:55:39 +0200402 return wl1271_ap_init_templates(wl, vif);
Arik Nemtsovc45a85b2011-04-18 14:15:26 +0300403}
404
Eliad Peller87fbcb02011-10-05 11:55:41 +0200405int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Arik Nemtsov70f47422011-04-18 14:15:25 +0300406{
407 int i, ret;
408 struct conf_tx_rate_class rc;
409 u32 supported_rates;
410
Eliad Peller87fbcb02011-10-05 11:55:41 +0200411 wl1271_debug(DEBUG_AP, "AP basic rate set: 0x%x",
412 wlvif->basic_rate_set);
Arik Nemtsov70f47422011-04-18 14:15:25 +0300413
Eliad Peller87fbcb02011-10-05 11:55:41 +0200414 if (wlvif->basic_rate_set == 0)
Arik Nemtsov70f47422011-04-18 14:15:25 +0300415 return -EINVAL;
416
Eliad Peller87fbcb02011-10-05 11:55:41 +0200417 rc.enabled_rates = wlvif->basic_rate_set;
Arik Nemtsov70f47422011-04-18 14:15:25 +0300418 rc.long_retry_limit = 10;
419 rc.short_retry_limit = 10;
420 rc.aflags = 0;
Eliad Pellere5a359f2011-10-10 10:13:15 +0200421 ret = wl1271_acx_ap_rate_policy(wl, &rc, wlvif->ap.mgmt_rate_idx);
Arik Nemtsov70f47422011-04-18 14:15:25 +0300422 if (ret < 0)
423 return ret;
424
425 /* use the min basic rate for AP broadcast/multicast */
Eliad Peller87fbcb02011-10-05 11:55:41 +0200426 rc.enabled_rates = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
Arik Nemtsov70f47422011-04-18 14:15:25 +0300427 rc.short_retry_limit = 10;
428 rc.long_retry_limit = 10;
429 rc.aflags = 0;
Eliad Pellere5a359f2011-10-10 10:13:15 +0200430 ret = wl1271_acx_ap_rate_policy(wl, &rc, wlvif->ap.bcast_rate_idx);
Arik Nemtsov70f47422011-04-18 14:15:25 +0300431 if (ret < 0)
432 return ret;
433
434 /*
435 * If the basic rates contain OFDM rates, use OFDM only
436 * rates for unicast TX as well. Else use all supported rates.
437 */
Eliad Peller87fbcb02011-10-05 11:55:41 +0200438 if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES))
Arik Nemtsov70f47422011-04-18 14:15:25 +0300439 supported_rates = CONF_TX_OFDM_RATES;
440 else
441 supported_rates = CONF_TX_AP_ENABLED_RATES;
442
Arik Nemtsov1a8adb62011-08-14 13:17:29 +0300443 /* unconditionally enable HT rates */
444 supported_rates |= CONF_TX_MCS_RATES;
445
Arik Nemtsov70f47422011-04-18 14:15:25 +0300446 /* configure unicast TX rate classes */
447 for (i = 0; i < wl->conf.tx.ac_conf_count; i++) {
448 rc.enabled_rates = supported_rates;
449 rc.short_retry_limit = 10;
450 rc.long_retry_limit = 10;
451 rc.aflags = 0;
Eliad Pellere5a359f2011-10-10 10:13:15 +0200452 ret = wl1271_acx_ap_rate_policy(wl, &rc,
453 wlvif->ap.ucast_rate_idx[i]);
Arik Nemtsov70f47422011-04-18 14:15:25 +0300454 if (ret < 0)
455 return ret;
456 }
457
458 return 0;
459}
460
Eliad Peller536129c2011-10-05 11:55:45 +0200461static int wl1271_set_ba_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100462{
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100463 /* Reset the BA RX indicators */
Eliad Pellerd0802ab2011-10-05 11:56:04 +0200464 wlvif->ba_allowed = true;
Arik Nemtsov0f9c8252011-08-17 10:45:49 +0300465 wl->ba_rx_session_count = 0;
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100466
Arik Nemtsov0f9c8252011-08-17 10:45:49 +0300467 /* BA is supported in STA/AP modes */
Eliad Peller536129c2011-10-05 11:55:45 +0200468 if (wlvif->bss_type != BSS_TYPE_AP_BSS &&
469 wlvif->bss_type != BSS_TYPE_STA_BSS) {
Eliad Pellerd0802ab2011-10-05 11:56:04 +0200470 wlvif->ba_support = false;
Arik Nemtsov0f9c8252011-08-17 10:45:49 +0300471 return 0;
472 }
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100473
Eliad Pellerd0802ab2011-10-05 11:56:04 +0200474 wlvif->ba_support = true;
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100475
Arik Nemtsov0f9c8252011-08-17 10:45:49 +0300476 /* 802.11n initiator BA session setting */
Eliad Peller0603d892011-10-05 11:55:51 +0200477 return wl12xx_acx_set_ba_initiator_policy(wl, wlvif);
Levi, Shahar4b7fac72011-01-23 07:27:22 +0100478}
479
Shahar Levi48a61472011-03-06 16:32:08 +0200480int wl1271_chip_specific_init(struct wl1271 *wl)
481{
482 int ret = 0;
483
484 if (wl->chip.id == CHIP_ID_1283_PG20) {
485 u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE;
486
Luciano Coelhoce39def2011-11-03 08:44:41 +0200487 if (!(wl->quirks & WL12XX_QUIRK_NO_BLOCKSIZE_ALIGNMENT))
Shahar Levi48a61472011-03-06 16:32:08 +0200488 /* Enable SDIO padding */
489 host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK;
490
491 /* Must be before wl1271_acx_init_mem_config() */
492 ret = wl1271_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap);
493 if (ret < 0)
494 goto out;
495 }
496out:
497 return ret;
498}
499
Eliad Peller92c77c72011-10-05 11:55:40 +0200500/* vif-specifc initialization */
Eliad Peller0603d892011-10-05 11:55:51 +0200501static int wl12xx_init_sta_role(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Eliad Peller92c77c72011-10-05 11:55:40 +0200502{
503 int ret;
Shahar Levi48a61472011-03-06 16:32:08 +0200504
Eliad Peller0603d892011-10-05 11:55:51 +0200505 ret = wl1271_acx_group_address_tbl(wl, wlvif, true, NULL, 0);
Eliad Peller92c77c72011-10-05 11:55:40 +0200506 if (ret < 0)
507 return ret;
508
509 /* Initialize connection monitoring thresholds */
Eliad Peller0603d892011-10-05 11:55:51 +0200510 ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
Eliad Peller92c77c72011-10-05 11:55:40 +0200511 if (ret < 0)
512 return ret;
513
514 /* Beacon filtering */
Arik Nemtsova6935342011-10-24 17:25:20 +0200515 ret = wl1271_init_sta_beacon_filter(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200516 if (ret < 0)
517 return ret;
518
519 /* Beacons and broadcast settings */
Eliad Peller0603d892011-10-05 11:55:51 +0200520 ret = wl1271_init_beacon_broadcast(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200521 if (ret < 0)
522 return ret;
523
524 /* Configure rssi/snr averaging weights */
Eliad Peller0603d892011-10-05 11:55:51 +0200525 ret = wl1271_acx_rssi_snr_avg_weights(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200526 if (ret < 0)
527 return ret;
528
529 return 0;
530}
531
532/* vif-specific intialization */
Eliad Peller0603d892011-10-05 11:55:51 +0200533static int wl12xx_init_ap_role(struct wl1271 *wl, struct wl12xx_vif *wlvif)
Eliad Peller92c77c72011-10-05 11:55:40 +0200534{
535 int ret;
536
Eliad Peller0603d892011-10-05 11:55:51 +0200537 ret = wl1271_acx_ap_max_tx_retry(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200538 if (ret < 0)
539 return ret;
540
541 /* initialize Tx power */
Eliad Peller6bd65022011-10-10 10:13:11 +0200542 ret = wl1271_acx_tx_power(wl, wlvif, wlvif->power_level);
Eliad Peller92c77c72011-10-05 11:55:40 +0200543 if (ret < 0)
544 return ret;
545
546 return 0;
547}
548
549int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif)
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300550{
Eliad Peller87fbcb02011-10-05 11:55:41 +0200551 struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
Kalle Valo243eeb52010-02-18 13:25:39 +0200552 struct conf_tx_ac_category *conf_ac;
Kalle Valof2054df2010-02-18 13:25:40 +0200553 struct conf_tx_tid *conf_tid;
Eliad Peller536129c2011-10-05 11:55:45 +0200554 bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
Eliad Peller92c77c72011-10-05 11:55:40 +0200555 int ret, i;
556
Eliad Pellera4e41302011-10-11 11:49:15 +0200557 /*
558 * consider all existing roles before configuring psm.
559 * TODO: reconfigure on interface removal.
560 */
561 if (!wl->ap_count) {
562 if (is_ap) {
563 /* Configure for power always on */
564 ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
565 if (ret < 0)
566 return ret;
567 } else if (!wl->sta_count) {
568 /* Configure for ELP power saving */
569 ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
570 if (ret < 0)
571 return ret;
572 }
573 }
574
Eliad Peller92c77c72011-10-05 11:55:40 +0200575 /* Mode specific init */
576 if (is_ap) {
Eliad Peller87fbcb02011-10-05 11:55:41 +0200577 ret = wl1271_ap_hw_init(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200578 if (ret < 0)
579 return ret;
580
Eliad Peller0603d892011-10-05 11:55:51 +0200581 ret = wl12xx_init_ap_role(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200582 if (ret < 0)
583 return ret;
584 } else {
Eliad Peller30d0c8f2011-10-05 11:55:42 +0200585 ret = wl1271_sta_hw_init(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200586 if (ret < 0)
587 return ret;
588
Eliad Peller0603d892011-10-05 11:55:51 +0200589 ret = wl12xx_init_sta_role(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200590 if (ret < 0)
591 return ret;
592 }
593
Eliad Peller0603d892011-10-05 11:55:51 +0200594 wl12xx_init_phy_vif_config(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200595
596 /* Default TID/AC configuration */
597 BUG_ON(wl->conf.tx.tid_conf_count != wl->conf.tx.ac_conf_count);
598 for (i = 0; i < wl->conf.tx.tid_conf_count; i++) {
599 conf_ac = &wl->conf.tx.ac_conf[i];
Eliad Peller0603d892011-10-05 11:55:51 +0200600 ret = wl1271_acx_ac_cfg(wl, wlvif, conf_ac->ac,
601 conf_ac->cw_min, conf_ac->cw_max,
602 conf_ac->aifsn, conf_ac->tx_op_limit);
Eliad Peller92c77c72011-10-05 11:55:40 +0200603 if (ret < 0)
604 return ret;
605
606 conf_tid = &wl->conf.tx.tid_conf[i];
Eliad Peller0603d892011-10-05 11:55:51 +0200607 ret = wl1271_acx_tid_cfg(wl, wlvif,
Eliad Peller92c77c72011-10-05 11:55:40 +0200608 conf_tid->queue_id,
609 conf_tid->channel_type,
610 conf_tid->tsid,
611 conf_tid->ps_scheme,
612 conf_tid->ack_policy,
613 conf_tid->apsd_conf[0],
614 conf_tid->apsd_conf[1]);
615 if (ret < 0)
616 return ret;
617 }
618
619 /* Configure HW encryption */
Eliad Peller0603d892011-10-05 11:55:51 +0200620 ret = wl1271_acx_feature_cfg(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200621 if (ret < 0)
622 return ret;
623
624 /* Mode specific init - post mem init */
625 if (is_ap)
626 ret = wl1271_ap_hw_init_post_mem(wl, vif);
627 else
Eliad Peller0603d892011-10-05 11:55:51 +0200628 ret = wl1271_sta_hw_init_post_mem(wl, vif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200629
630 if (ret < 0)
631 return ret;
632
633 /* Configure initiator BA sessions policies */
Eliad Peller536129c2011-10-05 11:55:45 +0200634 ret = wl1271_set_ba_policies(wl, wlvif);
Eliad Peller92c77c72011-10-05 11:55:40 +0200635 if (ret < 0)
636 return ret;
637
638 return 0;
639}
640
641int wl1271_hw_init(struct wl1271 *wl)
642{
643 int ret;
644
Luciano Coelho2c8f82e2011-12-13 11:39:50 +0200645 if (wl->chip.id == CHIP_ID_1283_PG20) {
Shahar Levi49d750ca2011-03-06 16:32:09 +0200646 ret = wl128x_cmd_general_parms(wl);
Luciano Coelho2c8f82e2011-12-13 11:39:50 +0200647 if (ret < 0)
648 return ret;
Shahar Levi49d750ca2011-03-06 16:32:09 +0200649 ret = wl128x_cmd_radio_parms(wl);
Luciano Coelho2c8f82e2011-12-13 11:39:50 +0200650 if (ret < 0)
651 return ret;
652 } else {
653 ret = wl1271_cmd_general_parms(wl);
654 if (ret < 0)
655 return ret;
Shahar Levi49d750ca2011-03-06 16:32:09 +0200656 ret = wl1271_cmd_radio_parms(wl);
Luciano Coelho2c8f82e2011-12-13 11:39:50 +0200657 if (ret < 0)
658 return ret;
659 ret = wl1271_cmd_ext_radio_parms(wl);
660 if (ret < 0)
661 return ret;
662 }
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300663
Shahar Levi48a61472011-03-06 16:32:08 +0200664 /* Chip-specific init */
665 ret = wl1271_chip_specific_init(wl);
666 if (ret < 0)
667 return ret;
668
Eliad Peller92c77c72011-10-05 11:55:40 +0200669 /* Init templates */
670 ret = wl1271_init_templates_config(wl);
671 if (ret < 0)
672 return ret;
Juuso Oikarinen644a4862010-10-05 13:11:56 +0200673
Eliad Peller92c77c72011-10-05 11:55:40 +0200674 ret = wl12xx_acx_mem_cfg(wl);
675 if (ret < 0)
676 return ret;
677
678 /* Configure the FW logger */
679 ret = wl12xx_init_fwlog(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300680 if (ret < 0)
681 return ret;
682
Arik Nemtsov801f8702011-04-18 14:15:20 +0300683 /* Bluetooth WLAN coexistence */
684 ret = wl1271_init_pta(wl);
685 if (ret < 0)
686 return ret;
687
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300688 /* Default memory configuration */
689 ret = wl1271_acx_init_mem_config(wl);
690 if (ret < 0)
691 return ret;
692
693 /* RX config */
Eliad Peller08c1d1c2011-08-14 13:17:04 +0300694 ret = wl12xx_init_rx_config(wl);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300695 if (ret < 0)
696 goto out_free_memmap;
697
Luciano Coelho6e92b412009-12-11 15:40:50 +0200698 ret = wl1271_acx_dco_itrim_params(wl);
699 if (ret < 0)
700 goto out_free_memmap;
701
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300702 /* Configure TX patch complete interrupt behavior */
703 ret = wl1271_acx_tx_config_options(wl);
704 if (ret < 0)
705 goto out_free_memmap;
706
707 /* RX complete interrupt pacing */
708 ret = wl1271_acx_init_rx_interrupt(wl);
709 if (ret < 0)
710 goto out_free_memmap;
711
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300712 /* Energy detection */
713 ret = wl1271_init_energy_detection(wl);
714 if (ret < 0)
715 goto out_free_memmap;
716
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300717 /* Default fragmentation threshold */
Arik Nemtsov5f704d12011-04-18 14:15:21 +0300718 ret = wl1271_acx_frag_threshold(wl, wl->hw->wiphy->frag_threshold);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300719 if (ret < 0)
720 goto out_free_memmap;
721
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300722 /* Enable data path */
Luciano Coelho94210892009-12-11 15:40:55 +0200723 ret = wl1271_cmd_data_path(wl, 1);
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300724 if (ret < 0)
725 goto out_free_memmap;
726
Juuso Oikarinen38ad2d82009-12-11 15:41:08 +0200727 /* configure PM */
728 ret = wl1271_acx_pm_config(wl);
729 if (ret < 0)
730 goto out_free_memmap;
731
Eliad Pellerfa6ad9f2011-08-14 13:17:14 +0300732 ret = wl12xx_acx_set_rate_mgmt_params(wl);
733 if (ret < 0)
734 goto out_free_memmap;
735
Eliad Peller94877752011-08-28 15:11:56 +0300736 /* configure hangover */
737 ret = wl12xx_acx_config_hangover(wl);
738 if (ret < 0)
739 goto out_free_memmap;
740
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300741 return 0;
742
743 out_free_memmap:
744 kfree(wl->target_mem_map);
Juuso Oikarinen34415232009-10-08 21:56:33 +0300745 wl->target_mem_map = NULL;
Luciano Coelhof5fc0f82009-08-06 16:25:28 +0300746
747 return ret;
748}