staging: brcm80211: purge epivers.h
[deliverable/linux.git] / drivers / staging / brcm80211 / sys / wlc_mac80211.c
CommitLineData
a9533e7e
HP
1/*
2 * Copyright (c) 2010 Broadcom Corporation
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 */
3327989a 16#include <linux/kernel.h>
a1c16ed2 17#include <linux/ctype.h>
a9533e7e 18#include <bcmdefs.h>
a52ba66c 19#include <bcmdevs.h>
a1c16ed2 20#include <wlc_cfg.h>
a9533e7e
HP
21#include <osl.h>
22#include <bcmutils.h>
23#include <bcmwifi.h>
24#include <siutils.h>
25#include <bcmendian.h>
a9533e7e 26#include <proto/wpa.h>
a9533e7e
HP
27#include <pcicfg.h>
28#include <bcmsrom.h>
29#include <wlioctl.h>
a52ba66c 30#include <sbhndpio.h>
a9533e7e
HP
31#include <sbhnddma.h>
32#include <hnddma.h>
33#include <hndpmu.h>
34#include <d11.h>
35#include <wlc_rate.h>
36#include <wlc_pub.h>
37#include <wlc_key.h>
38#include <wlc_bsscfg.h>
39#include <wlc_channel.h>
40#include <wlc_mac80211.h>
41#include <wlc_bmac.h>
42#include <wlc_scb.h>
43#include <wlc_phy_hal.h>
44#include <wlc_phy_shim.h>
45#include <wlc_antsel.h>
46#include <wlc_stf.h>
47#include <wlc_ampdu.h>
48#include <wlc_event.h>
49#include <wl_export.h>
50#ifdef BCMSDIO
51#include <bcmsdh.h>
52#else
53#include "d11ucode_ext.h"
54#endif
55#ifdef WLC_HIGH_ONLY
56#include <bcm_rpc_tp.h>
57#include <bcm_rpc.h>
58#include <bcm_xdr.h>
59#include <wlc_rpc.h>
60#include <wlc_rpctx.h>
61#endif /* WLC_HIGH_ONLY */
62#include <wlc_alloc.h>
63#include <net/mac80211.h>
64
65#ifdef WLC_HIGH_ONLY
66#undef R_REG
67#undef W_REG
68#define R_REG(osh, r) RPC_READ_REG(osh, r)
69#define W_REG(osh, r, v) RPC_WRITE_REG(osh, r, v)
70#endif
71
72/*
73 * buffer length needed for wlc_format_ssid
74 * 32 SSID chars, max of 4 chars for each SSID char "\xFF", plus NULL.
75 */
76#define SSID_FMT_BUF_LEN ((4 * DOT11_MAX_SSID_LEN) + 1)
77
78#define TIMER_INTERVAL_WATCHDOG 1000 /* watchdog timer, in unit of ms */
79#define TIMER_INTERVAL_RADIOCHK 800 /* radio monitor timer, in unit of ms */
80
81#ifndef WLC_MPC_MAX_DELAYCNT
82#define WLC_MPC_MAX_DELAYCNT 10 /* Max MPC timeout, in unit of watchdog */
83#endif
84#define WLC_MPC_MIN_DELAYCNT 1 /* Min MPC timeout, in unit of watchdog */
85#define WLC_MPC_THRESHOLD 3 /* MPC count threshold level */
86
87#define BEACON_INTERVAL_DEFAULT 100 /* beacon interval, in unit of 1024TU */
88#define DTIM_INTERVAL_DEFAULT 3 /* DTIM interval, in unit of beacon interval */
89
90/* Scale down delays to accommodate QT slow speed */
91#define BEACON_INTERVAL_DEF_QT 20 /* beacon interval, in unit of 1024TU */
92#define DTIM_INTERVAL_DEF_QT 1 /* DTIM interval, in unit of beacon interval */
93
94#define TBTT_ALIGN_LEEWAY_US 100 /* min leeway before first TBTT in us */
95
96/*
97 * driver maintains internal 'tick'(wlc->pub->now) which increments in 1s OS timer(soft
98 * watchdog) it is not a wall clock and won't increment when driver is in "down" state
99 * this low resolution driver tick can be used for maintenance tasks such as phy
100 * calibration and scb update
101 */
102
103/* watchdog trigger mode: OSL timer or TBTT */
104#define WLC_WATCHDOG_TBTT(wlc) \
105 (wlc->stas_associated > 0 && wlc->PM != PM_OFF && wlc->pub->align_wd_tbtt)
106
107/* To inform the ucode of the last mcast frame posted so that it can clear moredata bit */
108#define BCMCFID(wlc, fid) wlc_bmac_write_shm((wlc)->hw, M_BCMC_FID, (fid))
109
110#ifndef WLC_HIGH_ONLY
111#define WLC_WAR16165(wlc) (BUSTYPE(wlc->pub->sih->bustype) == PCI_BUS && \
112 (!AP_ENAB(wlc->pub)) && (wlc->war16165))
113#else
0965ae88 114#define WLC_WAR16165(wlc) (false)
a9533e7e
HP
115#endif /* WLC_HIGH_ONLY */
116
117/* debug/trace */
118uint wl_msg_level =
119#if defined(BCMDBG)
120 WL_ERROR_VAL;
121#else
122 0;
123#endif /* BCMDBG */
124
125/* Find basic rate for a given rate */
126#define WLC_BASIC_RATE(wlc, rspec) (IS_MCS(rspec) ? \
127 (wlc)->band->basic_rate[mcs_table[rspec & RSPEC_RATE_MASK].leg_ofdm] : \
128 (wlc)->band->basic_rate[rspec & RSPEC_RATE_MASK])
129
130#define FRAMETYPE(r, mimoframe) (IS_MCS(r) ? mimoframe : (IS_CCK(r) ? FT_CCK : FT_OFDM))
131
132#define RFDISABLE_DEFAULT 10000000 /* rfdisable delay timer 500 ms, runs of ALP clock */
133
134#define WLC_TEMPSENSE_PERIOD 10 /* 10 second timeout */
135
136#define SCAN_IN_PROGRESS(x) 0
137
4766ae6c
BR
138#define EPI_VERSION_NUM 0x054b0b00
139
a9533e7e
HP
140#ifdef BCMDBG
141/* pointer to most recently allocated wl/wlc */
142static wlc_info_t *wlc_info_dbg = (wlc_info_t *) (NULL);
143#endif
144
a9533e7e
HP
145/* IOVar table */
146
147/* Parameter IDs, for use only internally to wlc -- in the wlc_iovars
148 * table and by the wlc_doiovar() function. No ordering is imposed:
149 * the table is keyed by name, and the function uses a switch.
150 */
151enum {
152 IOV_MPC = 1,
153 IOV_QTXPOWER,
154 IOV_BCN_LI_BCN, /* Beacon listen interval in # of beacons */
155 IOV_LAST /* In case of a need to check max ID number */
156};
157
158const bcm_iovar_t wlc_iovars[] = {
159 {"mpc", IOV_MPC, (IOVF_OPEN_ALLOW), IOVT_BOOL, 0},
160 {"qtxpower", IOV_QTXPOWER, (IOVF_WHL | IOVF_OPEN_ALLOW), IOVT_UINT32,
161 0},
162 {"bcn_li_bcn", IOV_BCN_LI_BCN, 0, IOVT_UINT8, 0},
163 {NULL, 0, 0, 0, 0}
164};
165
41feb5ed 166const u8 prio2fifo[NUMPRIO] = {
a9533e7e
HP
167 TX_AC_BE_FIFO, /* 0 BE AC_BE Best Effort */
168 TX_AC_BK_FIFO, /* 1 BK AC_BK Background */
169 TX_AC_BK_FIFO, /* 2 -- AC_BK Background */
170 TX_AC_BE_FIFO, /* 3 EE AC_BE Best Effort */
171 TX_AC_VI_FIFO, /* 4 CL AC_VI Video */
172 TX_AC_VI_FIFO, /* 5 VI AC_VI Video */
173 TX_AC_VO_FIFO, /* 6 VO AC_VO Voice */
174 TX_AC_VO_FIFO /* 7 NC AC_VO Voice */
175};
176
177/* precedences numbers for wlc queues. These are twice as may levels as
178 * 802.1D priorities.
179 * Odd numbers are used for HI priority traffic at same precedence levels
180 * These constants are used ONLY by wlc_prio2prec_map. Do not use them elsewhere.
181 */
182#define _WLC_PREC_NONE 0 /* None = - */
183#define _WLC_PREC_BK 2 /* BK - Background */
184#define _WLC_PREC_BE 4 /* BE - Best-effort */
185#define _WLC_PREC_EE 6 /* EE - Excellent-effort */
186#define _WLC_PREC_CL 8 /* CL - Controlled Load */
187#define _WLC_PREC_VI 10 /* Vi - Video */
188#define _WLC_PREC_VO 12 /* Vo - Voice */
189#define _WLC_PREC_NC 14 /* NC - Network Control */
190
191/* 802.1D Priority to precedence queue mapping */
41feb5ed 192const u8 wlc_prio2prec_map[] = {
a9533e7e
HP
193 _WLC_PREC_BE, /* 0 BE - Best-effort */
194 _WLC_PREC_BK, /* 1 BK - Background */
195 _WLC_PREC_NONE, /* 2 None = - */
196 _WLC_PREC_EE, /* 3 EE - Excellent-effort */
197 _WLC_PREC_CL, /* 4 CL - Controlled Load */
198 _WLC_PREC_VI, /* 5 Vi - Video */
199 _WLC_PREC_VO, /* 6 Vo - Voice */
200 _WLC_PREC_NC, /* 7 NC - Network Control */
201};
202
203/* Sanity check for tx_prec_map and fifo synchup
204 * Either there are some packets pending for the fifo, else if fifo is empty then
205 * all the corresponding precmap bits should be set
206 */
207#define WLC_TX_FIFO_CHECK(wlc, fifo) (TXPKTPENDGET((wlc), (fifo)) || \
208 (TXPKTPENDGET((wlc), (fifo)) == 0 && \
209 ((wlc)->tx_prec_map & (wlc)->fifo2prec_map[(fifo)]) == \
210 (wlc)->fifo2prec_map[(fifo)]))
211
212/* TX FIFO number to WME/802.1E Access Category */
41feb5ed 213const u8 wme_fifo2ac[] = { AC_BK, AC_BE, AC_VI, AC_VO, AC_BE, AC_BE };
a9533e7e
HP
214
215/* WME/802.1E Access Category to TX FIFO number */
41feb5ed 216static const u8 wme_ac2fifo[] = { 1, 0, 2, 3 };
a9533e7e 217
0965ae88 218static bool in_send_q = false;
a9533e7e
HP
219
220/* Shared memory location index for various AC params */
221#define wme_shmemacindex(ac) wme_ac2fifo[ac]
222
223#ifdef BCMDBG
e5c4536f
JC
224static const char *fifo_names[] = {
225 "AC_BK", "AC_BE", "AC_VI", "AC_VO", "BCMC", "ATIM" };
a9533e7e
HP
226const char *aci_names[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
227#endif
228
41feb5ed 229static const u8 acbitmap2maxprio[] = {
a9533e7e
HP
230 PRIO_8021D_BE, PRIO_8021D_BE, PRIO_8021D_BK, PRIO_8021D_BK,
231 PRIO_8021D_VI, PRIO_8021D_VI, PRIO_8021D_VI, PRIO_8021D_VI,
232 PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO,
233 PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO
234};
235
236/* currently the best mechanism for determining SIFS is the band in use */
237#define SIFS(band) ((band)->bandtype == WLC_BAND_5G ? APHY_SIFS_TIME : BPHY_SIFS_TIME);
238
239/* value for # replay counters currently supported */
240#define WLC_REPLAY_CNTRS_VALUE WPA_CAP_16_REPLAY_CNTRS
241
242/* local prototypes */
243extern void wlc_txq_enq(void *ctx, struct scb *scb, void *sdu, uint prec);
7d4df48e 244static u16 BCMFASTPATH wlc_d11hdrs_mac80211(wlc_info_t *wlc,
a9533e7e
HP
245 struct ieee80211_hw *hw, void *p,
246 struct scb *scb, uint frag,
247 uint nfrags, uint queue,
248 uint next_frag_len,
7cc4a4c0 249 wsec_key_t *key,
a9533e7e 250 ratespec_t rspec_override);
7cc4a4c0
JC
251bool wlc_sendpkt_mac80211(wlc_info_t *wlc, void *sdu, struct ieee80211_hw *hw);
252void wlc_wme_setparams(wlc_info_t *wlc, u16 aci, void *arg, bool suspend);
253static void wlc_bss_default_init(wlc_info_t *wlc);
254static void wlc_ucode_mac_upd(wlc_info_t *wlc);
255static ratespec_t mac80211_wlc_set_nrate(wlc_info_t *wlc, wlcband_t *cur_band,
66cbd3ab 256 u32 int_val);
7cc4a4c0 257static void wlc_tx_prec_map_init(wlc_info_t *wlc);
a9533e7e
HP
258static void wlc_watchdog(void *arg);
259static void wlc_watchdog_by_timer(void *arg);
7cc4a4c0 260static int wlc_set_rateset(wlc_info_t *wlc, wlc_rateset_t *rs_arg);
66cbd3ab 261static int wlc_iovar_rangecheck(wlc_info_t *wlc, u32 val,
7cc4a4c0 262 const bcm_iovar_t *vi);
41feb5ed 263static u8 wlc_local_constraint_qdbm(wlc_info_t *wlc);
a9533e7e 264
a9533e7e 265/* send and receive */
7cc4a4c0
JC
266static wlc_txq_info_t *wlc_txq_alloc(wlc_info_t *wlc, osl_t *osh);
267static void wlc_txq_free(wlc_info_t *wlc, osl_t *osh, wlc_txq_info_t *qi);
268static void wlc_txflowcontrol_signal(wlc_info_t *wlc, wlc_txq_info_t *qi,
a9533e7e 269 bool on, int prio);
7cc4a4c0 270static void wlc_txflowcontrol_reset(wlc_info_t *wlc);
7d4df48e 271static u16 wlc_compute_airtime(wlc_info_t *wlc, ratespec_t rspec,
a9533e7e 272 uint length);
41feb5ed
GKH
273static void wlc_compute_cck_plcp(ratespec_t rate, uint length, u8 *plcp);
274static void wlc_compute_ofdm_plcp(ratespec_t rate, uint length, u8 *plcp);
275static void wlc_compute_mimo_plcp(ratespec_t rate, uint length, u8 *plcp);
7d4df48e 276static u16 wlc_compute_frame_dur(wlc_info_t *wlc, ratespec_t rate,
41feb5ed 277 u8 preamble_type, uint next_frag_len);
7cc4a4c0 278static void wlc_recvctl(wlc_info_t *wlc, osl_t *osh, d11rxhdr_t *rxh,
a9533e7e 279 void *p);
7cc4a4c0 280static uint wlc_calc_frame_len(wlc_info_t *wlc, ratespec_t rate,
41feb5ed 281 u8 preamble_type, uint dur);
7cc4a4c0 282static uint wlc_calc_ack_time(wlc_info_t *wlc, ratespec_t rate,
41feb5ed 283 u8 preamble_type);
7cc4a4c0 284static uint wlc_calc_cts_time(wlc_info_t *wlc, ratespec_t rate,
41feb5ed 285 u8 preamble_type);
a9533e7e 286/* interrupt, up/down, band */
7cc4a4c0
JC
287static void wlc_setband(wlc_info_t *wlc, uint bandunit);
288static chanspec_t wlc_init_chanspec(wlc_info_t *wlc);
289static void wlc_bandinit_ordered(wlc_info_t *wlc, chanspec_t chanspec);
290static void wlc_bsinit(wlc_info_t *wlc);
291static int wlc_duty_cycle_set(wlc_info_t *wlc, int duty_cycle, bool isOFDM,
a9533e7e 292 bool writeToShm);
7cc4a4c0
JC
293static void wlc_radio_hwdisable_upd(wlc_info_t *wlc);
294static bool wlc_radio_monitor_start(wlc_info_t *wlc);
a9533e7e 295static void wlc_radio_timer(void *arg);
7cc4a4c0
JC
296static void wlc_radio_enable(wlc_info_t *wlc);
297static void wlc_radio_upd(wlc_info_t *wlc);
a9533e7e
HP
298
299/* scan, association, BSS */
7cc4a4c0 300static uint wlc_calc_ba_time(wlc_info_t *wlc, ratespec_t rate,
41feb5ed
GKH
301 u8 preamble_type);
302static void wlc_update_mimo_band_bwcap(wlc_info_t *wlc, u8 bwcap);
7cc4a4c0 303static void wlc_ht_update_sgi_rx(wlc_info_t *wlc, int val);
41feb5ed 304void wlc_ht_mimops_cap_update(wlc_info_t *wlc, u8 mimops_mode);
562c8850 305static void wlc_ht_update_ldpc(wlc_info_t *wlc, s8 val);
7cc4a4c0 306static void wlc_war16165(wlc_info_t *wlc, bool tx);
a9533e7e
HP
307
308static void wlc_process_eventq(void *arg);
7cc4a4c0
JC
309static void wlc_wme_retries_write(wlc_info_t *wlc);
310static bool wlc_attach_stf_ant_init(wlc_info_t *wlc);
311static uint wlc_attach_module(wlc_info_t *wlc);
312static void wlc_detach_module(wlc_info_t *wlc);
313static void wlc_timers_deinit(wlc_info_t *wlc);
314static void wlc_down_led_upd(wlc_info_t *wlc);
315static uint wlc_down_del_timer(wlc_info_t *wlc);
316static void wlc_ofdm_rateset_war(wlc_info_t *wlc);
317static int _wlc_ioctl(wlc_info_t *wlc, int cmd, void *arg, int len,
a9533e7e 318 struct wlc_if *wlcif);
a9533e7e
HP
319
320#if defined(BCMDBG)
7cc4a4c0 321void wlc_get_rcmta(wlc_info_t *wlc, int idx, struct ether_addr *addr)
a9533e7e
HP
322{
323 d11regs_t *regs = wlc->regs;
66cbd3ab 324 u32 v32;
a9533e7e
HP
325 osl_t *osh;
326
327 WL_TRACE(("wl%d: %s\n", WLCWLUNIT(wlc), __func__));
328
329 ASSERT(wlc->pub->corerev > 4);
330
331 osh = wlc->osh;
332
333 W_REG(osh, &regs->objaddr, (OBJADDR_RCMTA_SEL | (idx * 2)));
334 (void)R_REG(osh, &regs->objaddr);
335 v32 = R_REG(osh, &regs->objdata);
41feb5ed
GKH
336 addr->octet[0] = (u8) v32;
337 addr->octet[1] = (u8) (v32 >> 8);
338 addr->octet[2] = (u8) (v32 >> 16);
339 addr->octet[3] = (u8) (v32 >> 24);
a9533e7e
HP
340 W_REG(osh, &regs->objaddr, (OBJADDR_RCMTA_SEL | ((idx * 2) + 1)));
341 (void)R_REG(osh, &regs->objaddr);
e88cf8eb 342 v32 = R_REG(osh, (volatile u16 *)&regs->objdata);
41feb5ed
GKH
343 addr->octet[4] = (u8) v32;
344 addr->octet[5] = (u8) (v32 >> 8);
a9533e7e
HP
345}
346#endif /* defined(BCMDBG) */
347
348/* keep the chip awake if needed */
7cc4a4c0 349bool wlc_stay_awake(wlc_info_t *wlc)
a9533e7e 350{
0f0881b0 351 return true;
a9533e7e
HP
352}
353
354/* conditions under which the PM bit should be set in outgoing frames and STAY_AWAKE is meaningful
355 */
7cc4a4c0 356bool wlc_ps_allowed(wlc_info_t *wlc)
a9533e7e
HP
357{
358 int idx;
359 wlc_bsscfg_t *cfg;
360
361 /* disallow PS when one of the following global conditions meets */
362 if (!wlc->pub->associated || !wlc->PMenabled || wlc->PM_override)
0965ae88 363 return false;
a9533e7e
HP
364
365 /* disallow PS when one of these meets when not scanning */
366 if (!wlc->PMblocked) {
367 if (AP_ACTIVE(wlc) || wlc->monitor)
0965ae88 368 return false;
a9533e7e
HP
369 }
370
371 FOREACH_AS_STA(wlc, idx, cfg) {
372 /* disallow PS when one of the following bsscfg specific conditions meets */
373 if (!cfg->BSS || !WLC_PORTOPEN(cfg))
0965ae88 374 return false;
a9533e7e
HP
375
376 if (!cfg->dtim_programmed)
0965ae88 377 return false;
a9533e7e
HP
378 }
379
0f0881b0 380 return true;
a9533e7e
HP
381}
382
b4f790ee 383void wlc_reset(wlc_info_t *wlc)
a2627bc0 384{
a9533e7e
HP
385 WL_TRACE(("wl%d: wlc_reset\n", wlc->pub->unit));
386
0965ae88 387 wlc->check_for_unaligned_tbtt = false;
a9533e7e
HP
388
389 /* slurp up hw mac counters before core reset */
390 if (WLC_UPDATE_STATS(wlc)) {
391 wlc_statsupd(wlc);
392
393 /* reset our snapshot of macstat counters */
394 bzero((char *)wlc->core->macstat_snapshot, sizeof(macstat_t));
395 }
396
397 wlc_bmac_reset(wlc->hw);
398 wlc_ampdu_reset(wlc->ampdu);
399 wlc->txretried = 0;
400
401#ifdef WLC_HIGH_ONLY
402 /* Need to set a flag(to be cleared asynchronously by BMAC driver with high call)
403 * in order to prevent wlc_rpctx_txreclaim() from screwing wlc_rpctx_getnexttxp(),
404 * which could be invoked by already QUEUED high call(s) from BMAC driver before
405 * wlc_bmac_reset() finishes.
406 * It's not needed before in monolithic driver model because d11core interrupts would
407 * have been cleared instantly in wlc_bmac_reset() and no txstatus interrupt
408 * will come to driver to fetch those flushed dma pkt pointers.
409 */
0f0881b0 410 wlc->reset_bmac_pending = true;
a9533e7e
HP
411
412 wlc_rpctx_txreclaim(wlc->rpctx);
413
414 wlc_stf_phy_txant_upd(wlc);
415 wlc_phy_ant_rxdiv_set(wlc->band->pi, wlc->stf->ant_rx_ovr);
416#endif
417}
418
7cc4a4c0 419void wlc_fatal_error(wlc_info_t *wlc)
a9533e7e
HP
420{
421 WL_ERROR(("wl%d: fatal error, reinitializing\n", wlc->pub->unit));
422 wl_init(wlc->wl);
423}
424
425/* Return the channel the driver should initialize during wlc_init.
426 * the channel may have to be changed from the currently configured channel
427 * if other configurations are in conflict (bandlocked, 11n mode disabled,
428 * invalid channel for current country, etc.)
429 */
b4f790ee 430static chanspec_t wlc_init_chanspec(wlc_info_t *wlc)
a2627bc0 431{
a9533e7e
HP
432 chanspec_t chanspec =
433 1 | WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE |
434 WL_CHANSPEC_BAND_2G;
435
436 /* make sure the channel is on the supported band if we are band-restricted */
437 if (wlc->bandlocked || NBANDS(wlc) == 1) {
438 ASSERT(CHSPEC_WLCBANDUNIT(chanspec) == wlc->band->bandunit);
439 }
440 ASSERT(wlc_valid_chanspec_db(wlc->cmi, chanspec));
441 return chanspec;
442}
443
444struct scb global_scb;
445
7cc4a4c0 446static void wlc_init_scb(wlc_info_t *wlc, struct scb *scb)
a9533e7e
HP
447{
448 int i;
449 scb->flags = SCB_WMECAP | SCB_HTCAP;
450 for (i = 0; i < NUMPRIO; i++)
451 scb->seqnum[i] = 0;
452}
453
b4f790ee 454void wlc_init(wlc_info_t *wlc)
a2627bc0 455{
a9533e7e
HP
456 d11regs_t *regs;
457 chanspec_t chanspec;
458 int i;
459 wlc_bsscfg_t *bsscfg;
0965ae88 460 bool mute = false;
a9533e7e
HP
461
462 WL_TRACE(("wl%d: wlc_init\n", wlc->pub->unit));
463
464 regs = wlc->regs;
465
466 /* This will happen if a big-hammer was executed. In that case, we want to go back
467 * to the channel that we were on and not new channel
468 */
469 if (wlc->pub->associated)
470 chanspec = wlc->home_chanspec;
471 else
472 chanspec = wlc_init_chanspec(wlc);
473
474 wlc_bmac_init(wlc->hw, chanspec, mute);
475
476 wlc->seckeys = wlc_bmac_read_shm(wlc->hw, M_SECRXKEYS_PTR) * 2;
477 if (D11REV_GE(wlc->pub->corerev, 15) && (wlc->machwcap & MCAP_TKIPMIC))
478 wlc->tkmickeys =
479 wlc_bmac_read_shm(wlc->hw, M_TKMICKEYS_PTR) * 2;
480
481 /* update beacon listen interval */
482 wlc_bcn_li_upd(wlc);
483 wlc->bcn_wait_prd =
41feb5ed 484 (u8) (wlc_bmac_read_shm(wlc->hw, M_NOSLPZNATDTIM) >> 10);
a9533e7e
HP
485 ASSERT(wlc->bcn_wait_prd > 0);
486
487 /* the world is new again, so is our reported rate */
488 wlc_reprate_init(wlc);
489
490 /* write ethernet address to core */
491 FOREACH_BSS(wlc, i, bsscfg) {
492 wlc_set_mac(bsscfg);
493 wlc_set_bssid(bsscfg);
494 }
495
496 /* Update tsf_cfprep if associated and up */
497 if (wlc->pub->associated) {
498 FOREACH_BSS(wlc, i, bsscfg) {
499 if (bsscfg->up) {
66cbd3ab 500 u32 bi;
a9533e7e
HP
501
502 /* get beacon period from bsscfg and convert to uS */
503 bi = bsscfg->current_bss->beacon_period << 10;
504 /* update the tsf_cfprep register */
505 /* since init path would reset to default value */
506 W_REG(wlc->osh, &regs->tsf_cfprep,
507 (bi << CFPREP_CBI_SHIFT));
508
509 /* Update maccontrol PM related bits */
510 wlc_set_ps_ctrl(wlc);
511
512 break;
513 }
514 }
515 }
516
517 wlc_key_hw_init_all(wlc);
518
519 wlc_bandinit_ordered(wlc, chanspec);
520
521 wlc_init_scb(wlc, &global_scb);
522
523 /* init probe response timeout */
524 wlc_write_shm(wlc, M_PRS_MAXTIME, wlc->prb_resp_timeout);
525
526 /* init max burst txop (framebursting) */
527 wlc_write_shm(wlc, M_MBURST_TXOP,
528 (wlc->
529 _rifs ? (EDCF_AC_VO_TXOP_AP << 5) : MAXFRAMEBURST_TXOP));
530
531 /* initialize maximum allowed duty cycle */
0f0881b0 532 wlc_duty_cycle_set(wlc, wlc->tx_duty_cycle_ofdm, true, true);
0965ae88 533 wlc_duty_cycle_set(wlc, wlc->tx_duty_cycle_cck, false, true);
a9533e7e
HP
534
535 /* Update some shared memory locations related to max AMPDU size allowed to received */
536 wlc_ampdu_shm_upd(wlc->ampdu);
537
538 /* band-specific inits */
539 wlc_bsinit(wlc);
540
541 /* Enable EDCF mode (while the MAC is suspended) */
542 if (EDCF_ENAB(wlc->pub)) {
543 OR_REG(wlc->osh, &regs->ifs_ctl, IFS_USEEDCF);
0965ae88 544 wlc_edcf_setparams(wlc->cfg, false);
a9533e7e
HP
545 }
546
547 /* Init precedence maps for empty FIFOs */
548 wlc_tx_prec_map_init(wlc);
549
550 /* read the ucode version if we have not yet done so */
551 if (wlc->ucode_rev == 0) {
552 wlc->ucode_rev =
7d4df48e 553 wlc_read_shm(wlc, M_BOM_REV_MAJOR) << NBITS(u16);
a9533e7e
HP
554 wlc->ucode_rev |= wlc_read_shm(wlc, M_BOM_REV_MINOR);
555 }
556
557 /* ..now really unleash hell (allow the MAC out of suspend) */
558 wlc_enable_mac(wlc);
559
560 /* clear tx flow control */
561 wlc_txflowcontrol_reset(wlc);
562
563 /* clear tx data fifo suspends */
0965ae88 564 wlc->tx_suspended = false;
a9533e7e
HP
565
566 /* enable the RF Disable Delay timer */
567 if (D11REV_GE(wlc->pub->corerev, 10))
568 W_REG(wlc->osh, &wlc->regs->rfdisabledly, RFDISABLE_DEFAULT);
569
570 /* initialize mpc delay */
571 wlc->mpc_delay_off = wlc->mpc_dlycnt = WLC_MPC_MIN_DELAYCNT;
572
573 /*
574 * Initialize WME parameters; if they haven't been set by some other
575 * mechanism (IOVar, etc) then read them from the hardware.
576 */
577 if (WLC_WME_RETRY_SHORT_GET(wlc, 0) == 0) { /* Unintialized; read from HW */
578 int ac;
579
580 ASSERT(wlc->clk);
581 for (ac = 0; ac < AC_COUNT; ac++) {
582 wlc->wme_retries[ac] =
583 wlc_read_shm(wlc, M_AC_TXLMT_ADDR(ac));
584 }
585 }
586}
587
7cc4a4c0 588void wlc_mac_bcn_promisc_change(wlc_info_t *wlc, bool promisc)
a9533e7e
HP
589{
590 wlc->bcnmisc_monitor = promisc;
591 wlc_mac_bcn_promisc(wlc);
592}
593
7cc4a4c0 594void wlc_mac_bcn_promisc(wlc_info_t *wlc)
a9533e7e
HP
595{
596 if ((AP_ENAB(wlc->pub) && (N_ENAB(wlc->pub) || wlc->band->gmode)) ||
597 wlc->bcnmisc_ibss || wlc->bcnmisc_scan || wlc->bcnmisc_monitor)
598 wlc_mctrl(wlc, MCTL_BCNS_PROMISC, MCTL_BCNS_PROMISC);
599 else
600 wlc_mctrl(wlc, MCTL_BCNS_PROMISC, 0);
601}
602
603/* set or clear maccontrol bits MCTL_PROMISC and MCTL_KEEPCONTROL */
7cc4a4c0 604void wlc_mac_promisc(wlc_info_t *wlc)
a9533e7e 605{
66cbd3ab 606 u32 promisc_bits = 0;
a9533e7e
HP
607
608 /* promiscuous mode just sets MCTL_PROMISC
609 * Note: APs get all BSS traffic without the need to set the MCTL_PROMISC bit
610 * since all BSS data traffic is directed at the AP
611 */
612 if (PROMISC_ENAB(wlc->pub) && !AP_ENAB(wlc->pub) && !wlc->wet)
613 promisc_bits |= MCTL_PROMISC;
614
615 /* monitor mode needs both MCTL_PROMISC and MCTL_KEEPCONTROL
616 * Note: monitor mode also needs MCTL_BCNS_PROMISC, but that is
617 * handled in wlc_mac_bcn_promisc()
618 */
619 if (MONITOR_ENAB(wlc))
620 promisc_bits |= MCTL_PROMISC | MCTL_KEEPCONTROL;
621
622 wlc_mctrl(wlc, MCTL_PROMISC | MCTL_KEEPCONTROL, promisc_bits);
623}
624
625/* check if hps and wake states of sw and hw are in sync */
7cc4a4c0 626bool wlc_ps_check(wlc_info_t *wlc)
a9533e7e 627{
0f0881b0 628 bool res = true;
a9533e7e
HP
629 bool hps, wake;
630 bool wake_ok;
631
632 if (!AP_ACTIVE(wlc)) {
66cbd3ab 633 volatile u32 tmp;
a9533e7e
HP
634 tmp = R_REG(wlc->osh, &wlc->regs->maccontrol);
635
636 /* If deviceremoved is detected, then don't take any action as this can be called
637 * in any context. Assume that caller will take care of the condition. This is just
638 * to avoid assert
639 */
640 if (tmp == 0xffffffff) {
641 WL_ERROR(("wl%d: %s: dead chip\n", wlc->pub->unit,
642 __func__));
643 return DEVICEREMOVED(wlc);
644 }
645
646 hps = PS_ALLOWED(wlc);
647
648 if (hps != ((tmp & MCTL_HPS) != 0)) {
649 int idx;
650 wlc_bsscfg_t *cfg;
651 WL_ERROR(("wl%d: hps not sync, sw %d, maccontrol 0x%x\n", wlc->pub->unit, hps, tmp));
652 FOREACH_BSS(wlc, idx, cfg) {
653 if (!BSSCFG_STA(cfg))
654 continue;
655 }
656
0965ae88 657 res = false;
a9533e7e
HP
658 }
659#ifdef WLC_LOW
660 /* For a monolithic build the wake check can be exact since it looks at wake
661 * override bits. The MCTL_WAKE bit should match the 'wake' value.
662 */
663 wake = STAY_AWAKE(wlc) || wlc->hw->wake_override;
664 wake_ok = (wake == ((tmp & MCTL_WAKE) != 0));
665#else
666 /* For a split build we will not have access to any wake overrides from the low
667 * level. The check can only make sure the MCTL_WAKE bit is on if the high
668 * level 'wake' value is true. If the high level 'wake' is false, the MCTL_WAKE
669 * may be either true or false due to the low level override.
670 */
671 wake = STAY_AWAKE(wlc);
672 wake_ok = (wake && ((tmp & MCTL_WAKE) != 0)) || !wake;
673#endif
674 if (hps && !wake_ok) {
675 WL_ERROR(("wl%d: wake not sync, sw %d maccontrol 0x%x\n", wlc->pub->unit, wake, tmp));
0965ae88 676 res = false;
a9533e7e
HP
677 }
678 }
679 ASSERT(res);
680 return res;
681}
682
683/* push sw hps and wake state through hardware */
7cc4a4c0 684void wlc_set_ps_ctrl(wlc_info_t *wlc)
a9533e7e 685{
66cbd3ab 686 u32 v1, v2;
a9533e7e
HP
687 bool hps, wake;
688 bool awake_before;
689
690 hps = PS_ALLOWED(wlc);
0f0881b0 691 wake = hps ? (STAY_AWAKE(wlc)) : true;
a9533e7e
HP
692
693 WL_TRACE(("wl%d: wlc_set_ps_ctrl: hps %d wake %d\n", wlc->pub->unit,
694 hps, wake));
695
696 v1 = R_REG(wlc->osh, &wlc->regs->maccontrol);
697 v2 = 0;
698 if (hps)
699 v2 |= MCTL_HPS;
700 if (wake)
701 v2 |= MCTL_WAKE;
702
703 wlc_mctrl(wlc, MCTL_WAKE | MCTL_HPS, v2);
704
705 awake_before = ((v1 & MCTL_WAKE) || ((v1 & MCTL_HPS) == 0));
706
707 if (wake && !awake_before)
708 wlc_bmac_wait_for_wake(wlc->hw);
709
710}
711
712/*
713 * Write this BSS config's MAC address to core.
714 * Updates RXE match engine.
715 */
7cc4a4c0 716int wlc_set_mac(wlc_bsscfg_t *cfg)
a9533e7e
HP
717{
718 int err = 0;
719 wlc_info_t *wlc = cfg->wlc;
720
721 if (cfg == wlc->cfg) {
722 /* enter the MAC addr into the RXE match registers */
723 wlc_set_addrmatch(wlc, RCM_MAC_OFFSET, &cfg->cur_etheraddr);
724 }
725
726 wlc_ampdu_macaddr_upd(wlc);
727
728 return err;
729}
730
731/* Write the BSS config's BSSID address to core (set_bssid in d11procs.tcl).
732 * Updates RXE match engine.
733 */
7cc4a4c0 734void wlc_set_bssid(wlc_bsscfg_t *cfg)
a9533e7e
HP
735{
736 wlc_info_t *wlc = cfg->wlc;
737
738 /* if primary config, we need to update BSSID in RXE match registers */
739 if (cfg == wlc->cfg) {
740 wlc_set_addrmatch(wlc, RCM_BSSID_OFFSET, &cfg->BSSID);
741 }
742#ifdef SUPPORT_HWKEYS
743 else if (BSSCFG_STA(cfg) && cfg->BSS) {
744 wlc_rcmta_add_bssid(wlc, cfg);
745 }
746#endif
747}
748
749/*
750 * Suspend the the MAC and update the slot timing
751 * for standard 11b/g (20us slots) or shortslot 11g (9us slots).
752 */
7cc4a4c0 753void wlc_switch_shortslot(wlc_info_t *wlc, bool shortslot)
a9533e7e
HP
754{
755 int idx;
756 wlc_bsscfg_t *cfg;
757
758 ASSERT(wlc->band->gmode);
759
760 /* use the override if it is set */
761 if (wlc->shortslot_override != WLC_SHORTSLOT_AUTO)
762 shortslot = (wlc->shortslot_override == WLC_SHORTSLOT_ON);
763
764 if (wlc->shortslot == shortslot)
765 return;
766
767 wlc->shortslot = shortslot;
768
769 /* update the capability based on current shortslot mode */
770 FOREACH_BSS(wlc, idx, cfg) {
771 if (!cfg->associated)
772 continue;
773 cfg->current_bss->capability &= ~DOT11_CAP_SHORTSLOT;
774 if (wlc->shortslot)
775 cfg->current_bss->capability |= DOT11_CAP_SHORTSLOT;
776 }
777
778 wlc_bmac_set_shortslot(wlc->hw, shortslot);
779}
780
41feb5ed 781static u8 wlc_local_constraint_qdbm(wlc_info_t *wlc)
a9533e7e 782{
41feb5ed 783 u8 local;
e59fe083 784 s16 local_max;
a9533e7e
HP
785
786 local = WLC_TXPWR_MAX;
787 if (wlc->pub->associated &&
788 (wf_chspec_ctlchan(wlc->chanspec) ==
789 wf_chspec_ctlchan(wlc->home_chanspec))) {
790
791 /* get the local power constraint if we are on the AP's
792 * channel [802.11h, 7.3.2.13]
793 */
794 /* Clamp the value between 0 and WLC_TXPWR_MAX w/o overflowing the target */
795 local_max =
796 (wlc->txpwr_local_max -
797 wlc->txpwr_local_constraint) * WLC_TXPWR_DB_FACTOR;
798 if (local_max > 0 && local_max < WLC_TXPWR_MAX)
41feb5ed 799 return (u8) local_max;
a9533e7e
HP
800 if (local_max < 0)
801 return 0;
802 }
803
804 return local;
805}
806
807/* propagate home chanspec to all bsscfgs in case bsscfg->current_bss->chanspec is referenced */
7cc4a4c0 808void wlc_set_home_chanspec(wlc_info_t *wlc, chanspec_t chanspec)
a9533e7e
HP
809{
810 if (wlc->home_chanspec != chanspec) {
811 int idx;
812 wlc_bsscfg_t *cfg;
813
814 wlc->home_chanspec = chanspec;
815
816 FOREACH_BSS(wlc, idx, cfg) {
817 if (!cfg->associated)
818 continue;
819 cfg->target_bss->chanspec = chanspec;
820 cfg->current_bss->chanspec = chanspec;
821 }
822
823 }
824}
825
7cc4a4c0 826static void wlc_set_phy_chanspec(wlc_info_t *wlc, chanspec_t chanspec)
a9533e7e
HP
827{
828 /* Save our copy of the chanspec */
829 wlc->chanspec = chanspec;
830
831 /* Set the chanspec and power limits for this locale after computing
832 * any 11h local tx power constraints.
833 */
834 wlc_channel_set_chanspec(wlc->cmi, chanspec,
835 wlc_local_constraint_qdbm(wlc));
836
837 if (wlc->stf->ss_algosel_auto)
838 wlc_stf_ss_algo_channel_get(wlc, &wlc->stf->ss_algo_channel,
839 chanspec);
840
841 wlc_stf_ss_update(wlc, wlc->band);
842
843}
844
7cc4a4c0 845void wlc_set_chanspec(wlc_info_t *wlc, chanspec_t chanspec)
a9533e7e
HP
846{
847 uint bandunit;
0965ae88 848 bool switchband = false;
a9533e7e
HP
849 chanspec_t old_chanspec = wlc->chanspec;
850
851 if (!wlc_valid_chanspec_db(wlc->cmi, chanspec)) {
852 WL_ERROR(("wl%d: %s: Bad channel %d\n",
853 wlc->pub->unit, __func__, CHSPEC_CHANNEL(chanspec)));
854 ASSERT(wlc_valid_chanspec_db(wlc->cmi, chanspec));
855 return;
856 }
857
858 /* Switch bands if necessary */
859 if (NBANDS(wlc) > 1) {
860 bandunit = CHSPEC_WLCBANDUNIT(chanspec);
861 if (wlc->band->bandunit != bandunit || wlc->bandinit_pending) {
0f0881b0 862 switchband = true;
a9533e7e
HP
863 if (wlc->bandlocked) {
864 WL_ERROR(("wl%d: %s: chspec %d band is locked!\n", wlc->pub->unit, __func__, CHSPEC_CHANNEL(chanspec)));
865 return;
866 }
867 /* BMAC_NOTE: should the setband call come after the wlc_bmac_chanspec() ?
868 * if the setband updates (wlc_bsinit) use low level calls to inspect and
869 * set state, the state inspected may be from the wrong band, or the
870 * following wlc_bmac_set_chanspec() may undo the work.
871 */
872 wlc_setband(wlc, bandunit);
873 }
874 }
875
876 ASSERT(N_ENAB(wlc->pub) || !CHSPEC_IS40(chanspec));
877
878 /* sync up phy/radio chanspec */
879 wlc_set_phy_chanspec(wlc, chanspec);
880
881 /* init antenna selection */
882 if (CHSPEC_WLC_BW(old_chanspec) != CHSPEC_WLC_BW(chanspec)) {
883 if (WLANTSEL_ENAB(wlc))
884 wlc_antsel_init(wlc->asi);
885
886 /* Fix the hardware rateset based on bw.
887 * Mainly add MCS32 for 40Mhz, remove MCS 32 for 20Mhz
888 */
889 wlc_rateset_bw_mcs_filter(&wlc->band->hw_rateset,
890 wlc->band->
891 mimo_cap_40 ? CHSPEC_WLC_BW(chanspec)
892 : 0);
893 }
894
895 /* update some mac configuration since chanspec changed */
896 wlc_ucode_mac_upd(wlc);
897}
898
899#if defined(BCMDBG)
7cc4a4c0 900static int wlc_get_current_txpwr(wlc_info_t *wlc, void *pwr, uint len)
a9533e7e
HP
901{
902 txpwr_limits_t txpwr;
903 tx_power_t power;
904 tx_power_legacy_t *old_power = NULL;
905 int r, c;
906 uint qdbm;
907 bool override;
908
909 if (len == sizeof(tx_power_legacy_t))
910 old_power = (tx_power_legacy_t *) pwr;
911 else if (len < sizeof(tx_power_t))
912 return BCME_BUFTOOSHORT;
913
914 bzero(&power, sizeof(tx_power_t));
915
916 power.chanspec = WLC_BAND_PI_RADIO_CHANSPEC;
917 if (wlc->pub->associated)
918 power.local_chanspec = wlc->home_chanspec;
919
920 /* Return the user target tx power limits for the various rates. Note wlc_phy.c's
921 * public interface only implements getting and setting a single value for all of
922 * rates, so we need to fill the array ourselves.
923 */
924 wlc_phy_txpower_get(wlc->band->pi, &qdbm, &override);
925 for (r = 0; r < WL_TX_POWER_RATES; r++) {
41feb5ed 926 power.user_limit[r] = (u8) qdbm;
a9533e7e
HP
927 }
928
929 power.local_max = wlc->txpwr_local_max * WLC_TXPWR_DB_FACTOR;
930 power.local_constraint =
931 wlc->txpwr_local_constraint * WLC_TXPWR_DB_FACTOR;
932
933 power.antgain[0] = wlc->bandstate[BAND_2G_INDEX]->antgain;
934 power.antgain[1] = wlc->bandstate[BAND_5G_INDEX]->antgain;
935
936 wlc_channel_reg_limits(wlc->cmi, power.chanspec, &txpwr);
937
938#if WL_TX_POWER_CCK_NUM != WLC_NUM_RATES_CCK
939#error "WL_TX_POWER_CCK_NUM != WLC_NUM_RATES_CCK"
940#endif
941
942 /* CCK tx power limits */
943 for (c = 0, r = WL_TX_POWER_CCK_FIRST; c < WL_TX_POWER_CCK_NUM;
944 c++, r++)
945 power.reg_limit[r] = txpwr.cck[c];
946
947#if WL_TX_POWER_OFDM_NUM != WLC_NUM_RATES_OFDM
948#error "WL_TX_POWER_OFDM_NUM != WLC_NUM_RATES_OFDM"
949#endif
950
951 /* 20 MHz OFDM SISO tx power limits */
952 for (c = 0, r = WL_TX_POWER_OFDM_FIRST; c < WL_TX_POWER_OFDM_NUM;
953 c++, r++)
954 power.reg_limit[r] = txpwr.ofdm[c];
955
956 if (WLC_PHY_11N_CAP(wlc->band)) {
957
958 /* 20 MHz OFDM CDD tx power limits */
959 for (c = 0, r = WL_TX_POWER_OFDM20_CDD_FIRST;
960 c < WL_TX_POWER_OFDM_NUM; c++, r++)
961 power.reg_limit[r] = txpwr.ofdm_cdd[c];
962
963 /* 40 MHz OFDM SISO tx power limits */
964 for (c = 0, r = WL_TX_POWER_OFDM40_SISO_FIRST;
965 c < WL_TX_POWER_OFDM_NUM; c++, r++)
966 power.reg_limit[r] = txpwr.ofdm_40_siso[c];
967
968 /* 40 MHz OFDM CDD tx power limits */
969 for (c = 0, r = WL_TX_POWER_OFDM40_CDD_FIRST;
970 c < WL_TX_POWER_OFDM_NUM; c++, r++)
971 power.reg_limit[r] = txpwr.ofdm_40_cdd[c];
972
973#if WL_TX_POWER_MCS_1_STREAM_NUM != WLC_NUM_RATES_MCS_1_STREAM
974#error "WL_TX_POWER_MCS_1_STREAM_NUM != WLC_NUM_RATES_MCS_1_STREAM"
975#endif
976
977 /* 20MHz MCS0-7 SISO tx power limits */
978 for (c = 0, r = WL_TX_POWER_MCS20_SISO_FIRST;
979 c < WLC_NUM_RATES_MCS_1_STREAM; c++, r++)
980 power.reg_limit[r] = txpwr.mcs_20_siso[c];
981
982 /* 20MHz MCS0-7 CDD tx power limits */
983 for (c = 0, r = WL_TX_POWER_MCS20_CDD_FIRST;
984 c < WLC_NUM_RATES_MCS_1_STREAM; c++, r++)
985 power.reg_limit[r] = txpwr.mcs_20_cdd[c];
986
987 /* 20MHz MCS0-7 STBC tx power limits */
988 for (c = 0, r = WL_TX_POWER_MCS20_STBC_FIRST;
989 c < WLC_NUM_RATES_MCS_1_STREAM; c++, r++)
990 power.reg_limit[r] = txpwr.mcs_20_stbc[c];
991
992 /* 40MHz MCS0-7 SISO tx power limits */
993 for (c = 0, r = WL_TX_POWER_MCS40_SISO_FIRST;
994 c < WLC_NUM_RATES_MCS_1_STREAM; c++, r++)
995 power.reg_limit[r] = txpwr.mcs_40_siso[c];
996
997 /* 40MHz MCS0-7 CDD tx power limits */
998 for (c = 0, r = WL_TX_POWER_MCS40_CDD_FIRST;
999 c < WLC_NUM_RATES_MCS_1_STREAM; c++, r++)
1000 power.reg_limit[r] = txpwr.mcs_40_cdd[c];
1001
1002 /* 40MHz MCS0-7 STBC tx power limits */
1003 for (c = 0, r = WL_TX_POWER_MCS40_STBC_FIRST;
1004 c < WLC_NUM_RATES_MCS_1_STREAM; c++, r++)
1005 power.reg_limit[r] = txpwr.mcs_40_stbc[c];
1006
1007#if WL_TX_POWER_MCS_2_STREAM_NUM != WLC_NUM_RATES_MCS_2_STREAM
1008#error "WL_TX_POWER_MCS_2_STREAM_NUM != WLC_NUM_RATES_MCS_2_STREAM"
1009#endif
1010
1011 /* 20MHz MCS8-15 SDM tx power limits */
1012 for (c = 0, r = WL_TX_POWER_MCS20_SDM_FIRST;
1013 c < WLC_NUM_RATES_MCS_2_STREAM; c++, r++)
1014 power.reg_limit[r] = txpwr.mcs_20_mimo[c];
1015
1016 /* 40MHz MCS8-15 SDM tx power limits */
1017 for (c = 0, r = WL_TX_POWER_MCS40_SDM_FIRST;
1018 c < WLC_NUM_RATES_MCS_2_STREAM; c++, r++)
1019 power.reg_limit[r] = txpwr.mcs_40_mimo[c];
1020
1021 /* MCS 32 */
1022 power.reg_limit[WL_TX_POWER_MCS_32] = txpwr.mcs32;
1023 }
1024
1025 wlc_phy_txpower_get_current(wlc->band->pi, &power,
1026 CHSPEC_CHANNEL(power.chanspec));
1027
1028 /* copy the tx_power_t struct to the return buffer,
1029 * or convert to a tx_power_legacy_t struct
1030 */
1031 if (!old_power) {
1032 bcopy(&power, pwr, sizeof(tx_power_t));
1033 } else {
1034 int band_idx = CHSPEC_IS2G(power.chanspec) ? 0 : 1;
1035
1036 bzero(old_power, sizeof(tx_power_legacy_t));
1037
1038 old_power->txpwr_local_max = power.local_max;
1039 old_power->txpwr_local_constraint = power.local_constraint;
1040 if (CHSPEC_IS2G(power.chanspec)) {
1041 old_power->txpwr_chan_reg_max = txpwr.cck[0];
1042 old_power->txpwr_est_Pout[band_idx] =
1043 power.est_Pout_cck;
1044 old_power->txpwr_est_Pout_gofdm = power.est_Pout[0];
1045 } else {
1046 old_power->txpwr_chan_reg_max = txpwr.ofdm[0];
1047 old_power->txpwr_est_Pout[band_idx] = power.est_Pout[0];
1048 }
1049 old_power->txpwr_antgain[0] = power.antgain[0];
1050 old_power->txpwr_antgain[1] = power.antgain[1];
1051
1052 for (r = 0; r < NUM_PWRCTRL_RATES; r++) {
1053 old_power->txpwr_band_max[r] = power.user_limit[r];
1054 old_power->txpwr_limit[r] = power.reg_limit[r];
1055 old_power->txpwr_target[band_idx][r] = power.target[r];
1056 if (CHSPEC_IS2G(power.chanspec))
1057 old_power->txpwr_bphy_cck_max[r] =
1058 power.board_limit[r];
1059 else
1060 old_power->txpwr_aphy_max[r] =
1061 power.board_limit[r];
1062 }
1063 }
1064
1065 return 0;
1066}
1067#endif /* defined(BCMDBG) */
1068
66cbd3ab 1069static u32 wlc_watchdog_backup_bi(wlc_info_t *wlc)
a9533e7e 1070{
66cbd3ab 1071 u32 bi;
a9533e7e
HP
1072 bi = 2 * wlc->cfg->current_bss->dtim_period *
1073 wlc->cfg->current_bss->beacon_period;
1074 if (wlc->bcn_li_dtim)
1075 bi *= wlc->bcn_li_dtim;
1076 else if (wlc->bcn_li_bcn)
1077 /* recalculate bi based on bcn_li_bcn */
1078 bi = 2 * wlc->bcn_li_bcn * wlc->cfg->current_bss->beacon_period;
1079
1080 if (bi < 2 * TIMER_INTERVAL_WATCHDOG)
1081 bi = 2 * TIMER_INTERVAL_WATCHDOG;
1082 return bi;
1083}
1084
1085/* Change to run the watchdog either from a periodic timer or from tbtt handler.
0f0881b0 1086 * Call watchdog from tbtt handler if tbtt is true, watchdog timer otherwise.
a9533e7e 1087 */
7cc4a4c0 1088void wlc_watchdog_upd(wlc_info_t *wlc, bool tbtt)
a9533e7e
HP
1089{
1090 /* make sure changing watchdog driver is allowed */
1091 if (!wlc->pub->up || !wlc->pub->align_wd_tbtt)
1092 return;
1093 if (!tbtt && wlc->WDarmed) {
1094 wl_del_timer(wlc->wl, wlc->wdtimer);
0965ae88 1095 wlc->WDarmed = false;
a9533e7e
HP
1096 }
1097
1098 /* stop watchdog timer and use tbtt interrupt to drive watchdog */
1099 if (tbtt && wlc->WDarmed) {
1100 wl_del_timer(wlc->wl, wlc->wdtimer);
0965ae88 1101 wlc->WDarmed = false;
a9533e7e
HP
1102 wlc->WDlast = OSL_SYSUPTIME();
1103 }
1104 /* arm watchdog timer and drive the watchdog there */
1105 else if (!tbtt && !wlc->WDarmed) {
1106 wl_add_timer(wlc->wl, wlc->wdtimer, TIMER_INTERVAL_WATCHDOG,
0f0881b0
GKH
1107 true);
1108 wlc->WDarmed = true;
a9533e7e
HP
1109 }
1110 if (tbtt && !wlc->WDarmed) {
1111 wl_add_timer(wlc->wl, wlc->wdtimer, wlc_watchdog_backup_bi(wlc),
0f0881b0
GKH
1112 true);
1113 wlc->WDarmed = true;
a9533e7e
HP
1114 }
1115}
1116
7cc4a4c0 1117ratespec_t wlc_lowest_basic_rspec(wlc_info_t *wlc, wlc_rateset_t *rs)
a9533e7e
HP
1118{
1119 ratespec_t lowest_basic_rspec;
1120 uint i;
1121
1122 /* Use the lowest basic rate */
1123 lowest_basic_rspec = rs->rates[0] & RATE_MASK;
1124 for (i = 0; i < rs->count; i++) {
1125 if (rs->rates[i] & WLC_RATE_FLAG) {
1126 lowest_basic_rspec = rs->rates[i] & RATE_MASK;
1127 break;
1128 }
1129 }
1130#if NCONF
1131 /* pick siso/cdd as default for OFDM (note no basic rate MCSs are supported yet) */
1132 if (IS_OFDM(lowest_basic_rspec)) {
1133 lowest_basic_rspec |= (wlc->stf->ss_opmode << RSPEC_STF_SHIFT);
1134 }
1135#endif
1136
90ea2296 1137 return lowest_basic_rspec;
a9533e7e
HP
1138}
1139
1140/* This function changes the phytxctl for beacon based on current beacon ratespec AND txant
1141 * setting as per this table:
1142 * ratespec CCK ant = wlc->stf->txant
1143 * OFDM ant = 3
1144 */
7cc4a4c0 1145void wlc_beacon_phytxctl_txant_upd(wlc_info_t *wlc, ratespec_t bcn_rspec)
a9533e7e 1146{
7d4df48e
GKH
1147 u16 phyctl;
1148 u16 phytxant = wlc->stf->phytxant;
1149 u16 mask = PHY_TXC_ANT_MASK;
a9533e7e
HP
1150
1151 /* for non-siso rates or default setting, use the available chains */
1152 if (WLC_PHY_11N_CAP(wlc->band)) {
1153 phytxant = wlc_stf_phytxchain_sel(wlc, bcn_rspec);
1154 }
1155
1156 phyctl = wlc_read_shm(wlc, M_BCN_PCTLWD);
1157 phyctl = (phyctl & ~mask) | phytxant;
1158 wlc_write_shm(wlc, M_BCN_PCTLWD, phyctl);
1159}
1160
1161/* centralized protection config change function to simplify debugging, no consistency checking
1162 * this should be called only on changes to avoid overhead in periodic function
1163*/
7cc4a4c0 1164void wlc_protection_upd(wlc_info_t *wlc, uint idx, int val)
a9533e7e
HP
1165{
1166 WL_TRACE(("wlc_protection_upd: idx %d, val %d\n", idx, val));
1167
1168 switch (idx) {
1169 case WLC_PROT_G_SPEC:
1170 wlc->protection->_g = (bool) val;
1171 break;
1172 case WLC_PROT_G_OVR:
562c8850 1173 wlc->protection->g_override = (s8) val;
a9533e7e
HP
1174 break;
1175 case WLC_PROT_G_USER:
41feb5ed 1176 wlc->protection->gmode_user = (u8) val;
a9533e7e
HP
1177 break;
1178 case WLC_PROT_OVERLAP:
562c8850 1179 wlc->protection->overlap = (s8) val;
a9533e7e
HP
1180 break;
1181 case WLC_PROT_N_USER:
562c8850 1182 wlc->protection->nmode_user = (s8) val;
a9533e7e
HP
1183 break;
1184 case WLC_PROT_N_CFG:
562c8850 1185 wlc->protection->n_cfg = (s8) val;
a9533e7e
HP
1186 break;
1187 case WLC_PROT_N_CFG_OVR:
562c8850 1188 wlc->protection->n_cfg_override = (s8) val;
a9533e7e
HP
1189 break;
1190 case WLC_PROT_N_NONGF:
1191 wlc->protection->nongf = (bool) val;
1192 break;
1193 case WLC_PROT_N_NONGF_OVR:
562c8850 1194 wlc->protection->nongf_override = (s8) val;
a9533e7e
HP
1195 break;
1196 case WLC_PROT_N_PAM_OVR:
562c8850 1197 wlc->protection->n_pam_override = (s8) val;
a9533e7e
HP
1198 break;
1199 case WLC_PROT_N_OBSS:
1200 wlc->protection->n_obss = (bool) val;
1201 break;
1202
1203 default:
1204 ASSERT(0);
1205 break;
1206 }
1207
1208}
1209
7cc4a4c0 1210static void wlc_ht_update_sgi_rx(wlc_info_t *wlc, int val)
a9533e7e
HP
1211{
1212 wlc->ht_cap.cap &= ~(HT_CAP_SHORT_GI_20 | HT_CAP_SHORT_GI_40);
1213 wlc->ht_cap.cap |= (val & WLC_N_SGI_20) ? HT_CAP_SHORT_GI_20 : 0;
1214 wlc->ht_cap.cap |= (val & WLC_N_SGI_40) ? HT_CAP_SHORT_GI_40 : 0;
1215
1216 if (wlc->pub->up) {
1217 wlc_update_beacon(wlc);
0f0881b0 1218 wlc_update_probe_resp(wlc, true);
a9533e7e
HP
1219 }
1220}
1221
562c8850 1222static void wlc_ht_update_ldpc(wlc_info_t *wlc, s8 val)
a9533e7e
HP
1223{
1224 wlc->stf->ldpc = val;
1225
1226 wlc->ht_cap.cap &= ~HT_CAP_LDPC_CODING;
1227 if (wlc->stf->ldpc != OFF)
1228 wlc->ht_cap.cap |= HT_CAP_LDPC_CODING;
1229
1230 if (wlc->pub->up) {
1231 wlc_update_beacon(wlc);
0f0881b0 1232 wlc_update_probe_resp(wlc, true);
0965ae88 1233 wlc_phy_ldpc_override_set(wlc->band->pi, (val ? true : false));
a9533e7e
HP
1234 }
1235}
1236
1237/*
1238 * ucode, hwmac update
1239 * Channel dependent updates for ucode and hw
1240 */
7cc4a4c0 1241static void wlc_ucode_mac_upd(wlc_info_t *wlc)
a9533e7e
HP
1242{
1243 /* enable or disable any active IBSSs depending on whether or not
1244 * we are on the home channel
1245 */
1246 if (wlc->home_chanspec == WLC_BAND_PI_RADIO_CHANSPEC) {
1247 if (wlc->pub->associated) {
1248 /* BMAC_NOTE: This is something that should be fixed in ucode inits.
1249 * I think that the ucode inits set up the bcn templates and shm values
1250 * with a bogus beacon. This should not be done in the inits. If ucode needs
1251 * to set up a beacon for testing, the test routines should write it down,
1252 * not expect the inits to populate a bogus beacon.
1253 */
1254 if (WLC_PHY_11N_CAP(wlc->band)) {
1255 wlc_write_shm(wlc, M_BCN_TXTSF_OFFSET,
1256 wlc->band->bcntsfoff);
1257 }
1258 }
1259 } else {
1260 /* disable an active IBSS if we are not on the home channel */
1261 }
1262
1263 /* update the various promisc bits */
1264 wlc_mac_bcn_promisc(wlc);
1265 wlc_mac_promisc(wlc);
1266}
1267
7cc4a4c0 1268static void wlc_bandinit_ordered(wlc_info_t *wlc, chanspec_t chanspec)
a9533e7e
HP
1269{
1270 wlc_rateset_t default_rateset;
1271 uint parkband;
1272 uint i, band_order[2];
1273
1274 WL_TRACE(("wl%d: wlc_bandinit_ordered\n", wlc->pub->unit));
1275 /*
1276 * We might have been bandlocked during down and the chip power-cycled (hibernate).
1277 * figure out the right band to park on
1278 */
1279 if (wlc->bandlocked || NBANDS(wlc) == 1) {
1280 ASSERT(CHSPEC_WLCBANDUNIT(chanspec) == wlc->band->bandunit);
1281
1282 parkband = wlc->band->bandunit; /* updated in wlc_bandlock() */
1283 band_order[0] = band_order[1] = parkband;
1284 } else {
1285 /* park on the band of the specified chanspec */
1286 parkband = CHSPEC_WLCBANDUNIT(chanspec);
1287
1288 /* order so that parkband initialize last */
1289 band_order[0] = parkband ^ 1;
1290 band_order[1] = parkband;
1291 }
1292
1293 /* make each band operational, software state init */
1294 for (i = 0; i < NBANDS(wlc); i++) {
1295 uint j = band_order[i];
1296
1297 wlc->band = wlc->bandstate[j];
1298
1299 wlc_default_rateset(wlc, &default_rateset);
1300
1301 /* fill in hw_rate */
1302 wlc_rateset_filter(&default_rateset, &wlc->band->hw_rateset,
0965ae88 1303 false, WLC_RATES_CCK_OFDM, RATE_MASK,
a9533e7e
HP
1304 (bool) N_ENAB(wlc->pub));
1305
1306 /* init basic rate lookup */
1307 wlc_rate_lookup_init(wlc, &default_rateset);
1308 }
1309
1310 /* sync up phy/radio chanspec */
1311 wlc_set_phy_chanspec(wlc, chanspec);
1312}
1313
1314/* band-specific init */
a2627bc0
JC
1315static void WLBANDINITFN(wlc_bsinit) (wlc_info_t *wlc)
1316{
a9533e7e
HP
1317 WL_TRACE(("wl%d: wlc_bsinit: bandunit %d\n", wlc->pub->unit,
1318 wlc->band->bandunit));
1319
1320 /* write ucode ACK/CTS rate table */
1321 wlc_set_ratetable(wlc);
1322
1323 /* update some band specific mac configuration */
1324 wlc_ucode_mac_upd(wlc);
1325
1326 /* init antenna selection */
1327 if (WLANTSEL_ENAB(wlc))
1328 wlc_antsel_init(wlc->asi);
1329
1330}
1331
1332/* switch to and initialize new band */
a2627bc0
JC
1333static void WLBANDINITFN(wlc_setband) (wlc_info_t *wlc, uint bandunit)
1334{
a9533e7e
HP
1335 int idx;
1336 wlc_bsscfg_t *cfg;
1337
1338 ASSERT(NBANDS(wlc) > 1);
1339 ASSERT(!wlc->bandlocked);
1340 ASSERT(bandunit != wlc->band->bandunit || wlc->bandinit_pending);
1341
1342 wlc->band = wlc->bandstate[bandunit];
1343
1344 if (!wlc->pub->up)
1345 return;
1346
1347 /* wait for at least one beacon before entering sleeping state */
0f0881b0 1348 wlc->PMawakebcn = true;
a9533e7e 1349 FOREACH_AS_STA(wlc, idx, cfg)
0f0881b0 1350 cfg->PMawakebcn = true;
a9533e7e
HP
1351 wlc_set_ps_ctrl(wlc);
1352
1353 /* band-specific initializations */
1354 wlc_bsinit(wlc);
1355}
1356
1357/* Initialize a WME Parameter Info Element with default STA parameters from WMM Spec, Table 12 */
7cc4a4c0 1358void wlc_wme_initparams_sta(wlc_info_t *wlc, wme_param_ie_t *pe)
a9533e7e
HP
1359{
1360 static const wme_param_ie_t stadef = {
1361 WME_OUI,
1362 WME_TYPE,
1363 WME_SUBTYPE_PARAM_IE,
1364 WME_VER,
1365 0,
1366 0,
1367 {
1368 {EDCF_AC_BE_ACI_STA, EDCF_AC_BE_ECW_STA,
1369 HTOL16(EDCF_AC_BE_TXOP_STA)},
1370 {EDCF_AC_BK_ACI_STA, EDCF_AC_BK_ECW_STA,
1371 HTOL16(EDCF_AC_BK_TXOP_STA)},
1372 {EDCF_AC_VI_ACI_STA, EDCF_AC_VI_ECW_STA,
1373 HTOL16(EDCF_AC_VI_TXOP_STA)},
1374 {EDCF_AC_VO_ACI_STA, EDCF_AC_VO_ECW_STA,
1375 HTOL16(EDCF_AC_VO_TXOP_STA)}
1376 }
1377 };
1378
1379 ASSERT(sizeof(*pe) == WME_PARAM_IE_LEN);
1380 memcpy(pe, &stadef, sizeof(*pe));
1381}
1382
7cc4a4c0 1383void wlc_wme_setparams(wlc_info_t *wlc, u16 aci, void *arg, bool suspend)
a9533e7e
HP
1384{
1385 int i;
1386 shm_acparams_t acp_shm;
7d4df48e 1387 u16 *shm_entry;
a9533e7e
HP
1388 struct ieee80211_tx_queue_params *params = arg;
1389
1390 ASSERT(wlc);
1391
1392 /* Only apply params if the core is out of reset and has clocks */
1393 if (!wlc->clk) {
1394 WL_ERROR(("wl%d: %s : no-clock\n", wlc->pub->unit, __func__));
1395 return;
1396 }
1397
1398 /*
1399 * AP uses AC params from wme_param_ie_ap.
1400 * AP advertises AC params from wme_param_ie.
1401 * STA uses AC params from wme_param_ie.
1402 */
1403
1404 wlc->wme_admctl = 0;
1405
1406 do {
1407 bzero((char *)&acp_shm, sizeof(shm_acparams_t));
1408 /* find out which ac this set of params applies to */
1409 ASSERT(aci < AC_COUNT);
1410 /* set the admission control policy for this AC */
1411 /* wlc->wme_admctl |= 1 << aci; *//* should be set ?? seems like off by default */
1412
1413 /* fill in shm ac params struct */
1414 acp_shm.txop = ltoh16(params->txop);
1415 /* convert from units of 32us to us for ucode */
1416 wlc->edcf_txop[aci & 0x3] = acp_shm.txop =
1417 EDCF_TXOP2USEC(acp_shm.txop);
1418 acp_shm.aifs = (params->aifs & EDCF_AIFSN_MASK);
1419
1420 if (aci == AC_VI && acp_shm.txop == 0
1421 && acp_shm.aifs < EDCF_AIFSN_MAX)
1422 acp_shm.aifs++;
1423
1424 if (acp_shm.aifs < EDCF_AIFSN_MIN
1425 || acp_shm.aifs > EDCF_AIFSN_MAX) {
1426 WL_ERROR(("wl%d: wlc_edcf_setparams: bad aifs %d\n",
1427 wlc->pub->unit, acp_shm.aifs));
1428 continue;
1429 }
1430
1431 acp_shm.cwmin = params->cw_min;
1432 acp_shm.cwmax = params->cw_max;
1433 acp_shm.cwcur = acp_shm.cwmin;
1434 acp_shm.bslots =
1435 R_REG(wlc->osh, &wlc->regs->tsf_random) & acp_shm.cwcur;
1436 acp_shm.reggap = acp_shm.bslots + acp_shm.aifs;
1437 /* Indicate the new params to the ucode */
1438 acp_shm.status = wlc_read_shm(wlc, (M_EDCF_QINFO +
1439 wme_shmemacindex(aci) *
1440 M_EDCF_QLEN +
1441 M_EDCF_STATUS_OFF));
1442 acp_shm.status |= WME_STATUS_NEWAC;
1443
1444 /* Fill in shm acparam table */
7d4df48e 1445 shm_entry = (u16 *) &acp_shm;
a9533e7e
HP
1446 for (i = 0; i < (int)sizeof(shm_acparams_t); i += 2)
1447 wlc_write_shm(wlc,
1448 M_EDCF_QINFO +
1449 wme_shmemacindex(aci) * M_EDCF_QLEN + i,
1450 *shm_entry++);
1451
1452 } while (0);
1453
1454 if (suspend)
1455 wlc_suspend_mac_and_wait(wlc);
1456
1457 if (suspend)
1458 wlc_enable_mac(wlc);
1459
1460}
1461
7cc4a4c0 1462void wlc_edcf_setparams(wlc_bsscfg_t *cfg, bool suspend)
a9533e7e
HP
1463{
1464 wlc_info_t *wlc = cfg->wlc;
1465 uint aci, i, j;
1466 edcf_acparam_t *edcf_acp;
1467 shm_acparams_t acp_shm;
7d4df48e 1468 u16 *shm_entry;
a9533e7e
HP
1469
1470 ASSERT(cfg);
1471 ASSERT(wlc);
1472
1473 /* Only apply params if the core is out of reset and has clocks */
1474 if (!wlc->clk)
1475 return;
1476
1477 /*
1478 * AP uses AC params from wme_param_ie_ap.
1479 * AP advertises AC params from wme_param_ie.
1480 * STA uses AC params from wme_param_ie.
1481 */
1482
29c4275a 1483 edcf_acp = (edcf_acparam_t *) &wlc->wme_param_ie.acparam[0];
a9533e7e
HP
1484
1485 wlc->wme_admctl = 0;
1486
1487 for (i = 0; i < AC_COUNT; i++, edcf_acp++) {
1488 bzero((char *)&acp_shm, sizeof(shm_acparams_t));
1489 /* find out which ac this set of params applies to */
1490 aci = (edcf_acp->ACI & EDCF_ACI_MASK) >> EDCF_ACI_SHIFT;
1491 ASSERT(aci < AC_COUNT);
1492 /* set the admission control policy for this AC */
1493 if (edcf_acp->ACI & EDCF_ACM_MASK) {
1494 wlc->wme_admctl |= 1 << aci;
1495 }
1496
1497 /* fill in shm ac params struct */
1498 acp_shm.txop = ltoh16(edcf_acp->TXOP);
1499 /* convert from units of 32us to us for ucode */
1500 wlc->edcf_txop[aci] = acp_shm.txop =
1501 EDCF_TXOP2USEC(acp_shm.txop);
1502 acp_shm.aifs = (edcf_acp->ACI & EDCF_AIFSN_MASK);
1503
1504 if (aci == AC_VI && acp_shm.txop == 0
1505 && acp_shm.aifs < EDCF_AIFSN_MAX)
1506 acp_shm.aifs++;
1507
1508 if (acp_shm.aifs < EDCF_AIFSN_MIN
1509 || acp_shm.aifs > EDCF_AIFSN_MAX) {
1510 WL_ERROR(("wl%d: wlc_edcf_setparams: bad aifs %d\n",
1511 wlc->pub->unit, acp_shm.aifs));
1512 continue;
1513 }
1514
1515 /* CWmin = 2^(ECWmin) - 1 */
1516 acp_shm.cwmin = EDCF_ECW2CW(edcf_acp->ECW & EDCF_ECWMIN_MASK);
1517 /* CWmax = 2^(ECWmax) - 1 */
1518 acp_shm.cwmax = EDCF_ECW2CW((edcf_acp->ECW & EDCF_ECWMAX_MASK)
1519 >> EDCF_ECWMAX_SHIFT);
1520 acp_shm.cwcur = acp_shm.cwmin;
1521 acp_shm.bslots =
1522 R_REG(wlc->osh, &wlc->regs->tsf_random) & acp_shm.cwcur;
1523 acp_shm.reggap = acp_shm.bslots + acp_shm.aifs;
1524 /* Indicate the new params to the ucode */
1525 acp_shm.status = wlc_read_shm(wlc, (M_EDCF_QINFO +
1526 wme_shmemacindex(aci) *
1527 M_EDCF_QLEN +
1528 M_EDCF_STATUS_OFF));
1529 acp_shm.status |= WME_STATUS_NEWAC;
1530
1531 /* Fill in shm acparam table */
7d4df48e 1532 shm_entry = (u16 *) &acp_shm;
a9533e7e
HP
1533 for (j = 0; j < (int)sizeof(shm_acparams_t); j += 2)
1534 wlc_write_shm(wlc,
1535 M_EDCF_QINFO +
1536 wme_shmemacindex(aci) * M_EDCF_QLEN + j,
1537 *shm_entry++);
1538 }
1539
1540 if (suspend)
1541 wlc_suspend_mac_and_wait(wlc);
1542
1543 if (AP_ENAB(wlc->pub) && WME_ENAB(wlc->pub)) {
1544 wlc_update_beacon(wlc);
0965ae88 1545 wlc_update_probe_resp(wlc, false);
a9533e7e
HP
1546 }
1547
1548 if (suspend)
1549 wlc_enable_mac(wlc);
1550
1551}
1552
0d2f0724 1553bool wlc_timers_init(wlc_info_t *wlc, int unit)
a2627bc0 1554{
ca8c1e59
JC
1555 wlc->wdtimer = wl_init_timer(wlc->wl, wlc_watchdog_by_timer,
1556 wlc, "watchdog");
1557 if (!wlc->wdtimer) {
a9533e7e
HP
1558 WL_ERROR(("wl%d: wl_init_timer for wdtimer failed\n", unit));
1559 goto fail;
1560 }
1561
ca8c1e59
JC
1562 wlc->radio_timer = wl_init_timer(wlc->wl, wlc_radio_timer,
1563 wlc, "radio");
1564 if (!wlc->radio_timer) {
a9533e7e
HP
1565 WL_ERROR(("wl%d: wl_init_timer for radio_timer failed\n",
1566 unit));
1567 goto fail;
1568 }
1569
0f0881b0 1570 return true;
a9533e7e
HP
1571
1572 fail:
0965ae88 1573 return false;
a9533e7e
HP
1574}
1575
1576/*
1577 * Initialize wlc_info default values ...
1578 * may get overrides later in this function
1579 */
0d2f0724 1580void wlc_info_init(wlc_info_t *wlc, int unit)
a2627bc0 1581{
a9533e7e
HP
1582 int i;
1583 /* Assume the device is there until proven otherwise */
0f0881b0 1584 wlc->device_present = true;
a9533e7e
HP
1585
1586 /* set default power output percentage to 100 percent */
1587 wlc->txpwr_percent = 100;
1588
1589 /* Save our copy of the chanspec */
1590 wlc->chanspec = CH20MHZ_CHSPEC(1);
1591
1592 /* initialize CCK preamble mode to unassociated state */
0965ae88 1593 wlc->shortpreamble = false;
a9533e7e 1594
0f0881b0 1595 wlc->legacy_probe = true;
a9533e7e
HP
1596
1597 /* various 802.11g modes */
0965ae88 1598 wlc->shortslot = false;
a9533e7e
HP
1599 wlc->shortslot_override = WLC_SHORTSLOT_AUTO;
1600
0f0881b0 1601 wlc->barker_overlap_control = true;
a9533e7e
HP
1602 wlc->barker_preamble = WLC_BARKER_SHORT_ALLOWED;
1603 wlc->txburst_limit_override = AUTO;
1604
1605 wlc_protection_upd(wlc, WLC_PROT_G_OVR, WLC_PROTECTION_AUTO);
0965ae88 1606 wlc_protection_upd(wlc, WLC_PROT_G_SPEC, false);
a9533e7e
HP
1607
1608 wlc_protection_upd(wlc, WLC_PROT_N_CFG_OVR, WLC_PROTECTION_AUTO);
1609 wlc_protection_upd(wlc, WLC_PROT_N_CFG, WLC_N_PROTECTION_OFF);
1610 wlc_protection_upd(wlc, WLC_PROT_N_NONGF_OVR, WLC_PROTECTION_AUTO);
0965ae88 1611 wlc_protection_upd(wlc, WLC_PROT_N_NONGF, false);
a9533e7e
HP
1612 wlc_protection_upd(wlc, WLC_PROT_N_PAM_OVR, AUTO);
1613
1614 wlc_protection_upd(wlc, WLC_PROT_OVERLAP, WLC_PROTECTION_CTL_OVERLAP);
1615
1616 /* 802.11g draft 4.0 NonERP elt advertisement */
0f0881b0 1617 wlc->include_legacy_erp = true;
a9533e7e
HP
1618
1619 wlc->stf->ant_rx_ovr = ANT_RX_DIV_DEF;
1620 wlc->stf->txant = ANT_TX_DEF;
1621
1622 wlc->prb_resp_timeout = WLC_PRB_RESP_TIMEOUT;
1623
1624 wlc->usr_fragthresh = DOT11_DEFAULT_FRAG_LEN;
1625 for (i = 0; i < NFIFO; i++)
1626 wlc->fragthresh[i] = DOT11_DEFAULT_FRAG_LEN;
1627 wlc->RTSThresh = DOT11_DEFAULT_RTS_LEN;
1628
1629 /* default rate fallback retry limits */
1630 wlc->SFBL = RETRY_SHORT_FB;
1631 wlc->LFBL = RETRY_LONG_FB;
1632
1633 /* default mac retry limits */
1634 wlc->SRL = RETRY_SHORT_DEF;
1635 wlc->LRL = RETRY_LONG_DEF;
1636
1637 /* init PM state */
1638 wlc->PM = PM_OFF; /* User's setting of PM mode through IOCTL */
0965ae88
GKH
1639 wlc->PM_override = false; /* Prevents from going to PM if our AP is 'ill' */
1640 wlc->PMenabled = false; /* Current PM state */
1641 wlc->PMpending = false; /* Tracks whether STA indicated PM in the last attempt */
1642 wlc->PMblocked = false; /* To allow blocking going into PM during RM and scans */
a9533e7e
HP
1643
1644 /* In WMM Auto mode, PM is allowed if association is a UAPSD association */
0965ae88 1645 wlc->WME_PM_blocked = false;
a9533e7e
HP
1646
1647 /* Init wme queuing method */
0965ae88 1648 wlc->wme_prec_queuing = false;
a9533e7e
HP
1649
1650 /* Overrides for the core to stay awake under zillion conditions Look for STAY_AWAKE */
0965ae88 1651 wlc->wake = false;
a9533e7e 1652 /* Are we waiting for a response to PS-Poll that we sent */
0965ae88 1653 wlc->PSpoll = false;
a9533e7e
HP
1654
1655 /* APSD defaults */
0f0881b0 1656 wlc->wme_apsd = true;
0965ae88 1657 wlc->apsd_sta_usp = false;
a9533e7e
HP
1658 wlc->apsd_trigger_timeout = 0; /* disable the trigger timer */
1659 wlc->apsd_trigger_ac = AC_BITMAP_ALL;
1660
1661 /* Set flag to indicate that hw keys should be used when available. */
0965ae88 1662 wlc->wsec_swkeys = false;
a9533e7e
HP
1663
1664 /* init the 4 static WEP default keys */
1665 for (i = 0; i < WSEC_MAX_DEFAULT_KEYS; i++) {
1666 wlc->wsec_keys[i] = wlc->wsec_def_keys[i];
41feb5ed 1667 wlc->wsec_keys[i]->idx = (u8) i;
a9533e7e
HP
1668 }
1669
0965ae88 1670 wlc->_regulatory_domain = false; /* 802.11d */
a9533e7e
HP
1671
1672 /* WME QoS mode is Auto by default */
1673 wlc->pub->_wme = AUTO;
1674
1675#ifdef BCMSDIODEV_ENABLED
0f0881b0 1676 wlc->pub->_priofc = true; /* enable priority flow control for sdio dongle */
a9533e7e
HP
1677#endif
1678
1679 wlc->pub->_ampdu = AMPDU_AGG_HOST;
1680 wlc->pub->bcmerror = 0;
0f0881b0
GKH
1681 wlc->ibss_allowed = true;
1682 wlc->ibss_coalesce_allowed = true;
a9533e7e
HP
1683 wlc->pub->_coex = ON;
1684
1685 /* intialize mpc delay */
1686 wlc->mpc_delay_off = wlc->mpc_dlycnt = WLC_MPC_MIN_DELAYCNT;
1687
0f0881b0 1688 wlc->pr80838_war = true;
a9533e7e
HP
1689}
1690
7cc4a4c0 1691static bool wlc_state_bmac_sync(wlc_info_t *wlc)
a9533e7e
HP
1692{
1693 wlc_bmac_state_t state_bmac;
1694
1695 if (wlc_bmac_state_get(wlc->hw, &state_bmac) != 0)
0965ae88 1696 return false;
a9533e7e
HP
1697
1698 wlc->machwcap = state_bmac.machwcap;
1699 wlc_protection_upd(wlc, WLC_PROT_N_PAM_OVR,
562c8850 1700 (s8) state_bmac.preamble_ovr);
a9533e7e 1701
0f0881b0 1702 return true;
a9533e7e
HP
1703}
1704
0d2f0724 1705static uint wlc_attach_module(wlc_info_t *wlc)
a2627bc0 1706{
a9533e7e
HP
1707 uint err = 0;
1708 uint unit;
1709 unit = wlc->pub->unit;
1710
ca8c1e59
JC
1711 wlc->asi = wlc_antsel_attach(wlc, wlc->osh, wlc->pub, wlc->hw);
1712 if (wlc->asi == NULL) {
a9533e7e
HP
1713 WL_ERROR(("wl%d: wlc_attach: wlc_antsel_attach failed\n",
1714 unit));
1715 err = 44;
1716 goto fail;
1717 }
1718
ca8c1e59
JC
1719 wlc->ampdu = wlc_ampdu_attach(wlc);
1720 if (wlc->ampdu == NULL) {
a9533e7e
HP
1721 WL_ERROR(("wl%d: wlc_attach: wlc_ampdu_attach failed\n", unit));
1722 err = 50;
1723 goto fail;
1724 }
1725
1726 /* Initialize event queue; needed before following calls */
1727 wlc->eventq =
1728 wlc_eventq_attach(wlc->pub, wlc, wlc->wl, wlc_process_eventq);
1729 if (wlc->eventq == NULL) {
1730 WL_ERROR(("wl%d: wlc_attach: wlc_eventq_attachfailed\n", unit));
1731 err = 57;
1732 goto fail;
1733 }
1734
1735 if ((wlc_stf_attach(wlc) != 0)) {
1736 WL_ERROR(("wl%d: wlc_attach: wlc_stf_attach failed\n", unit));
1737 err = 68;
1738 goto fail;
1739 }
1740 fail:
1741 return err;
1742}
1743
1744wlc_pub_t *wlc_pub(void *wlc)
1745{
1746 return ((wlc_info_t *) wlc)->pub;
1747}
1748
1749#define CHIP_SUPPORTS_11N(wlc) 1
1750
1751/*
1752 * The common driver entry routine. Error codes should be unique
1753 */
0d2f0724
GKH
1754void *wlc_attach(void *wl, u16 vendor, u16 device, uint unit, bool piomode,
1755 osl_t *osh, void *regsva, uint bustype, void *btparam,
1756 uint *perr)
1757{
a9533e7e
HP
1758 wlc_info_t *wlc;
1759 uint err = 0;
1760 uint j;
1761 wlc_pub_t *pub;
1762 wlc_txq_info_t *qi;
1763 uint n_disabled;
1764
1765 WL_NONE(("wl%d: %s: vendor 0x%x device 0x%x\n", unit, __func__, vendor,
1766 device));
1767
1768 ASSERT(WSEC_MAX_RCMTA_KEYS <= WSEC_MAX_KEYS);
1769 ASSERT(WSEC_MAX_DEFAULT_KEYS == WLC_DEFAULT_KEYS);
1770
1771 /* some code depends on packed structures */
1772 ASSERT(sizeof(struct ether_addr) == ETHER_ADDR_LEN);
1773 ASSERT(sizeof(struct ether_header) == ETHER_HDR_LEN);
1774 ASSERT(sizeof(d11regs_t) == SI_CORE_SIZE);
1775 ASSERT(sizeof(ofdm_phy_hdr_t) == D11_PHY_HDR_LEN);
1776 ASSERT(sizeof(cck_phy_hdr_t) == D11_PHY_HDR_LEN);
1777 ASSERT(sizeof(d11txh_t) == D11_TXH_LEN);
1778 ASSERT(sizeof(d11rxhdr_t) == RXHDR_LEN);
a9533e7e
HP
1779 ASSERT(sizeof(struct dot11_header) == DOT11_A4_HDR_LEN);
1780 ASSERT(sizeof(struct dot11_rts_frame) == DOT11_RTS_LEN);
a9533e7e 1781 ASSERT(sizeof(struct dot11_management_header) == DOT11_MGMT_HDR_LEN);
a9533e7e
HP
1782 ASSERT(sizeof(struct dot11_bcn_prb) == DOT11_BCN_PRB_LEN);
1783 ASSERT(sizeof(tx_status_t) == TXSTATUS_LEN);
a9533e7e 1784 ASSERT(sizeof(ht_cap_ie_t) == HT_CAP_IE_LEN);
eb816036 1785#ifdef BRCM_FULLMAC
ce0f1b8c 1786 ASSERT(offsetof(wl_scan_params_t, channel_list) ==
a9533e7e 1787 WL_SCAN_PARAMS_FIXED_SIZE);
eb816036 1788#endif
36c63ff6 1789 ASSERT(IS_ALIGNED(offsetof(wsec_key_t, data), sizeof(u32)));
a9533e7e
HP
1790 ASSERT(ISPOWEROF2(MA_WINDOW_SZ));
1791
1792 ASSERT(sizeof(wlc_d11rxhdr_t) <= WL_HWRXOFF);
1793
1794 /*
1795 * Number of replay counters value used in WPA IE must match # rxivs
1796 * supported in wsec_key_t struct. See 802.11i/D3.0 sect. 7.3.2.17
1797 * 'RSN Information Element' figure 8 for this mapping.
1798 */
1799 ASSERT((WPA_CAP_16_REPLAY_CNTRS == WLC_REPLAY_CNTRS_VALUE
1800 && 16 == WLC_NUMRXIVS)
1801 || (WPA_CAP_4_REPLAY_CNTRS == WLC_REPLAY_CNTRS_VALUE
1802 && 4 == WLC_NUMRXIVS));
1803
1804 /* allocate wlc_info_t state and its substructures */
ca8c1e59
JC
1805 wlc = (wlc_info_t *) wlc_attach_malloc(osh, unit, &err, device);
1806 if (wlc == NULL)
a9533e7e
HP
1807 goto fail;
1808 wlc->osh = osh;
1809 pub = wlc->pub;
1810
1811#if defined(BCMDBG)
1812 wlc_info_dbg = wlc;
1813#endif
1814
1815 wlc->band = wlc->bandstate[0];
1816 wlc->core = wlc->corestate;
1817 wlc->wl = wl;
1818 pub->unit = unit;
1819 pub->osh = osh;
1820 wlc->btparam = btparam;
1821 pub->_piomode = piomode;
0965ae88 1822 wlc->bandinit_pending = false;
a9533e7e
HP
1823 /* By default restrict TKIP associations from 11n STA's */
1824 wlc->ht_wsec_restriction = WLC_HT_TKIP_RESTRICT;
1825
1826 /* populate wlc_info_t with default values */
1827 wlc_info_init(wlc, unit);
1828
1829 /* update sta/ap related parameters */
1830 wlc_ap_upd(wlc);
1831
1832 /* 11n_disable nvram */
1833 n_disabled = getintvar(pub->vars, "11n_disable");
1834
1835 /* register a module (to handle iovars) */
1836 wlc_module_register(wlc->pub, wlc_iovars, "wlc_iovars", wlc,
1837 wlc_doiovar, NULL, NULL);
1838
1839 /* low level attach steps(all hw accesses go inside, no more in rest of the attach) */
1840 err = wlc_bmac_attach(wlc, vendor, device, unit, piomode, osh, regsva,
1841 bustype, btparam);
1842 if (err)
1843 goto fail;
1844
1845 /* for some states, due to different info pointer(e,g, wlc, wlc_hw) or master/slave split,
1846 * HIGH driver(both monolithic and HIGH_ONLY) needs to sync states FROM BMAC portion driver
1847 */
1848 if (!wlc_state_bmac_sync(wlc)) {
1849 err = 20;
1850 goto fail;
1851 }
1852
1853 pub->phy_11ncapable = WLC_PHY_11N_CAP(wlc->band);
1854
1855 /* propagate *vars* from BMAC driver to high driver */
1856 wlc_bmac_copyfrom_vars(wlc->hw, &pub->vars, &wlc->vars_size);
1857
1858#ifdef WLC_HIGH_ONLY
1859 WL_TRACE(("nvram : vars %p , vars_size %d\n", pub->vars,
1860 wlc->vars_size));
1861#endif
1862
1863 /* set maximum allowed duty cycle */
1864 wlc->tx_duty_cycle_ofdm =
7d4df48e 1865 (u16) getintvar(pub->vars, "tx_duty_cycle_ofdm");
a9533e7e 1866 wlc->tx_duty_cycle_cck =
7d4df48e 1867 (u16) getintvar(pub->vars, "tx_duty_cycle_cck");
a9533e7e
HP
1868
1869 wlc_stf_phy_chain_calc(wlc);
1870
1871 /* txchain 1: txant 0, txchain 2: txant 1 */
1872 if (WLCISNPHY(wlc->band) && (wlc->stf->txstreams == 1))
1873 wlc->stf->txant = wlc->stf->hw_txchain - 1;
1874
1875 /* push to BMAC driver */
1876 wlc_phy_stf_chain_init(wlc->band->pi, wlc->stf->hw_txchain,
1877 wlc->stf->hw_rxchain);
1878
1879#ifdef WLC_LOW
1880 /* pull up some info resulting from the low attach */
1881 {
1882 int i;
1883 for (i = 0; i < NFIFO; i++)
1884 wlc->core->txavail[i] = wlc->hw->txavail[i];
1885 }
1886#endif /* WLC_LOW */
1887
1888 wlc_bmac_hw_etheraddr(wlc->hw, &wlc->perm_etheraddr);
1889
1890 bcopy((char *)&wlc->perm_etheraddr, (char *)&pub->cur_etheraddr,
1891 ETHER_ADDR_LEN);
1892
1893 for (j = 0; j < NBANDS(wlc); j++) {
1894 /* Use band 1 for single band 11a */
1895 if (IS_SINGLEBAND_5G(wlc->deviceid))
1896 j = BAND_5G_INDEX;
1897
1898 wlc->band = wlc->bandstate[j];
1899
1900 if (!wlc_attach_stf_ant_init(wlc)) {
1901 err = 24;
1902 goto fail;
1903 }
1904
1905 /* default contention windows size limits */
1906 wlc->band->CWmin = APHY_CWMIN;
1907 wlc->band->CWmax = PHY_CWMAX;
1908
1909 /* init gmode value */
1910 if (BAND_2G(wlc->band->bandtype)) {
1911 wlc->band->gmode = GMODE_AUTO;
1912 wlc_protection_upd(wlc, WLC_PROT_G_USER,
1913 wlc->band->gmode);
1914 }
1915
1916 /* init _n_enab supported mode */
1917 if (WLC_PHY_11N_CAP(wlc->band) && CHIP_SUPPORTS_11N(wlc)) {
1918 if (n_disabled & WLFEATURE_DISABLE_11N) {
1919 pub->_n_enab = OFF;
1920 wlc_protection_upd(wlc, WLC_PROT_N_USER, OFF);
1921 } else {
1922 pub->_n_enab = SUPPORT_11N;
1923 wlc_protection_upd(wlc, WLC_PROT_N_USER,
1924 ((pub->_n_enab ==
1925 SUPPORT_11N) ? WL_11N_2x2 :
1926 WL_11N_3x3));
1927 }
1928 }
1929
1930 /* init per-band default rateset, depend on band->gmode */
1931 wlc_default_rateset(wlc, &wlc->band->defrateset);
1932
1933 /* fill in hw_rateset (used early by WLC_SET_RATESET) */
1934 wlc_rateset_filter(&wlc->band->defrateset,
0965ae88 1935 &wlc->band->hw_rateset, false,
a9533e7e
HP
1936 WLC_RATES_CCK_OFDM, RATE_MASK,
1937 (bool) N_ENAB(wlc->pub));
1938 }
1939
1940 /* update antenna config due to wlc->stf->txant/txchain/ant_rx_ovr change */
1941 wlc_stf_phy_txant_upd(wlc);
1942
1943 /* attach each modules */
1944 err = wlc_attach_module(wlc);
1945 if (err != 0)
1946 goto fail;
1947
1948 if (!wlc_timers_init(wlc, unit)) {
1949 WL_ERROR(("wl%d: %s: wlc_init_timer failed\n", unit, __func__));
1950 err = 32;
1951 goto fail;
1952 }
1953
1954 /* depend on rateset, gmode */
1955 wlc->cmi = wlc_channel_mgr_attach(wlc);
1956 if (!wlc->cmi) {
1957 WL_ERROR(("wl%d: %s: wlc_channel_mgr_attach failed\n", unit,
1958 __func__));
1959 err = 33;
1960 goto fail;
1961 }
1962
1963 /* init default when all parameters are ready, i.e. ->rateset */
1964 wlc_bss_default_init(wlc);
1965
1966 /*
1967 * Complete the wlc default state initializations..
1968 */
1969
1970 /* allocate our initial queue */
1971 qi = wlc_txq_alloc(wlc, osh);
1972 if (qi == NULL) {
1973 WL_ERROR(("wl%d: %s: failed to malloc tx queue\n", unit,
1974 __func__));
1975 err = 100;
1976 goto fail;
1977 }
1978 wlc->active_queue = qi;
1979
1980 wlc->bsscfg[0] = wlc->cfg;
1981 wlc->cfg->_idx = 0;
1982 wlc->cfg->wlc = wlc;
1983 pub->txmaxpkts = MAXTXPKTS;
1984
1985 WLCNTSET(pub->_cnt->version, WL_CNT_T_VERSION);
1986 WLCNTSET(pub->_cnt->length, sizeof(wl_cnt_t));
1987
1988 WLCNTSET(pub->_wme_cnt->version, WL_WME_CNT_VERSION);
1989 WLCNTSET(pub->_wme_cnt->length, sizeof(wl_wme_cnt_t));
1990
1991 wlc_wme_initparams_sta(wlc, &wlc->wme_param_ie);
1992
1993 wlc->mimoft = FT_HT;
1994 wlc->ht_cap.cap = HT_CAP;
1995 if (HT_ENAB(wlc->pub))
1996 wlc->stf->ldpc = AUTO;
1997
1998 wlc->mimo_40txbw = AUTO;
1999 wlc->ofdm_40txbw = AUTO;
2000 wlc->cck_40txbw = AUTO;
2001 wlc_update_mimo_band_bwcap(wlc, WLC_N_BW_20IN2G_40IN5G);
2002
2003 /* Enable setting the RIFS Mode bit by default in HT Info IE */
2004 wlc->rifs_advert = AUTO;
2005
2006 /* Set default values of SGI */
2007 if (WLC_SGI_CAP_PHY(wlc)) {
2008 wlc_ht_update_sgi_rx(wlc, (WLC_N_SGI_20 | WLC_N_SGI_40));
2009 wlc->sgi_tx = AUTO;
2010 } else if (WLCISSSLPNPHY(wlc->band)) {
2011 wlc_ht_update_sgi_rx(wlc, (WLC_N_SGI_20 | WLC_N_SGI_40));
2012 wlc->sgi_tx = AUTO;
2013 } else {
2014 wlc_ht_update_sgi_rx(wlc, 0);
2015 wlc->sgi_tx = OFF;
2016 }
2017
2018 /* *******nvram 11n config overrides Start ********* */
2019
2020 /* apply the sgi override from nvram conf */
2021 if (n_disabled & WLFEATURE_DISABLE_11N_SGI_TX)
2022 wlc->sgi_tx = OFF;
2023
2024 if (n_disabled & WLFEATURE_DISABLE_11N_SGI_RX)
2025 wlc_ht_update_sgi_rx(wlc, 0);
2026
2027 /* apply the stbc override from nvram conf */
2028 if (n_disabled & WLFEATURE_DISABLE_11N_STBC_TX) {
2029 wlc->bandstate[BAND_2G_INDEX]->band_stf_stbc_tx = OFF;
2030 wlc->bandstate[BAND_5G_INDEX]->band_stf_stbc_tx = OFF;
2031 wlc->ht_cap.cap &= ~HT_CAP_TX_STBC;
2032 }
2033 if (n_disabled & WLFEATURE_DISABLE_11N_STBC_RX)
2034 wlc_stf_stbc_rx_set(wlc, HT_CAP_RX_STBC_NO);
2035
2036 /* apply the GF override from nvram conf */
2037 if (n_disabled & WLFEATURE_DISABLE_11N_GF)
2038 wlc->ht_cap.cap &= ~HT_CAP_GF;
2039
2040 /* initialize radio_mpc_disable according to wlc->mpc */
2041 wlc_radio_mpc_upd(wlc);
2042
2043 if (WLANTSEL_ENAB(wlc)) {
2044 if ((CHIPID(wlc->pub->sih->chip)) == BCM43235_CHIP_ID) {
2045 if ((getintvar(wlc->pub->vars, "aa2g") == 7) ||
2046 (getintvar(wlc->pub->vars, "aa5g") == 7)) {
2047 wlc_bmac_antsel_set(wlc->hw, 1);
2048 }
2049 } else {
2050 wlc_bmac_antsel_set(wlc->hw, wlc->asi->antsel_avail);
2051 }
2052 }
2053
2054 if (perr)
2055 *perr = 0;
2056
90ea2296 2057 return (void *)wlc;
a9533e7e
HP
2058
2059 fail:
2060 WL_ERROR(("wl%d: %s: failed with err %d\n", unit, __func__, err));
2061 if (wlc)
2062 wlc_detach(wlc);
2063
2064 if (perr)
2065 *perr = err;
90ea2296 2066 return NULL;
a9533e7e
HP
2067}
2068
a9d0fffa 2069static void wlc_attach_antgain_init(wlc_info_t *wlc)
a2627bc0 2070{
a9533e7e
HP
2071 uint unit;
2072 unit = wlc->pub->unit;
2073
2074 if ((wlc->band->antgain == -1) && (wlc->pub->sromrev == 1)) {
2075 /* default antenna gain for srom rev 1 is 2 dBm (8 qdbm) */
2076 wlc->band->antgain = 8;
2077 } else if (wlc->band->antgain == -1) {
2078 WL_ERROR(("wl%d: %s: Invalid antennas available in srom, using 2dB\n", unit, __func__));
2079 wlc->band->antgain = 8;
2080 } else {
562c8850 2081 s8 gain, fract;
a9533e7e
HP
2082 /* Older sroms specified gain in whole dbm only. In order
2083 * be able to specify qdbm granularity and remain backward compatible
2084 * the whole dbms are now encoded in only low 6 bits and remaining qdbms
2085 * are encoded in the hi 2 bits. 6 bit signed number ranges from
2086 * -32 - 31. Examples: 0x1 = 1 db,
2087 * 0xc1 = 1.75 db (1 + 3 quarters),
2088 * 0x3f = -1 (-1 + 0 quarters),
2089 * 0x7f = -.75 (-1 in low 6 bits + 1 quarters in hi 2 bits) = -3 qdbm.
2090 * 0xbf = -.50 (-1 in low 6 bits + 2 quarters in hi 2 bits) = -2 qdbm.
2091 */
2092 gain = wlc->band->antgain & 0x3f;
2093 gain <<= 2; /* Sign extend */
2094 gain >>= 2;
2095 fract = (wlc->band->antgain & 0xc0) >> 6;
2096 wlc->band->antgain = 4 * gain + fract;
2097 }
2098}
2099
0d2f0724 2100static bool wlc_attach_stf_ant_init(wlc_info_t *wlc)
a2627bc0 2101{
a9533e7e
HP
2102 int aa;
2103 uint unit;
2104 char *vars;
2105 int bandtype;
2106
2107 unit = wlc->pub->unit;
2108 vars = wlc->pub->vars;
2109 bandtype = wlc->band->bandtype;
2110
2111 /* get antennas available */
562c8850 2112 aa = (s8) getintvar(vars, (BAND_5G(bandtype) ? "aa5g" : "aa2g"));
a9533e7e 2113 if (aa == 0)
562c8850 2114 aa = (s8) getintvar(vars,
a9533e7e
HP
2115 (BAND_5G(bandtype) ? "aa1" : "aa0"));
2116 if ((aa < 1) || (aa > 15)) {
2117 WL_ERROR(("wl%d: %s: Invalid antennas available in srom (0x%x), using 3.\n", unit, __func__, aa));
2118 aa = 3;
2119 }
2120
2121 /* reset the defaults if we have a single antenna */
2122 if (aa == 1) {
2123 wlc->stf->ant_rx_ovr = ANT_RX_DIV_FORCE_0;
2124 wlc->stf->txant = ANT_TX_FORCE_0;
2125 } else if (aa == 2) {
2126 wlc->stf->ant_rx_ovr = ANT_RX_DIV_FORCE_1;
2127 wlc->stf->txant = ANT_TX_FORCE_1;
2128 } else {
2129 }
2130
2131 /* Compute Antenna Gain */
2132 wlc->band->antgain =
562c8850 2133 (s8) getintvar(vars, (BAND_5G(bandtype) ? "ag1" : "ag0"));
a9533e7e
HP
2134 wlc_attach_antgain_init(wlc);
2135
0f0881b0 2136 return true;
a9533e7e
HP
2137}
2138
2139#ifdef WLC_HIGH_ONLY
2140/* HIGH_ONLY bmac_attach, which sync over LOW_ONLY bmac_attach states */
0d2f0724
GKH
2141int wlc_bmac_attach(wlc_info_t *wlc, u16 vendor, u16 device, uint unit,
2142 bool piomode, osl_t *osh, void *regsva, uint bustype,
2143 void *btparam)
2144{
a9533e7e
HP
2145 wlc_bmac_revinfo_t revinfo;
2146 uint idx = 0;
2147 rpc_info_t *rpc = (rpc_info_t *) btparam;
2148
2149 ASSERT(bustype == RPC_BUS);
2150
2151 /* install the rpc handle in the various state structures used by stub RPC functions */
2152 wlc->rpc = rpc;
2153 wlc->hw->rpc = rpc;
2154 wlc->hw->osh = osh;
2155
2156 wlc->regs = 0;
2157
ca8c1e59
JC
2158 wlc->rpctx = wlc_rpctx_attach(wlc->pub, wlc);
2159 if (wlc->rpctx == NULL)
a9533e7e
HP
2160 return -1;
2161
2162 /*
2163 * FIFO 0
2164 * TX: TX_AC_BK_FIFO (TX AC Background data packets)
2165 */
2166 /* Always initialized */
2167 ASSERT(NRPCTXBUFPOST <= NTXD);
2168 wlc_rpctx_fifoinit(wlc->rpctx, TX_DATA_FIFO, NRPCTXBUFPOST);
2169 wlc_rpctx_fifoinit(wlc->rpctx, TX_CTL_FIFO, NRPCTXBUFPOST);
2170 wlc_rpctx_fifoinit(wlc->rpctx, TX_BCMC_FIFO, NRPCTXBUFPOST);
2171
2172 /* VI and BK inited only if WME */
2173 if (WME_ENAB(wlc->pub)) {
2174 wlc_rpctx_fifoinit(wlc->rpctx, TX_AC_BK_FIFO, NRPCTXBUFPOST);
2175 wlc_rpctx_fifoinit(wlc->rpctx, TX_AC_VI_FIFO, NRPCTXBUFPOST);
2176 }
2177
2178 /* Allocate SB handle */
2179 wlc->pub->sih = osl_malloc(wlc->osh, sizeof(si_t));
2180 if (!wlc->pub->sih)
2181 return -1;
2182 bzero(wlc->pub->sih, sizeof(si_t));
2183
2184 /* sync up revinfo with BMAC */
2185 bzero(&revinfo, sizeof(wlc_bmac_revinfo_t));
2186 if (wlc_bmac_revinfo_get(wlc->hw, &revinfo) != 0)
2187 return -1;
7d4df48e
GKH
2188 wlc->vendorid = (u16) revinfo.vendorid;
2189 wlc->deviceid = (u16) revinfo.deviceid;
a9533e7e 2190
7d4df48e 2191 wlc->pub->boardrev = (u16) revinfo.boardrev;
a9533e7e 2192 wlc->pub->corerev = revinfo.corerev;
41feb5ed 2193 wlc->pub->sromrev = (u8) revinfo.sromrev;
a9533e7e
HP
2194 wlc->pub->sih->chiprev = revinfo.chiprev;
2195 wlc->pub->sih->chip = revinfo.chip;
2196 wlc->pub->sih->chippkg = revinfo.chippkg;
2197 wlc->pub->sih->boardtype = revinfo.boardtype;
2198 wlc->pub->sih->boardvendor = revinfo.boardvendor;
2199 wlc->pub->sih->bustype = revinfo.bustype;
2200 wlc->pub->sih->buscoretype = revinfo.buscoretype;
2201 wlc->pub->sih->buscorerev = revinfo.buscorerev;
2202 wlc->pub->sih->issim = (bool) revinfo.issim;
2203 wlc->pub->sih->rpc = rpc;
2204
2205 if (revinfo.nbands == 0 || revinfo.nbands > 2)
2206 return -1;
2207 wlc->pub->_nbands = revinfo.nbands;
2208
2209 for (idx = 0; idx < wlc->pub->_nbands; idx++) {
2210 uint bandunit, bandtype; /* To access bandstate */
2211 wlc_phy_t *pi = osl_malloc(wlc->osh, sizeof(wlc_phy_t));
2212
2213 if (!pi)
2214 return -1;
2215 bzero(pi, sizeof(wlc_phy_t));
2216 pi->rpc = rpc;
2217
2218 bandunit = revinfo.band[idx].bandunit;
2219 bandtype = revinfo.band[idx].bandtype;
2220 wlc->bandstate[bandunit]->radiorev =
41feb5ed 2221 (u8) revinfo.band[idx].radiorev;
a9533e7e 2222 wlc->bandstate[bandunit]->phytype =
7d4df48e 2223 (u16) revinfo.band[idx].phytype;
a9533e7e 2224 wlc->bandstate[bandunit]->phyrev =
7d4df48e 2225 (u16) revinfo.band[idx].phyrev;
a9533e7e 2226 wlc->bandstate[bandunit]->radioid =
7d4df48e 2227 (u16) revinfo.band[idx].radioid;
a9533e7e
HP
2228 wlc->bandstate[bandunit]->abgphy_encore =
2229 revinfo.band[idx].abgphy_encore;
2230
2231 wlc->bandstate[bandunit]->pi = pi;
2232 wlc->bandstate[bandunit]->bandunit = bandunit;
2233 wlc->bandstate[bandunit]->bandtype = bandtype;
2234 }
2235
2236 /* misc stuff */
2237
2238 return 0;
2239}
2240
2241/* Free the convenience handles */
7cc4a4c0 2242int wlc_bmac_detach(wlc_info_t *wlc)
a9533e7e
HP
2243{
2244 uint idx;
2245
2246 if (wlc->pub->sih) {
2247 osl_mfree(wlc->osh, (void *)wlc->pub->sih, sizeof(si_t));
2248 wlc->pub->sih = NULL;
2249 }
2250
2251 for (idx = 0; idx < MAXBANDS; idx++)
2252 if (wlc->bandstate[idx]->pi) {
182acb3c 2253 kfree(wlc->bandstate[idx]->pi);
a9533e7e
HP
2254 wlc->bandstate[idx]->pi = NULL;
2255 }
2256
2257 if (wlc->rpctx) {
2258 wlc_rpctx_detach(wlc->rpctx);
2259 wlc->rpctx = NULL;
2260 }
2261
2262 return 0;
2263
2264}
2265
2266#endif /* WLC_HIGH_ONLY */
2267
0d2f0724 2268static void wlc_timers_deinit(wlc_info_t *wlc)
a2627bc0 2269{
a9533e7e
HP
2270 /* free timer state */
2271 if (wlc->wdtimer) {
2272 wl_free_timer(wlc->wl, wlc->wdtimer);
2273 wlc->wdtimer = NULL;
2274 }
2275 if (wlc->radio_timer) {
2276 wl_free_timer(wlc->wl, wlc->radio_timer);
2277 wlc->radio_timer = NULL;
2278 }
2279}
2280
0d2f0724 2281static void wlc_detach_module(wlc_info_t *wlc)
a2627bc0 2282{
a9533e7e
HP
2283 if (wlc->asi) {
2284 wlc_antsel_detach(wlc->asi);
2285 wlc->asi = NULL;
2286 }
2287
2288 if (wlc->ampdu) {
2289 wlc_ampdu_detach(wlc->ampdu);
2290 wlc->ampdu = NULL;
2291 }
2292
2293 wlc_stf_detach(wlc);
2294}
2295
2296/*
2297 * Return a count of the number of driver callbacks still pending.
2298 *
2299 * General policy is that wlc_detach can only dealloc/free software states. It can NOT
2300 * touch hardware registers since the d11core may be in reset and clock may not be available.
2301 * One exception is sb register access, which is possible if crystal is turned on
2302 * After "down" state, driver should avoid software timer with the exception of radio_monitor.
2303 */
0d2f0724 2304uint wlc_detach(wlc_info_t *wlc)
a2627bc0 2305{
a9533e7e
HP
2306 uint i;
2307 uint callbacks = 0;
2308
2309 if (wlc == NULL)
2310 return 0;
2311
2312 WL_TRACE(("wl%d: %s\n", wlc->pub->unit, __func__));
2313
2314 ASSERT(!wlc->pub->up);
2315
2316 callbacks += wlc_bmac_detach(wlc);
2317
2318 /* delete software timers */
2319 if (!wlc_radio_monitor_stop(wlc))
2320 callbacks++;
2321
2322 if (wlc->eventq) {
2323 wlc_eventq_detach(wlc->eventq);
2324 wlc->eventq = NULL;
2325 }
2326
2327 wlc_channel_mgr_detach(wlc->cmi);
2328
2329 wlc_timers_deinit(wlc);
2330
2331 wlc_detach_module(wlc);
2332
2333 /* free other state */
2334
2335#ifdef WLC_HIGH_ONLY
2336 /* High-Only driver has an allocated copy of vars, monolithic just
2337 * references the wlc->hw->vars which is freed in wlc_bmac_detach()
2338 */
2339 if (wlc->pub->vars) {
182acb3c 2340 kfree(wlc->pub->vars);
a9533e7e
HP
2341 wlc->pub->vars = NULL;
2342 }
2343#endif
2344
2345#ifdef BCMDBG
2346 if (wlc->country_ie_override) {
182acb3c 2347 kfree(wlc->country_ie_override);
a9533e7e
HP
2348 wlc->country_ie_override = NULL;
2349 }
2350#endif /* BCMDBG */
2351
2352 {
2353 /* free dumpcb list */
2354 dumpcb_t *prev, *ptr;
2355 prev = ptr = wlc->dumpcb_head;
2356 while (ptr) {
2357 ptr = prev->next;
182acb3c 2358 kfree(prev);
a9533e7e
HP
2359 prev = ptr;
2360 }
2361 wlc->dumpcb_head = NULL;
2362 }
2363
2364 /* Detach from iovar manager */
2365 wlc_module_unregister(wlc->pub, "wlc_iovars", wlc);
2366
2367 /*
2368 if (wlc->ap) {
2369 wlc_ap_detach(wlc->ap);
2370 wlc->ap = NULL;
2371 }
2372 */
2373
2374 while (wlc->tx_queues != NULL) {
2375 wlc_txq_free(wlc, wlc->osh, wlc->tx_queues);
2376 }
2377
2378 /*
2379 * consistency check: wlc_module_register/wlc_module_unregister calls
2380 * should match therefore nothing should be left here.
2381 */
2382 for (i = 0; i < WLC_MAXMODULES; i++)
2383 ASSERT(wlc->modulecb[i].name[0] == '\0');
2384
2385 wlc_detach_mfree(wlc, wlc->osh);
90ea2296 2386 return callbacks;
a9533e7e
HP
2387}
2388
2389/* update state that depends on the current value of "ap" */
7cc4a4c0 2390void wlc_ap_upd(wlc_info_t *wlc)
a9533e7e
HP
2391{
2392 if (AP_ENAB(wlc->pub))
2393 wlc->PLCPHdr_override = WLC_PLCP_AUTO; /* AP: short not allowed, but not enforced */
2394 else
2395 wlc->PLCPHdr_override = WLC_PLCP_SHORT; /* STA-BSS; short capable */
2396
2397 /* disable vlan_mode on AP since some legacy STAs cannot rx tagged pkts */
2398 wlc->vlan_mode = AP_ENAB(wlc->pub) ? OFF : AUTO;
2399
2400 /* fixup mpc */
0f0881b0 2401 wlc->mpc = true;
a9533e7e
HP
2402}
2403
2404/* read hwdisable state and propagate to wlc flag */
7cc4a4c0 2405static void wlc_radio_hwdisable_upd(wlc_info_t *wlc)
a9533e7e
HP
2406{
2407 if (wlc->pub->wlfeatureflag & WL_SWFL_NOHWRADIO || wlc->pub->hw_off)
2408 return;
2409
2410 if (wlc_bmac_radio_read_hwdisabled(wlc->hw)) {
2411 mboolset(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE);
2412 } else {
2413 mboolclr(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE);
2414 }
2415}
2416
0965ae88 2417/* return true if Minimum Power Consumption should be entered, false otherwise */
7cc4a4c0 2418bool wlc_is_non_delay_mpc(wlc_info_t *wlc)
a9533e7e 2419{
0965ae88 2420 return false;
a9533e7e
HP
2421}
2422
7cc4a4c0 2423bool wlc_ismpc(wlc_info_t *wlc)
a9533e7e 2424{
90ea2296 2425 return (wlc->mpc_delay_off == 0) && (wlc_is_non_delay_mpc(wlc));
a9533e7e
HP
2426}
2427
7cc4a4c0 2428void wlc_radio_mpc_upd(wlc_info_t *wlc)
a9533e7e
HP
2429{
2430 bool mpc_radio, radio_state;
2431
2432 /*
2433 * Clear the WL_RADIO_MPC_DISABLE bit when mpc feature is disabled
2434 * in case the WL_RADIO_MPC_DISABLE bit was set. Stop the radio
2435 * monitor also when WL_RADIO_MPC_DISABLE is the only reason that
2436 * the radio is going down.
2437 */
2438 if (!wlc->mpc) {
2439 if (!wlc->pub->radio_disabled)
2440 return;
2441 mboolclr(wlc->pub->radio_disabled, WL_RADIO_MPC_DISABLE);
2442 wlc_radio_upd(wlc);
2443 if (!wlc->pub->radio_disabled)
2444 wlc_radio_monitor_stop(wlc);
2445 return;
2446 }
2447
2448 /*
2449 * sync ismpc logic with WL_RADIO_MPC_DISABLE bit in wlc->pub->radio_disabled
2450 * to go ON, always call radio_upd synchronously
2451 * to go OFF, postpone radio_upd to later when context is safe(e.g. watchdog)
2452 */
2453 radio_state =
2454 (mboolisset(wlc->pub->radio_disabled, WL_RADIO_MPC_DISABLE) ? OFF :
2455 ON);
0f0881b0 2456 mpc_radio = (wlc_ismpc(wlc) == true) ? OFF : ON;
a9533e7e
HP
2457
2458 if (radio_state == ON && mpc_radio == OFF)
2459 wlc->mpc_delay_off = wlc->mpc_dlycnt;
2460 else if (radio_state == OFF && mpc_radio == ON) {
2461 mboolclr(wlc->pub->radio_disabled, WL_RADIO_MPC_DISABLE);
2462 wlc_radio_upd(wlc);
2463 if (wlc->mpc_offcnt < WLC_MPC_THRESHOLD) {
2464 wlc->mpc_dlycnt = WLC_MPC_MAX_DELAYCNT;
2465 } else
2466 wlc->mpc_dlycnt = WLC_MPC_MIN_DELAYCNT;
2467 wlc->mpc_dur += OSL_SYSUPTIME() - wlc->mpc_laston_ts;
2468 }
2469 /* Below logic is meant to capture the transition from mpc off to mpc on for reasons
2470 * other than wlc->mpc_delay_off keeping the mpc off. In that case reset
2471 * wlc->mpc_delay_off to wlc->mpc_dlycnt, so that we restart the countdown of mpc_delay_off
2472 */
0965ae88 2473 if ((wlc->prev_non_delay_mpc == false) &&
0f0881b0 2474 (wlc_is_non_delay_mpc(wlc) == true) && wlc->mpc_delay_off) {
a9533e7e
HP
2475 wlc->mpc_delay_off = wlc->mpc_dlycnt;
2476 }
2477 wlc->prev_non_delay_mpc = wlc_is_non_delay_mpc(wlc);
2478}
2479
2480/*
2481 * centralized radio disable/enable function,
2482 * invoke radio enable/disable after updating hwradio status
2483 */
7cc4a4c0 2484static void wlc_radio_upd(wlc_info_t *wlc)
a9533e7e
HP
2485{
2486 if (wlc->pub->radio_disabled)
2487 wlc_radio_disable(wlc);
2488 else
2489 wlc_radio_enable(wlc);
2490}
2491
2492/* maintain LED behavior in down state */
7cc4a4c0 2493static void wlc_down_led_upd(wlc_info_t *wlc)
a9533e7e
HP
2494{
2495 ASSERT(!wlc->pub->up);
2496
2497 /* maintain LEDs while in down state, turn on sbclk if not available yet */
2498 /* turn on sbclk if necessary */
2499 if (!AP_ENAB(wlc->pub)) {
0f0881b0 2500 wlc_pllreq(wlc, true, WLC_PLLREQ_FLIP);
a9533e7e 2501
0965ae88 2502 wlc_pllreq(wlc, false, WLC_PLLREQ_FLIP);
a9533e7e
HP
2503 }
2504}
2505
7cc4a4c0 2506void wlc_radio_disable(wlc_info_t *wlc)
a9533e7e
HP
2507{
2508 if (!wlc->pub->up) {
2509 wlc_down_led_upd(wlc);
2510 return;
2511 }
2512
2513 wlc_radio_monitor_start(wlc);
2514 wl_down(wlc->wl);
2515}
2516
7cc4a4c0 2517static void wlc_radio_enable(wlc_info_t *wlc)
a9533e7e
HP
2518{
2519 if (wlc->pub->up)
2520 return;
2521
2522 if (DEVICEREMOVED(wlc))
2523 return;
2524
2525 if (!wlc->down_override) { /* imposed by wl down/out ioctl */
2526 wl_up(wlc->wl);
2527 }
2528}
2529
2530/* periodical query hw radio button while driver is "down" */
2531static void wlc_radio_timer(void *arg)
2532{
2533 wlc_info_t *wlc = (wlc_info_t *) arg;
2534
2535 if (DEVICEREMOVED(wlc)) {
2536 WL_ERROR(("wl%d: %s: dead chip\n", wlc->pub->unit, __func__));
2537 wl_down(wlc->wl);
2538 return;
2539 }
2540
2541 /* cap mpc off count */
2542 if (wlc->mpc_offcnt < WLC_MPC_MAX_DELAYCNT)
2543 wlc->mpc_offcnt++;
2544
2545 /* validate all the reasons driver could be down and running this radio_timer */
2546 ASSERT(wlc->pub->radio_disabled || wlc->down_override);
2547 wlc_radio_hwdisable_upd(wlc);
2548 wlc_radio_upd(wlc);
2549}
2550
7cc4a4c0 2551static bool wlc_radio_monitor_start(wlc_info_t *wlc)
a9533e7e
HP
2552{
2553 /* Don't start the timer if HWRADIO feature is disabled */
2554 if (wlc->radio_monitor || (wlc->pub->wlfeatureflag & WL_SWFL_NOHWRADIO))
0f0881b0 2555 return true;
a9533e7e 2556
0f0881b0
GKH
2557 wlc->radio_monitor = true;
2558 wlc_pllreq(wlc, true, WLC_PLLREQ_RADIO_MON);
2559 wl_add_timer(wlc->wl, wlc->radio_timer, TIMER_INTERVAL_RADIOCHK, true);
2560 return true;
a9533e7e
HP
2561}
2562
7cc4a4c0 2563bool wlc_radio_monitor_stop(wlc_info_t *wlc)
a9533e7e
HP
2564{
2565 if (!wlc->radio_monitor)
0f0881b0 2566 return true;
a9533e7e
HP
2567
2568 ASSERT((wlc->pub->wlfeatureflag & WL_SWFL_NOHWRADIO) !=
2569 WL_SWFL_NOHWRADIO);
2570
0965ae88
GKH
2571 wlc->radio_monitor = false;
2572 wlc_pllreq(wlc, false, WLC_PLLREQ_RADIO_MON);
90ea2296 2573 return wl_del_timer(wlc->wl, wlc->radio_timer);
a9533e7e
HP
2574}
2575
2576/* bring the driver down, but don't reset hardware */
7cc4a4c0 2577void wlc_out(wlc_info_t *wlc)
a9533e7e 2578{
0f0881b0 2579 wlc_bmac_set_noreset(wlc->hw, true);
a9533e7e
HP
2580 wlc_radio_upd(wlc);
2581 wl_down(wlc->wl);
0965ae88 2582 wlc_bmac_set_noreset(wlc->hw, false);
a9533e7e 2583
0f0881b0
GKH
2584 /* core clk is true in BMAC driver due to noreset, need to mirror it in HIGH */
2585 wlc->clk = true;
a9533e7e
HP
2586
2587 /* This will make sure that when 'up' is done
2588 * after 'out' it'll restore hardware (especially gpios)
2589 */
0965ae88 2590 wlc->pub->hw_up = false;
a9533e7e
HP
2591}
2592
2593#if defined(BCMDBG)
2594/* Verify the sanity of wlc->tx_prec_map. This can be done only by making sure that
2595 * if there is no packet pending for the FIFO, then the corresponding prec bits should be set
2596 * in prec_map. Of course, ignore this rule when block_datafifo is set
2597 */
7cc4a4c0 2598static bool wlc_tx_prec_map_verify(wlc_info_t *wlc)
a9533e7e
HP
2599{
2600 /* For non-WME, both fifos have overlapping prec_map. So it's an error only if both
2601 * fail the check.
2602 */
2603 if (!EDCF_ENAB(wlc->pub)) {
2604 if (!(WLC_TX_FIFO_CHECK(wlc, TX_DATA_FIFO) ||
2605 WLC_TX_FIFO_CHECK(wlc, TX_CTL_FIFO)))
0965ae88 2606 return false;
a9533e7e 2607 else
0f0881b0 2608 return true;
a9533e7e
HP
2609 }
2610
90ea2296 2611 return WLC_TX_FIFO_CHECK(wlc, TX_AC_BK_FIFO)
a9533e7e
HP
2612 && WLC_TX_FIFO_CHECK(wlc, TX_AC_BE_FIFO)
2613 && WLC_TX_FIFO_CHECK(wlc, TX_AC_VI_FIFO)
90ea2296 2614 && WLC_TX_FIFO_CHECK(wlc, TX_AC_VO_FIFO);
a9533e7e
HP
2615}
2616#endif /* BCMDBG */
2617
2618static void wlc_watchdog_by_timer(void *arg)
2619{
2620 wlc_info_t *wlc = (wlc_info_t *) arg;
2621 wlc_watchdog(arg);
2622 if (WLC_WATCHDOG_TBTT(wlc)) {
2623 /* set to normal osl watchdog period */
2624 wl_del_timer(wlc->wl, wlc->wdtimer);
2625 wl_add_timer(wlc->wl, wlc->wdtimer, TIMER_INTERVAL_WATCHDOG,
0f0881b0 2626 true);
a9533e7e
HP
2627 }
2628}
2629
2630/* common watchdog code */
2631static void wlc_watchdog(void *arg)
2632{
2633 wlc_info_t *wlc = (wlc_info_t *) arg;
2634 int i;
2635 wlc_bsscfg_t *cfg;
2636
2637 WL_TRACE(("wl%d: wlc_watchdog\n", wlc->pub->unit));
2638
2639 if (!wlc->pub->up)
2640 return;
2641
2642 if (DEVICEREMOVED(wlc)) {
2643 WL_ERROR(("wl%d: %s: dead chip\n", wlc->pub->unit, __func__));
2644 wl_down(wlc->wl);
2645 return;
2646 }
2647
2648 /* increment second count */
2649 wlc->pub->now++;
2650
2651 /* delay radio disable */
2652 if (wlc->mpc_delay_off) {
2653 if (--wlc->mpc_delay_off == 0) {
2654 mboolset(wlc->pub->radio_disabled,
2655 WL_RADIO_MPC_DISABLE);
2656 if (wlc->mpc && wlc_ismpc(wlc))
2657 wlc->mpc_offcnt = 0;
2658 wlc->mpc_laston_ts = OSL_SYSUPTIME();
2659 }
2660 }
2661
2662 /* mpc sync */
2663 wlc_radio_mpc_upd(wlc);
2664 /* radio sync: sw/hw/mpc --> radio_disable/radio_enable */
2665 wlc_radio_hwdisable_upd(wlc);
2666 wlc_radio_upd(wlc);
2667 /* if ismpc, driver should be in down state if up/down is allowed */
2668 if (wlc->mpc && wlc_ismpc(wlc))
2669 ASSERT(!wlc->pub->up);
2670 /* if radio is disable, driver may be down, quit here */
2671 if (wlc->pub->radio_disabled)
2672 return;
2673
2674#ifdef WLC_LOW
2675 wlc_bmac_watchdog(wlc);
2676#endif
2677#ifdef WLC_HIGH_ONLY
2678 /* maintenance */
2679 wlc_bmac_rpc_watchdog(wlc);
2680#endif
2681
2682 /* occasionally sample mac stat counters to detect 16-bit counter wrap */
2683 if ((WLC_UPDATE_STATS(wlc))
2684 && (!(wlc->pub->now % SW_TIMER_MAC_STAT_UPD)))
2685 wlc_statsupd(wlc);
2686
2687 /* Manage TKIP countermeasures timers */
2688 FOREACH_BSS(wlc, i, cfg) {
2689 if (cfg->tk_cm_dt) {
2690 cfg->tk_cm_dt--;
2691 }
2692 if (cfg->tk_cm_bt) {
2693 cfg->tk_cm_bt--;
2694 }
2695 }
2696
2697 /* Call any registered watchdog handlers */
2698 for (i = 0; i < WLC_MAXMODULES; i++) {
2699 if (wlc->modulecb[i].watchdog_fn)
2700 wlc->modulecb[i].watchdog_fn(wlc->modulecb[i].hdl);
2701 }
2702
2703 if (WLCISNPHY(wlc->band) && !wlc->pub->tempsense_disable &&
2704 ((wlc->pub->now - wlc->tempsense_lasttime) >=
2705 WLC_TEMPSENSE_PERIOD)) {
2706 wlc->tempsense_lasttime = wlc->pub->now;
2707 wlc_tempsense_upd(wlc);
2708 }
2709#ifdef WLC_LOW
2710 /* BMAC_NOTE: for HIGH_ONLY driver, this seems being called after RPC bus failed */
0f0881b0 2711 ASSERT(wlc_bmac_taclear(wlc->hw, true));
a9533e7e
HP
2712#endif
2713
2714 /* Verify that tx_prec_map and fifos are in sync to avoid lock ups */
2715 ASSERT(wlc_tx_prec_map_verify(wlc));
2716
2717 ASSERT(wlc_ps_check(wlc));
2718}
2719
2720/* make interface operational */
b4f790ee 2721int wlc_up(wlc_info_t *wlc)
a2627bc0 2722{
a9533e7e
HP
2723 WL_TRACE(("wl%d: %s:\n", wlc->pub->unit, __func__));
2724
2725 /* HW is turned off so don't try to access it */
2726 if (wlc->pub->hw_off || DEVICEREMOVED(wlc))
2727 return BCME_RADIOOFF;
2728
2729 if (!wlc->pub->hw_up) {
2730 wlc_bmac_hw_up(wlc->hw);
0f0881b0 2731 wlc->pub->hw_up = true;
a9533e7e
HP
2732 }
2733
2734 if ((wlc->pub->boardflags & BFL_FEM)
2735 && (CHIPID(wlc->pub->sih->chip) == BCM4313_CHIP_ID)) {
2736 if (wlc->pub->boardrev >= 0x1250
2737 && (wlc->pub->boardflags & BFL_FEM_BT)) {
2738 wlc_mhf(wlc, MHF5, MHF5_4313_GPIOCTRL,
2739 MHF5_4313_GPIOCTRL, WLC_BAND_ALL);
2740 } else {
2741 wlc_mhf(wlc, MHF4, MHF4_EXTPA_ENABLE, MHF4_EXTPA_ENABLE,
2742 WLC_BAND_ALL);
2743 }
2744 }
2745
2746 /*
2747 * Need to read the hwradio status here to cover the case where the system
2748 * is loaded with the hw radio disabled. We do not want to bring the driver up in this case.
2749 * if radio is disabled, abort up, lower power, start radio timer and return 0(for NDIS)
2750 * don't call radio_update to avoid looping wlc_up.
2751 *
2752 * wlc_bmac_up_prep() returns either 0 or BCME_RADIOOFF only
2753 */
2754 if (!wlc->pub->radio_disabled) {
2755 int status = wlc_bmac_up_prep(wlc->hw);
2756 if (status == BCME_RADIOOFF) {
2757 if (!mboolisset
2758 (wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE)) {
2759 int idx;
2760 wlc_bsscfg_t *bsscfg;
2761 mboolset(wlc->pub->radio_disabled,
2762 WL_RADIO_HW_DISABLE);
2763
2764 FOREACH_BSS(wlc, idx, bsscfg) {
2765 if (!BSSCFG_STA(bsscfg)
2766 || !bsscfg->enable || !bsscfg->BSS)
2767 continue;
2768 WL_ERROR(("wl%d.%d: wlc_up: rfdisable -> " "wlc_bsscfg_disable()\n", wlc->pub->unit, idx));
2769 }
2770 }
2771 } else
2772 ASSERT(!status);
2773 }
2774
2775 if (wlc->pub->radio_disabled) {
2776 wlc_radio_monitor_start(wlc);
2777 return 0;
2778 }
2779
2780 /* wlc_bmac_up_prep has done wlc_corereset(). so clk is on, set it */
0f0881b0 2781 wlc->clk = true;
a9533e7e
HP
2782
2783 wlc_radio_monitor_stop(wlc);
2784
2785 /* Set EDCF hostflags */
2786 if (EDCF_ENAB(wlc->pub)) {
2787 wlc_mhf(wlc, MHF1, MHF1_EDCF, MHF1_EDCF, WLC_BAND_ALL);
2788 } else {
2789 wlc_mhf(wlc, MHF1, MHF1_EDCF, 0, WLC_BAND_ALL);
2790 }
2791
2792 if (WLC_WAR16165(wlc))
2793 wlc_mhf(wlc, MHF2, MHF2_PCISLOWCLKWAR, MHF2_PCISLOWCLKWAR,
2794 WLC_BAND_ALL);
2795
2796 wl_init(wlc->wl);
0f0881b0 2797 wlc->pub->up = true;
a9533e7e
HP
2798
2799 if (wlc->bandinit_pending) {
2800 wlc_suspend_mac_and_wait(wlc);
2801 wlc_set_chanspec(wlc, wlc->default_bss->chanspec);
0965ae88 2802 wlc->bandinit_pending = false;
a9533e7e
HP
2803 wlc_enable_mac(wlc);
2804 }
2805
2806 wlc_bmac_up_finish(wlc->hw);
2807
2808 /* other software states up after ISR is running */
2809 /* start APs that were to be brought up but are not up yet */
2810 /* if (AP_ENAB(wlc->pub)) wlc_restart_ap(wlc->ap); */
2811
2812 /* Program the TX wme params with the current settings */
2813 wlc_wme_retries_write(wlc);
2814
2815 /* start one second watchdog timer */
2816 ASSERT(!wlc->WDarmed);
0f0881b0
GKH
2817 wl_add_timer(wlc->wl, wlc->wdtimer, TIMER_INTERVAL_WATCHDOG, true);
2818 wlc->WDarmed = true;
a9533e7e
HP
2819
2820 /* ensure antenna config is up to date */
2821 wlc_stf_phy_txant_upd(wlc);
2822 /* ensure LDPC config is in sync */
2823 wlc_ht_update_ldpc(wlc, wlc->stf->ldpc);
2824
90ea2296 2825 return 0;
a9533e7e
HP
2826}
2827
2828/* Initialize the base precedence map for dequeueing from txq based on WME settings */
b4f790ee 2829static void wlc_tx_prec_map_init(wlc_info_t *wlc)
a2627bc0 2830{
a9533e7e 2831 wlc->tx_prec_map = WLC_PREC_BMP_ALL;
7d4df48e 2832 bzero(wlc->fifo2prec_map, sizeof(u16) * NFIFO);
a9533e7e
HP
2833
2834 /* For non-WME, both fifos have overlapping MAXPRIO. So just disable all precedences
2835 * if either is full.
2836 */
2837 if (!EDCF_ENAB(wlc->pub)) {
2838 wlc->fifo2prec_map[TX_DATA_FIFO] = WLC_PREC_BMP_ALL;
2839 wlc->fifo2prec_map[TX_CTL_FIFO] = WLC_PREC_BMP_ALL;
2840 } else {
2841 wlc->fifo2prec_map[TX_AC_BK_FIFO] = WLC_PREC_BMP_AC_BK;
2842 wlc->fifo2prec_map[TX_AC_BE_FIFO] = WLC_PREC_BMP_AC_BE;
2843 wlc->fifo2prec_map[TX_AC_VI_FIFO] = WLC_PREC_BMP_AC_VI;
2844 wlc->fifo2prec_map[TX_AC_VO_FIFO] = WLC_PREC_BMP_AC_VO;
2845 }
2846}
2847
9927fc2e 2848static uint wlc_down_del_timer(wlc_info_t *wlc)
a2627bc0 2849{
a9533e7e
HP
2850 uint callbacks = 0;
2851
2852 return callbacks;
2853}
2854
2855/*
2856 * Mark the interface nonoperational, stop the software mechanisms,
2857 * disable the hardware, free any transient buffer state.
2858 * Return a count of the number of driver callbacks still pending.
2859 */
9927fc2e 2860uint wlc_down(wlc_info_t *wlc)
a2627bc0 2861{
a9533e7e
HP
2862
2863 uint callbacks = 0;
2864 int i;
0965ae88 2865 bool dev_gone = false;
a9533e7e
HP
2866 wlc_txq_info_t *qi;
2867
2868 WL_TRACE(("wl%d: %s:\n", wlc->pub->unit, __func__));
2869
2870 /* check if we are already in the going down path */
2871 if (wlc->going_down) {
2872 WL_ERROR(("wl%d: %s: Driver going down so return\n",
2873 wlc->pub->unit, __func__));
2874 return 0;
2875 }
2876 if (!wlc->pub->up)
90ea2296 2877 return callbacks;
a9533e7e
HP
2878
2879 /* in between, mpc could try to bring down again.. */
0f0881b0 2880 wlc->going_down = true;
a9533e7e
HP
2881
2882 callbacks += wlc_bmac_down_prep(wlc->hw);
2883
2884 dev_gone = DEVICEREMOVED(wlc);
2885
2886 /* Call any registered down handlers */
2887 for (i = 0; i < WLC_MAXMODULES; i++) {
2888 if (wlc->modulecb[i].down_fn)
2889 callbacks +=
2890 wlc->modulecb[i].down_fn(wlc->modulecb[i].hdl);
2891 }
2892
2893 /* cancel the watchdog timer */
2894 if (wlc->WDarmed) {
2895 if (!wl_del_timer(wlc->wl, wlc->wdtimer))
2896 callbacks++;
0965ae88 2897 wlc->WDarmed = false;
a9533e7e
HP
2898 }
2899 /* cancel all other timers */
2900 callbacks += wlc_down_del_timer(wlc);
2901
2902 /* interrupt must have been blocked */
2903 ASSERT((wlc->macintmask == 0) || !wlc->pub->up);
2904
0965ae88 2905 wlc->pub->up = false;
a9533e7e 2906
0965ae88 2907 wlc_phy_mute_upd(wlc->band->pi, false, PHY_MUTE_ALL);
a9533e7e
HP
2908
2909 /* clear txq flow control */
2910 wlc_txflowcontrol_reset(wlc);
2911
2912 /* flush tx queues */
2913 for (qi = wlc->tx_queues; qi != NULL; qi = qi->next) {
0f0881b0 2914 pktq_flush(wlc->osh, &qi->q, true, NULL, 0);
a9533e7e
HP
2915 ASSERT(pktq_empty(&qi->q));
2916 }
2917
2918 /* flush event queue.
2919 * Should be the last thing done after all the events are generated
2920 * Just delivers the events synchronously instead of waiting for a timer
2921 */
2922 callbacks += wlc_eventq_down(wlc->eventq);
2923
2924 callbacks += wlc_bmac_down_finish(wlc->hw);
2925
2926 /* wlc_bmac_down_finish has done wlc_coredisable(). so clk is off */
0965ae88 2927 wlc->clk = false;
a9533e7e
HP
2928
2929#ifdef WLC_HIGH_ONLY
2930 wlc_rpctx_txreclaim(wlc->rpctx);
2931#endif
2932
2933 /* Verify all packets are flushed from the driver */
2934 if (PKTALLOCED(wlc->osh) != 0) {
2935 WL_ERROR(("%d packets not freed at wlc_down!!!!!!\n",
2936 PKTALLOCED(wlc->osh)));
2937 }
2938#ifdef BCMDBG
2939 /* Since all the packets should have been freed,
2940 * all callbacks should have been called
2941 */
2942 for (i = 1; i <= wlc->pub->tunables->maxpktcb; i++)
2943 ASSERT(wlc->pkt_callback[i].fn == NULL);
2944#endif
0965ae88 2945 wlc->going_down = false;
90ea2296 2946 return callbacks;
a9533e7e
HP
2947}
2948
2949/* Set the current gmode configuration */
41feb5ed 2950int wlc_set_gmode(wlc_info_t *wlc, u8 gmode, bool config)
a9533e7e
HP
2951{
2952 int ret = 0;
2953 uint i;
2954 wlc_rateset_t rs;
2955 /* Default to 54g Auto */
562c8850 2956 s8 shortslot = WLC_SHORTSLOT_AUTO; /* Advertise and use shortslot (-1/0/1 Auto/Off/On) */
0965ae88 2957 bool shortslot_restrict = false; /* Restrict association to stations that support shortslot
a9533e7e 2958 */
0f0881b0 2959 bool ignore_bcns = true; /* Ignore legacy beacons on the same channel */
0965ae88 2960 bool ofdm_basic = false; /* Make 6, 12, and 24 basic rates */
a9533e7e 2961 int preamble = WLC_PLCP_LONG; /* Advertise and use short preambles (-1/0/1 Auto/Off/On) */
0965ae88 2962 bool preamble_restrict = false; /* Restrict association to stations that support short
a9533e7e
HP
2963 * preambles
2964 */
2965 wlcband_t *band;
2966
2967 /* if N-support is enabled, allow Gmode set as long as requested
2968 * Gmode is not GMODE_LEGACY_B
2969 */
2970 if (N_ENAB(wlc->pub) && gmode == GMODE_LEGACY_B)
2971 return BCME_UNSUPPORTED;
2972
2973 /* verify that we are dealing with 2G band and grab the band pointer */
2974 if (wlc->band->bandtype == WLC_BAND_2G)
2975 band = wlc->band;
2976 else if ((NBANDS(wlc) > 1) &&
2977 (wlc->bandstate[OTHERBANDUNIT(wlc)]->bandtype == WLC_BAND_2G))
2978 band = wlc->bandstate[OTHERBANDUNIT(wlc)];
2979 else
2980 return BCME_BADBAND;
2981
2982 /* Legacy or bust when no OFDM is supported by regulatory */
2983 if ((wlc_channel_locale_flags_in_band(wlc->cmi, band->bandunit) &
2984 WLC_NO_OFDM) && (gmode != GMODE_LEGACY_B))
2985 return BCME_RANGE;
2986
2987 /* update configuration value */
0f0881b0 2988 if (config == true)
a9533e7e
HP
2989 wlc_protection_upd(wlc, WLC_PROT_G_USER, gmode);
2990
2991 /* Clear supported rates filter */
2992 bzero(&wlc->sup_rates_override, sizeof(wlc_rateset_t));
2993
2994 /* Clear rateset override */
2995 bzero(&rs, sizeof(wlc_rateset_t));
2996
2997 switch (gmode) {
2998 case GMODE_LEGACY_B:
2999 shortslot = WLC_SHORTSLOT_OFF;
3000 wlc_rateset_copy(&gphy_legacy_rates, &rs);
3001
3002 break;
3003
3004 case GMODE_LRS:
3005 if (AP_ENAB(wlc->pub))
3006 wlc_rateset_copy(&cck_rates, &wlc->sup_rates_override);
3007 break;
3008
3009 case GMODE_AUTO:
3010 /* Accept defaults */
3011 break;
3012
3013 case GMODE_ONLY:
0f0881b0 3014 ofdm_basic = true;
a9533e7e 3015 preamble = WLC_PLCP_SHORT;
0f0881b0 3016 preamble_restrict = true;
a9533e7e
HP
3017 break;
3018
3019 case GMODE_PERFORMANCE:
3020 if (AP_ENAB(wlc->pub)) /* Put all rates into the Supported Rates element */
3021 wlc_rateset_copy(&cck_ofdm_rates,
3022 &wlc->sup_rates_override);
3023
3024 shortslot = WLC_SHORTSLOT_ON;
0f0881b0
GKH
3025 shortslot_restrict = true;
3026 ofdm_basic = true;
a9533e7e 3027 preamble = WLC_PLCP_SHORT;
0f0881b0 3028 preamble_restrict = true;
a9533e7e
HP
3029 break;
3030
3031 default:
3032 /* Error */
3033 WL_ERROR(("wl%d: %s: invalid gmode %d\n", wlc->pub->unit,
3034 __func__, gmode));
3035 return BCME_UNSUPPORTED;
3036 }
3037
3038 /*
3039 * If we are switching to gmode == GMODE_LEGACY_B,
3040 * clean up rate info that may refer to OFDM rates.
3041 */
3042 if ((gmode == GMODE_LEGACY_B) && (band->gmode != GMODE_LEGACY_B)) {
3043 band->gmode = gmode;
3044 if (band->rspec_override && !IS_CCK(band->rspec_override)) {
3045 band->rspec_override = 0;
3046 wlc_reprate_init(wlc);
3047 }
3048 if (band->mrspec_override && !IS_CCK(band->mrspec_override)) {
3049 band->mrspec_override = 0;
3050 }
3051 }
3052
3053 band->gmode = gmode;
3054
3055 wlc->ignore_bcns = ignore_bcns;
3056
3057 wlc->shortslot_override = shortslot;
3058
3059 if (AP_ENAB(wlc->pub)) {
3060 /* wlc->ap->shortslot_restrict = shortslot_restrict; */
3061 wlc->PLCPHdr_override =
3062 (preamble !=
3063 WLC_PLCP_LONG) ? WLC_PLCP_SHORT : WLC_PLCP_AUTO;
3064 }
3065
3066 if ((AP_ENAB(wlc->pub) && preamble != WLC_PLCP_LONG)
3067 || preamble == WLC_PLCP_SHORT)
3068 wlc->default_bss->capability |= DOT11_CAP_SHORT;
3069 else
3070 wlc->default_bss->capability &= ~DOT11_CAP_SHORT;
3071
3072 /* Update shortslot capability bit for AP and IBSS */
3073 if ((AP_ENAB(wlc->pub) && shortslot == WLC_SHORTSLOT_AUTO) ||
3074 shortslot == WLC_SHORTSLOT_ON)
3075 wlc->default_bss->capability |= DOT11_CAP_SHORTSLOT;
3076 else
3077 wlc->default_bss->capability &= ~DOT11_CAP_SHORTSLOT;
3078
3079 /* Use the default 11g rateset */
3080 if (!rs.count)
3081 wlc_rateset_copy(&cck_ofdm_rates, &rs);
3082
3083 if (ofdm_basic) {
3084 for (i = 0; i < rs.count; i++) {
3085 if (rs.rates[i] == WLC_RATE_6M
3086 || rs.rates[i] == WLC_RATE_12M
3087 || rs.rates[i] == WLC_RATE_24M)
3088 rs.rates[i] |= WLC_RATE_FLAG;
3089 }
3090 }
3091
3092 /* Set default bss rateset */
3093 wlc->default_bss->rateset.count = rs.count;
3094 bcopy((char *)rs.rates, (char *)wlc->default_bss->rateset.rates,
3095 sizeof(wlc->default_bss->rateset.rates));
3096
3097 return ret;
3098}
3099
3e26416e 3100static int wlc_nmode_validate(wlc_info_t *wlc, s32 nmode)
a9533e7e
HP
3101{
3102 int err = 0;
3103
3104 switch (nmode) {
3105
3106 case OFF:
3107 break;
3108
3109 case AUTO:
3110 case WL_11N_2x2:
3111 case WL_11N_3x3:
3112 if (!(WLC_PHY_11N_CAP(wlc->band)))
3113 err = BCME_BADBAND;
3114 break;
3115
3116 default:
3117 err = BCME_RANGE;
3118 break;
3119 }
3120
3121 return err;
3122}
3123
3e26416e 3124int wlc_set_nmode(wlc_info_t *wlc, s32 nmode)
a9533e7e
HP
3125{
3126 uint i;
3127 int err;
3128
3129 err = wlc_nmode_validate(wlc, nmode);
3130 ASSERT(err == 0);
3131 if (err)
3132 return err;
3133
3134 switch (nmode) {
3135 case OFF:
3136 wlc->pub->_n_enab = OFF;
3137 wlc->default_bss->flags &= ~WLC_BSS_HT;
3138 /* delete the mcs rates from the default and hw ratesets */
3139 wlc_rateset_mcs_clear(&wlc->default_bss->rateset);
3140 for (i = 0; i < NBANDS(wlc); i++) {
3141 memset(wlc->bandstate[i]->hw_rateset.mcs, 0,
3142 MCSSET_LEN);
3143 if (IS_MCS(wlc->band->rspec_override)) {
3144 wlc->bandstate[i]->rspec_override = 0;
3145 wlc_reprate_init(wlc);
3146 }
3147 if (IS_MCS(wlc->band->mrspec_override))
3148 wlc->bandstate[i]->mrspec_override = 0;
3149 }
3150 break;
3151
3152 case AUTO:
3153 if (wlc->stf->txstreams == WL_11N_3x3)
3154 nmode = WL_11N_3x3;
3155 else
3156 nmode = WL_11N_2x2;
3157 case WL_11N_2x2:
3158 case WL_11N_3x3:
3159 ASSERT(WLC_PHY_11N_CAP(wlc->band));
3160 /* force GMODE_AUTO if NMODE is ON */
0f0881b0 3161 wlc_set_gmode(wlc, GMODE_AUTO, true);
a9533e7e
HP
3162 if (nmode == WL_11N_3x3)
3163 wlc->pub->_n_enab = SUPPORT_HT;
3164 else
3165 wlc->pub->_n_enab = SUPPORT_11N;
3166 wlc->default_bss->flags |= WLC_BSS_HT;
3167 /* add the mcs rates to the default and hw ratesets */
3168 wlc_rateset_mcs_build(&wlc->default_bss->rateset,
3169 wlc->stf->txstreams);
3170 for (i = 0; i < NBANDS(wlc); i++)
3171 memcpy(wlc->bandstate[i]->hw_rateset.mcs,
3172 wlc->default_bss->rateset.mcs, MCSSET_LEN);
3173 break;
3174
3175 default:
3176 ASSERT(0);
3177 break;
3178 }
3179
3180 return err;
3181}
3182
7cc4a4c0 3183static int wlc_set_rateset(wlc_info_t *wlc, wlc_rateset_t *rs_arg)
a9533e7e
HP
3184{
3185 wlc_rateset_t rs, new;
3186 uint bandunit;
3187
3188 bcopy((char *)rs_arg, (char *)&rs, sizeof(wlc_rateset_t));
3189
3190 /* check for bad count value */
3191 if ((rs.count == 0) || (rs.count > WLC_NUMRATES))
3192 return BCME_BADRATESET;
3193
3194 /* try the current band */
3195 bandunit = wlc->band->bandunit;
3196 bcopy((char *)&rs, (char *)&new, sizeof(wlc_rateset_t));
3197 if (wlc_rate_hwrs_filter_sort_validate
0f0881b0 3198 (&new, &wlc->bandstate[bandunit]->hw_rateset, true,
a9533e7e
HP
3199 wlc->stf->txstreams))
3200 goto good;
3201
3202 /* try the other band */
3203 if (IS_MBAND_UNLOCKED(wlc)) {
3204 bandunit = OTHERBANDUNIT(wlc);
3205 bcopy((char *)&rs, (char *)&new, sizeof(wlc_rateset_t));
3206 if (wlc_rate_hwrs_filter_sort_validate(&new,
3207 &wlc->
3208 bandstate[bandunit]->
0f0881b0 3209 hw_rateset, true,
a9533e7e
HP
3210 wlc->stf->txstreams))
3211 goto good;
3212 }
3213
3214 return BCME_ERROR;
3215
3216 good:
3217 /* apply new rateset */
3218 bcopy((char *)&new, (char *)&wlc->default_bss->rateset,
3219 sizeof(wlc_rateset_t));
3220 bcopy((char *)&new, (char *)&wlc->bandstate[bandunit]->defrateset,
3221 sizeof(wlc_rateset_t));
90ea2296 3222 return 0;
a9533e7e
HP
3223}
3224
3225/* simplified integer set interface for common ioctl handler */
7cc4a4c0 3226int wlc_set(wlc_info_t *wlc, int cmd, int arg)
a9533e7e
HP
3227{
3228 return wlc_ioctl(wlc, cmd, (void *)&arg, sizeof(arg), NULL);
3229}
3230
3231/* simplified integer get interface for common ioctl handler */
7cc4a4c0 3232int wlc_get(wlc_info_t *wlc, int cmd, int *arg)
a9533e7e
HP
3233{
3234 return wlc_ioctl(wlc, cmd, arg, sizeof(int), NULL);
3235}
3236
7cc4a4c0 3237static void wlc_ofdm_rateset_war(wlc_info_t *wlc)
a9533e7e 3238{
41feb5ed 3239 u8 r;
0965ae88 3240 bool war = false;
a9533e7e
HP
3241
3242 if (wlc->cfg->associated)
3243 r = wlc->cfg->current_bss->rateset.rates[0];
3244 else
3245 r = wlc->default_bss->rateset.rates[0];
3246
3247 wlc_phy_ofdm_rateset_war(wlc->band->pi, war);
3248
3249 return;
3250}
3251
3252int
7cc4a4c0 3253wlc_ioctl(wlc_info_t *wlc, int cmd, void *arg, int len, struct wlc_if *wlcif)
a9533e7e 3254{
90ea2296 3255 return _wlc_ioctl(wlc, cmd, arg, len, wlcif);
a9533e7e
HP
3256}
3257
3258/* common ioctl handler. return: 0=ok, -1=error, positive=particular error */
3259static int
7cc4a4c0 3260_wlc_ioctl(wlc_info_t *wlc, int cmd, void *arg, int len, struct wlc_if *wlcif)
a9533e7e
HP
3261{
3262 int val, *pval;
3263 bool bool_val;
3264 int bcmerror;
3265 d11regs_t *regs;
3266 uint i;
3267 struct scb *nextscb;
3268 bool ta_ok;
3269 uint band;
3270 rw_reg_t *r;
3271 wlc_bsscfg_t *bsscfg;
3272 osl_t *osh;
3273 wlc_bss_info_t *current_bss;
3274
3275 /* update bsscfg pointer */
3276 bsscfg = NULL; /* XXX: Hack bsscfg to be size one and use this globally */
3277 current_bss = NULL;
3278
3279 /* initialize the following to get rid of compiler warning */
3280 nextscb = NULL;
0965ae88 3281 ta_ok = false;
a9533e7e
HP
3282 band = 0;
3283 r = NULL;
3284
3285 /* If the device is turned off, then it's not "removed" */
3286 if (!wlc->pub->hw_off && DEVICEREMOVED(wlc)) {
3287 WL_ERROR(("wl%d: %s: dead chip\n", wlc->pub->unit, __func__));
3288 wl_down(wlc->wl);
3289 return BCME_ERROR;
3290 }
3291
3292 ASSERT(!(wlc->pub->hw_off && wlc->pub->up));
3293
3294 /* default argument is generic integer */
2ae3b7ea 3295 pval = arg ? (int *)arg:NULL;
a9533e7e
HP
3296
3297 /* This will prevent the misaligned access */
66cbd3ab 3298 if (pval && (u32) len >= sizeof(val))
a9533e7e
HP
3299 bcopy(pval, &val, sizeof(val));
3300 else
3301 val = 0;
3302
3303 /* bool conversion to avoid duplication below */
3304 bool_val = val != 0;
3305
3306 if (cmd != WLC_SET_CHANNEL)
3307 WL_NONE(("WLC_IOCTL: cmd %d val 0x%x (%d) len %d\n", cmd,
3308 (uint) val, val, len));
3309
3310 bcmerror = 0;
3311 regs = wlc->regs;
3312 osh = wlc->osh;
3313
3314 /* A few commands don't need any arguments; all the others do. */
3315 switch (cmd) {
3316 case WLC_UP:
3317 case WLC_OUT:
3318 case WLC_DOWN:
3319 case WLC_DISASSOC:
3320 case WLC_RESTART:
3321 case WLC_REBOOT:
3322 case WLC_START_CHANNEL_QA:
3323 case WLC_INIT:
3324 break;
3325
3326 default:
3327 if ((arg == NULL) || (len <= 0)) {
3328 WL_ERROR(("wl%d: %s: Command %d needs arguments\n",
3329 wlc->pub->unit, __func__, cmd));
3330 bcmerror = BCME_BADARG;
3331 goto done;
3332 }
3333 }
3334
3335 switch (cmd) {
3336
3337#if defined(BCMDBG)
3338 case WLC_GET_MSGLEVEL:
3339 *pval = wl_msg_level;
3340 break;
3341
3342 case WLC_SET_MSGLEVEL:
3343 wl_msg_level = val;
3344 break;
3345#endif
3346
3347 case WLC_GET_INSTANCE:
3348 *pval = wlc->pub->unit;
3349 break;
3350
3351 case WLC_GET_CHANNEL:{
3352 channel_info_t *ci = (channel_info_t *) arg;
3353
3354 ASSERT(len > (int)sizeof(ci));
3355
3356 ci->hw_channel =
3357 CHSPEC_CHANNEL(WLC_BAND_PI_RADIO_CHANSPEC);
3358 ci->target_channel =
3359 CHSPEC_CHANNEL(wlc->default_bss->chanspec);
3360 ci->scan_channel = 0;
3361
3362 break;
3363 }
3364
3365 case WLC_SET_CHANNEL:{
3366 chanspec_t chspec = CH20MHZ_CHSPEC(val);
3367
3368 if (val < 0 || val > MAXCHANNEL) {
3369 bcmerror = BCME_OUTOFRANGECHAN;
3370 break;
3371 }
3372
3373 if (!wlc_valid_chanspec_db(wlc->cmi, chspec)) {
3374 bcmerror = BCME_BADCHAN;
3375 break;
3376 }
3377
3378 if (!wlc->pub->up && IS_MBAND_UNLOCKED(wlc)) {
3379 if (wlc->band->bandunit !=
3380 CHSPEC_WLCBANDUNIT(chspec))
0f0881b0 3381 wlc->bandinit_pending = true;
a9533e7e 3382 else
0965ae88 3383 wlc->bandinit_pending = false;
a9533e7e
HP
3384 }
3385
3386 wlc->default_bss->chanspec = chspec;
3387 /* wlc_BSSinit() will sanitize the rateset before using it.. */
3388 if (wlc->pub->up && !wlc->pub->associated &&
3389 (WLC_BAND_PI_RADIO_CHANSPEC != chspec)) {
3390 wlc_set_home_chanspec(wlc, chspec);
3391 wlc_suspend_mac_and_wait(wlc);
3392 wlc_set_chanspec(wlc, chspec);
3393 wlc_enable_mac(wlc);
3394 }
3395#ifdef WLC_HIGH_ONLY
3396 /* delay for channel change */
3397 msleep(50);
3398#endif
3399 break;
3400 }
3401
3402#if defined(BCMDBG)
3403 case WLC_GET_UCFLAGS:
3404 if (!wlc->pub->up) {
3405 bcmerror = BCME_NOTUP;
3406 break;
3407 }
3408
3409 /* optional band is stored in the second integer of incoming buffer */
3410 band =
3411 (len <
3412 (int)(2 * sizeof(int))) ? WLC_BAND_AUTO : ((int *)arg)[1];
3413
3414 /* bcmerror checking */
ca8c1e59
JC
3415 bcmerror = wlc_iocregchk(wlc, band);
3416 if (bcmerror)
a9533e7e
HP
3417 break;
3418
3419 if (val >= MHFMAX) {
3420 bcmerror = BCME_RANGE;
3421 break;
3422 }
3423
41feb5ed 3424 *pval = wlc_bmac_mhf_get(wlc->hw, (u8) val, WLC_BAND_AUTO);
a9533e7e
HP
3425 break;
3426
3427 case WLC_SET_UCFLAGS:
3428 if (!wlc->pub->up) {
3429 bcmerror = BCME_NOTUP;
3430 break;
3431 }
3432
3433 /* optional band is stored in the second integer of incoming buffer */
3434 band =
3435 (len <
3436 (int)(2 * sizeof(int))) ? WLC_BAND_AUTO : ((int *)arg)[1];
3437
3438 /* bcmerror checking */
ca8c1e59
JC
3439 bcmerror = wlc_iocregchk(wlc, band);
3440 if (bcmerror)
a9533e7e
HP
3441 break;
3442
7d4df48e 3443 i = (u16) val;
a9533e7e
HP
3444 if (i >= MHFMAX) {
3445 bcmerror = BCME_RANGE;
3446 break;
3447 }
3448
7d4df48e 3449 wlc_mhf(wlc, (u8) i, 0xffff, (u16) (val >> NBITS(u16)),
a9533e7e
HP
3450 WLC_BAND_AUTO);
3451 break;
3452
3453 case WLC_GET_SHMEM:
0f0881b0 3454 ta_ok = true;
a9533e7e
HP
3455
3456 /* optional band is stored in the second integer of incoming buffer */
3457 band =
3458 (len <
3459 (int)(2 * sizeof(int))) ? WLC_BAND_AUTO : ((int *)arg)[1];
3460
3461 /* bcmerror checking */
ca8c1e59
JC
3462 bcmerror = wlc_iocregchk(wlc, band);
3463 if (bcmerror)
a9533e7e
HP
3464 break;
3465
3466 if (val & 1) {
3467 bcmerror = BCME_BADADDR;
3468 break;
3469 }
3470
7d4df48e 3471 *pval = wlc_read_shm(wlc, (u16) val);
a9533e7e
HP
3472 break;
3473
3474 case WLC_SET_SHMEM:
0f0881b0 3475 ta_ok = true;
a9533e7e
HP
3476
3477 /* optional band is stored in the second integer of incoming buffer */
3478 band =
3479 (len <
3480 (int)(2 * sizeof(int))) ? WLC_BAND_AUTO : ((int *)arg)[1];
3481
3482 /* bcmerror checking */
ca8c1e59
JC
3483 bcmerror = wlc_iocregchk(wlc, band);
3484 if (bcmerror)
a9533e7e
HP
3485 break;
3486
3487 if (val & 1) {
3488 bcmerror = BCME_BADADDR;
3489 break;
3490 }
3491
7d4df48e
GKH
3492 wlc_write_shm(wlc, (u16) val,
3493 (u16) (val >> NBITS(u16)));
a9533e7e
HP
3494 break;
3495
3496 case WLC_R_REG: /* MAC registers */
0f0881b0 3497 ta_ok = true;
a9533e7e
HP
3498 r = (rw_reg_t *) arg;
3499 band = WLC_BAND_AUTO;
3500
3501 if (len < (int)(sizeof(rw_reg_t) - sizeof(uint))) {
3502 bcmerror = BCME_BUFTOOSHORT;
3503 break;
3504 }
3505
3506 if (len >= (int)sizeof(rw_reg_t))
3507 band = r->band;
3508
3509 /* bcmerror checking */
ca8c1e59
JC
3510 bcmerror = wlc_iocregchk(wlc, band);
3511 if (bcmerror)
a9533e7e
HP
3512 break;
3513
3514 if ((r->byteoff + r->size) > sizeof(d11regs_t)) {
3515 bcmerror = BCME_BADADDR;
3516 break;
3517 }
66cbd3ab 3518 if (r->size == sizeof(u32))
a9533e7e
HP
3519 r->val =
3520 R_REG(osh,
f024c48a 3521 (u32 *)((unsigned char *)(unsigned long)regs +
a9533e7e 3522 r->byteoff));
7d4df48e 3523 else if (r->size == sizeof(u16))
a9533e7e
HP
3524 r->val =
3525 R_REG(osh,
f024c48a 3526 (u16 *)((unsigned char *)(unsigned long)regs +
a9533e7e
HP
3527 r->byteoff));
3528 else
3529 bcmerror = BCME_BADADDR;
3530 break;
3531
3532 case WLC_W_REG:
0f0881b0 3533 ta_ok = true;
a9533e7e
HP
3534 r = (rw_reg_t *) arg;
3535 band = WLC_BAND_AUTO;
3536
3537 if (len < (int)(sizeof(rw_reg_t) - sizeof(uint))) {
3538 bcmerror = BCME_BUFTOOSHORT;
3539 break;
3540 }
3541
3542 if (len >= (int)sizeof(rw_reg_t))
3543 band = r->band;
3544
3545 /* bcmerror checking */
ca8c1e59
JC
3546 bcmerror = wlc_iocregchk(wlc, band);
3547 if (bcmerror)
a9533e7e
HP
3548 break;
3549
3550 if (r->byteoff + r->size > sizeof(d11regs_t)) {
3551 bcmerror = BCME_BADADDR;
3552 break;
3553 }
66cbd3ab 3554 if (r->size == sizeof(u32))
a9533e7e 3555 W_REG(osh,
f024c48a 3556 (u32 *)((unsigned char *)(unsigned long) regs +
a9533e7e 3557 r->byteoff), r->val);
7d4df48e 3558 else if (r->size == sizeof(u16))
a9533e7e 3559 W_REG(osh,
f024c48a 3560 (u16 *)((unsigned char *)(unsigned long) regs +
a9533e7e
HP
3561 r->byteoff), r->val);
3562 else
3563 bcmerror = BCME_BADADDR;
3564 break;
3565#endif /* BCMDBG */
3566
3567 case WLC_GET_TXANT:
3568 *pval = wlc->stf->txant;
3569 break;
3570
3571 case WLC_SET_TXANT:
562c8850 3572 bcmerror = wlc_stf_ant_txant_validate(wlc, (s8) val);
a9533e7e
HP
3573 if (bcmerror < 0)
3574 break;
3575
562c8850 3576 wlc->stf->txant = (s8) val;
a9533e7e
HP
3577
3578 /* if down, we are done */
3579 if (!wlc->pub->up)
3580 break;
3581
3582 wlc_suspend_mac_and_wait(wlc);
3583
3584 wlc_stf_phy_txant_upd(wlc);
3585 wlc_beacon_phytxctl_txant_upd(wlc, wlc->bcn_rspec);
3586
3587 wlc_enable_mac(wlc);
3588
3589 break;
3590
3591 case WLC_GET_ANTDIV:{
41feb5ed 3592 u8 phy_antdiv;
a9533e7e
HP
3593
3594 /* return configured value if core is down */
3595 if (!wlc->pub->up) {
3596 *pval = wlc->stf->ant_rx_ovr;
3597
3598 } else {
3599 if (wlc_phy_ant_rxdiv_get
3600 (wlc->band->pi, &phy_antdiv))
3601 *pval = (int)phy_antdiv;
3602 else
3603 *pval = (int)wlc->stf->ant_rx_ovr;
3604 }
3605
3606 break;
3607 }
3608 case WLC_SET_ANTDIV:
3609 /* values are -1=driver default, 0=force0, 1=force1, 2=start1, 3=start0 */
3610 if ((val < -1) || (val > 3)) {
3611 bcmerror = BCME_RANGE;
3612 break;
3613 }
3614
3615 if (val == -1)
3616 val = ANT_RX_DIV_DEF;
3617
41feb5ed
GKH
3618 wlc->stf->ant_rx_ovr = (u8) val;
3619 wlc_phy_ant_rxdiv_set(wlc->band->pi, (u8) val);
a9533e7e
HP
3620 break;
3621
3622 case WLC_GET_RX_ANT:{ /* get latest used rx antenna */
7d4df48e 3623 u16 rxstatus;
a9533e7e
HP
3624
3625 if (!wlc->pub->up) {
3626 bcmerror = BCME_NOTUP;
3627 break;
3628 }
3629
3630 rxstatus = R_REG(wlc->osh, &wlc->regs->phyrxstatus0);
7d4df48e 3631 if (rxstatus == 0xdead || rxstatus == (u16) -1) {
a9533e7e
HP
3632 bcmerror = BCME_ERROR;
3633 break;
3634 }
3635 *pval = (rxstatus & PRXS0_RXANT_UPSUBBAND) ? 1 : 0;
3636 break;
3637 }
3638
3639#if defined(BCMDBG)
3640 case WLC_GET_UCANTDIV:
3641 if (!wlc->clk) {
3642 bcmerror = BCME_NOCLK;
3643 break;
3644 }
3645
3646 *pval =
3647 (wlc_bmac_mhf_get(wlc->hw, MHF1, WLC_BAND_AUTO) &
3648 MHF1_ANTDIV);
3649 break;
3650
3651 case WLC_SET_UCANTDIV:{
3652 if (!wlc->pub->up) {
3653 bcmerror = BCME_NOTUP;
3654 break;
3655 }
3656
3657 /* if multiband, band must be locked */
3658 if (IS_MBAND_UNLOCKED(wlc)) {
3659 bcmerror = BCME_NOTBANDLOCKED;
3660 break;
3661 }
3662
3663 /* 4322 supports antdiv in phy, no need to set it to ucode */
3664 if (WLCISNPHY(wlc->band)
3665 && D11REV_IS(wlc->pub->corerev, 16)) {
3666 WL_ERROR(("wl%d: can't set ucantdiv for 4322\n",
3667 wlc->pub->unit));
3668 bcmerror = BCME_UNSUPPORTED;
3669 } else
3670 wlc_mhf(wlc, MHF1, MHF1_ANTDIV,
3671 (val ? MHF1_ANTDIV : 0), WLC_BAND_AUTO);
3672 break;
3673 }
3674#endif /* defined(BCMDBG) */
3675
3676 case WLC_GET_SRL:
3677 *pval = wlc->SRL;
3678 break;
3679
3680 case WLC_SET_SRL:
3681 if (val >= 1 && val <= RETRY_SHORT_MAX) {
3682 int ac;
7d4df48e 3683 wlc->SRL = (u16) val;
a9533e7e
HP
3684
3685 wlc_bmac_retrylimit_upd(wlc->hw, wlc->SRL, wlc->LRL);
3686
3687 for (ac = 0; ac < AC_COUNT; ac++) {
3688 WLC_WME_RETRY_SHORT_SET(wlc, ac, wlc->SRL);
3689 }
3690 wlc_wme_retries_write(wlc);
3691 } else
3692 bcmerror = BCME_RANGE;
3693 break;
3694
3695 case WLC_GET_LRL:
3696 *pval = wlc->LRL;
3697 break;
3698
3699 case WLC_SET_LRL:
3700 if (val >= 1 && val <= 255) {
3701 int ac;
7d4df48e 3702 wlc->LRL = (u16) val;
a9533e7e
HP
3703
3704 wlc_bmac_retrylimit_upd(wlc->hw, wlc->SRL, wlc->LRL);
3705
3706 for (ac = 0; ac < AC_COUNT; ac++) {
3707 WLC_WME_RETRY_LONG_SET(wlc, ac, wlc->LRL);
3708 }
3709 wlc_wme_retries_write(wlc);
3710 } else
3711 bcmerror = BCME_RANGE;
3712 break;
3713
3714 case WLC_GET_CWMIN:
3715 *pval = wlc->band->CWmin;
3716 break;
3717
3718 case WLC_SET_CWMIN:
3719 if (!wlc->clk) {
3720 bcmerror = BCME_NOCLK;
3721 break;
3722 }
3723
3724 if (val >= 1 && val <= 255) {
7d4df48e 3725 wlc_set_cwmin(wlc, (u16) val);
a9533e7e
HP
3726 } else
3727 bcmerror = BCME_RANGE;
3728 break;
3729
3730 case WLC_GET_CWMAX:
3731 *pval = wlc->band->CWmax;
3732 break;
3733
3734 case WLC_SET_CWMAX:
3735 if (!wlc->clk) {
3736 bcmerror = BCME_NOCLK;
3737 break;
3738 }
3739
3740 if (val >= 255 && val <= 2047) {
7d4df48e 3741 wlc_set_cwmax(wlc, (u16) val);
a9533e7e
HP
3742 } else
3743 bcmerror = BCME_RANGE;
3744 break;
3745
3746 case WLC_GET_RADIO: /* use mask if don't want to expose some internal bits */
3747 *pval = wlc->pub->radio_disabled;
3748 break;
3749
3750 case WLC_SET_RADIO:{ /* 32 bits input, higher 16 bits are mask, lower 16 bits are value to
3751 * set
3752 */
7d4df48e 3753 u16 radiomask, radioval;
a9533e7e
HP
3754 uint validbits =
3755 WL_RADIO_SW_DISABLE | WL_RADIO_HW_DISABLE;
3756 mbool new = 0;
3757
3758 radiomask = (val & 0xffff0000) >> 16;
3759 radioval = val & 0x0000ffff;
3760
3761 if ((radiomask == 0) || (radiomask & ~validbits)
3762 || (radioval & ~validbits)
3763 || ((radioval & ~radiomask) != 0)) {
3764 WL_ERROR(("SET_RADIO with wrong bits 0x%x\n",
3765 val));
3766 bcmerror = BCME_RANGE;
3767 break;
3768 }
3769
3770 new =
3771 (wlc->pub->radio_disabled & ~radiomask) | radioval;
3772 wlc->pub->radio_disabled = new;
3773
3774 wlc_radio_hwdisable_upd(wlc);
3775 wlc_radio_upd(wlc);
3776 break;
3777 }
3778
3779 case WLC_GET_PHYTYPE:
3780 *pval = WLC_PHYTYPE(wlc->band->phytype);
3781 break;
3782
3783#if defined(BCMDBG)
3784 case WLC_GET_KEY:
3785 if ((val >= 0) && (val < WLC_MAX_WSEC_KEYS(wlc))) {
3786 wl_wsec_key_t key;
3787
3788 wsec_key_t *src_key = wlc->wsec_keys[val];
3789
3790 if (len < (int)sizeof(key)) {
3791 bcmerror = BCME_BUFTOOSHORT;
3792 break;
3793 }
3794
3795 bzero((char *)&key, sizeof(key));
3796 if (src_key) {
3797 key.index = src_key->id;
3798 key.len = src_key->len;
3799 bcopy(src_key->data, key.data, key.len);
3800 key.algo = src_key->algo;
3801 if (WSEC_SOFTKEY(wlc, src_key, bsscfg))
3802 key.flags |= WL_SOFT_KEY;
3803 if (src_key->flags & WSEC_PRIMARY_KEY)
3804 key.flags |= WL_PRIMARY_KEY;
3805
3806 bcopy(src_key->ea.octet, key.ea.octet,
3807 ETHER_ADDR_LEN);
3808 }
3809
3810 bcopy((char *)&key, arg, sizeof(key));
3811 } else
3812 bcmerror = BCME_BADKEYIDX;
3813 break;
3814#endif /* defined(BCMDBG) */
3815
3816 case WLC_SET_KEY:
3817 bcmerror =
3818 wlc_iovar_op(wlc, "wsec_key", NULL, 0, arg, len, IOV_SET,
3819 wlcif);
3820 break;
3821
3822 case WLC_GET_KEY_SEQ:{
3823 wsec_key_t *key;
3824
3825 if (len < DOT11_WPA_KEY_RSC_LEN) {
3826 bcmerror = BCME_BUFTOOSHORT;
3827 break;
3828 }
3829
3830 /* Return the key's tx iv as an EAPOL sequence counter.
3831 * This will be used to supply the RSC value to a supplicant.
3832 * The format is 8 bytes, with least significant in seq[0].
3833 */
3834
ca8c1e59 3835 key = WSEC_KEY(wlc, val);
a9533e7e 3836 if ((val >= 0) && (val < WLC_MAX_WSEC_KEYS(wlc)) &&
ca8c1e59 3837 (key != NULL)) {
41feb5ed 3838 u8 seq[DOT11_WPA_KEY_RSC_LEN];
7d4df48e 3839 u16 lo;
66cbd3ab 3840 u32 hi;
a9533e7e
HP
3841 /* group keys in WPA-NONE (IBSS only, AES and TKIP) use a global TXIV */
3842 if ((bsscfg->WPA_auth & WPA_AUTH_NONE)
3843 && ETHER_ISNULLADDR(&key->ea)) {
3844 lo = bsscfg->wpa_none_txiv.lo;
3845 hi = bsscfg->wpa_none_txiv.hi;
3846 } else {
3847 lo = key->txiv.lo;
3848 hi = key->txiv.hi;
3849 }
3850
3851 /* format the buffer, low to high */
3852 seq[0] = lo & 0xff;
3853 seq[1] = (lo >> 8) & 0xff;
3854 seq[2] = hi & 0xff;
3855 seq[3] = (hi >> 8) & 0xff;
3856 seq[4] = (hi >> 16) & 0xff;
3857 seq[5] = (hi >> 24) & 0xff;
3858 seq[6] = 0;
3859 seq[7] = 0;
3860
3861 bcopy((char *)seq, arg, sizeof(seq));
3862 } else {
3863 bcmerror = BCME_BADKEYIDX;
3864 }
3865 break;
3866 }
3867
3868 case WLC_GET_CURR_RATESET:{
3869 wl_rateset_t *ret_rs = (wl_rateset_t *) arg;
3870 wlc_rateset_t *rs;
3871
3872 if (bsscfg->associated)
3873 rs = &current_bss->rateset;
3874 else
3875 rs = &wlc->default_bss->rateset;
3876
3877 if (len < (int)(rs->count + sizeof(rs->count))) {
3878 bcmerror = BCME_BUFTOOSHORT;
3879 break;
3880 }
3881
3882 /* Copy only legacy rateset section */
3883 ret_rs->count = rs->count;
3884 bcopy(&rs->rates, &ret_rs->rates, rs->count);
3885 break;
3886 }
3887
3888 case WLC_GET_RATESET:{
3889 wlc_rateset_t rs;
3890 wl_rateset_t *ret_rs = (wl_rateset_t *) arg;
3891
3892 bzero(&rs, sizeof(wlc_rateset_t));
29c4275a 3893 wlc_default_rateset(wlc, (wlc_rateset_t *) &rs);
a9533e7e
HP
3894
3895 if (len < (int)(rs.count + sizeof(rs.count))) {
3896 bcmerror = BCME_BUFTOOSHORT;
3897 break;
3898 }
3899
3900 /* Copy only legacy rateset section */
3901 ret_rs->count = rs.count;
3902 bcopy(&rs.rates, &ret_rs->rates, rs.count);
3903 break;
3904 }
3905
3906 case WLC_SET_RATESET:{
3907 wlc_rateset_t rs;
3908 wl_rateset_t *in_rs = (wl_rateset_t *) arg;
3909
3910 if (len < (int)(in_rs->count + sizeof(in_rs->count))) {
3911 bcmerror = BCME_BUFTOOSHORT;
3912 break;
3913 }
3914
3915 if (in_rs->count > WLC_NUMRATES) {
3916 bcmerror = BCME_BUFTOOLONG;
3917 break;
3918 }
3919
3920 bzero(&rs, sizeof(wlc_rateset_t));
3921
3922 /* Copy only legacy rateset section */
3923 rs.count = in_rs->count;
3924 bcopy(&in_rs->rates, &rs.rates, rs.count);
3925
3926 /* merge rateset coming in with the current mcsset */
3927 if (N_ENAB(wlc->pub)) {
3928 if (bsscfg->associated)
3929 bcopy(&current_bss->rateset.mcs[0],
3930 rs.mcs, MCSSET_LEN);
3931 else
3932 bcopy(&wlc->default_bss->rateset.mcs[0],
3933 rs.mcs, MCSSET_LEN);
3934 }
3935
3936 bcmerror = wlc_set_rateset(wlc, &rs);
3937
3938 if (!bcmerror)
3939 wlc_ofdm_rateset_war(wlc);
3940
3941 break;
3942 }
3943
3944 case WLC_GET_BCNPRD:
3945 if (BSSCFG_STA(bsscfg) && bsscfg->BSS && bsscfg->associated)
3946 *pval = current_bss->beacon_period;
3947 else
3948 *pval = wlc->default_bss->beacon_period;
3949 break;
3950
3951 case WLC_SET_BCNPRD:
3952 /* range [1, 0xffff] */
3953 if (val >= DOT11_MIN_BEACON_PERIOD
3954 && val <= DOT11_MAX_BEACON_PERIOD) {
7d4df48e 3955 wlc->default_bss->beacon_period = (u16) val;
a9533e7e
HP
3956 } else
3957 bcmerror = BCME_RANGE;
3958 break;
3959
3960 case WLC_GET_DTIMPRD:
3961 if (BSSCFG_STA(bsscfg) && bsscfg->BSS && bsscfg->associated)
3962 *pval = current_bss->dtim_period;
3963 else
3964 *pval = wlc->default_bss->dtim_period;
3965 break;
3966
3967 case WLC_SET_DTIMPRD:
3968 /* range [1, 0xff] */
3969 if (val >= DOT11_MIN_DTIM_PERIOD
3970 && val <= DOT11_MAX_DTIM_PERIOD) {
41feb5ed 3971 wlc->default_bss->dtim_period = (u8) val;
a9533e7e
HP
3972 } else
3973 bcmerror = BCME_RANGE;
3974 break;
3975
3976#ifdef SUPPORT_PS
3977 case WLC_GET_PM:
3978 *pval = wlc->PM;
3979 break;
3980
3981 case WLC_SET_PM:
3982 if ((val >= PM_OFF) && (val <= PM_MAX)) {
41feb5ed 3983 wlc->PM = (u8) val;
a9533e7e
HP
3984 if (wlc->pub->up) {
3985 }
3986 /* Change watchdog driver to align watchdog with tbtt if possible */
3987 wlc_watchdog_upd(wlc, PS_ALLOWED(wlc));
3988 } else
3989 bcmerror = BCME_ERROR;
3990 break;
3991#endif /* SUPPORT_PS */
3992
3993#ifdef SUPPORT_PS
3994#ifdef BCMDBG
3995 case WLC_GET_WAKE:
3996 if (AP_ENAB(wlc->pub)) {
3997 bcmerror = BCME_NOTSTA;
3998 break;
3999 }
4000 *pval = wlc->wake;
4001 break;
4002
4003 case WLC_SET_WAKE:
4004 if (AP_ENAB(wlc->pub)) {
4005 bcmerror = BCME_NOTSTA;
4006 break;
4007 }
4008
0965ae88 4009 wlc->wake = val ? true : false;
a9533e7e
HP
4010
4011 /* if down, we're done */
4012 if (!wlc->pub->up)
4013 break;
4014
4015 /* apply to the mac */
4016 wlc_set_ps_ctrl(wlc);
4017 break;
4018#endif /* BCMDBG */
4019#endif /* SUPPORT_PS */
4020
4021 case WLC_GET_REVINFO:
4022 bcmerror = wlc_get_revision_info(wlc, arg, (uint) len);
4023 break;
4024
4025 case WLC_GET_AP:
4026 *pval = (int)AP_ENAB(wlc->pub);
4027 break;
4028
4029 case WLC_GET_ATIM:
4030 if (bsscfg->associated)
4031 *pval = (int)current_bss->atim_window;
4032 else
4033 *pval = (int)wlc->default_bss->atim_window;
4034 break;
4035
4036 case WLC_SET_ATIM:
66cbd3ab 4037 wlc->default_bss->atim_window = (u32) val;
a9533e7e
HP
4038 break;
4039
4040 case WLC_GET_PKTCNTS:{
4041 get_pktcnt_t *pktcnt = (get_pktcnt_t *) pval;
4042 if (WLC_UPDATE_STATS(wlc))
4043 wlc_statsupd(wlc);
4044 pktcnt->rx_good_pkt = WLCNTVAL(wlc->pub->_cnt->rxframe);
4045 pktcnt->rx_bad_pkt = WLCNTVAL(wlc->pub->_cnt->rxerror);
4046 pktcnt->tx_good_pkt =
4047 WLCNTVAL(wlc->pub->_cnt->txfrmsnt);
4048 pktcnt->tx_bad_pkt =
4049 WLCNTVAL(wlc->pub->_cnt->txerror) +
4050 WLCNTVAL(wlc->pub->_cnt->txfail);
4051 if (len >= (int)sizeof(get_pktcnt_t)) {
4052 /* Be backward compatible - only if buffer is large enough */
4053 pktcnt->rx_ocast_good_pkt =
4054 WLCNTVAL(wlc->pub->_cnt->rxmfrmocast);
4055 }
4056 break;
4057 }
4058
4059#ifdef SUPPORT_HWKEY
4060 case WLC_GET_WSEC:
4061 bcmerror =
4062 wlc_iovar_op(wlc, "wsec", NULL, 0, arg, len, IOV_GET,
4063 wlcif);
4064 break;
4065
4066 case WLC_SET_WSEC:
4067 bcmerror =
4068 wlc_iovar_op(wlc, "wsec", NULL, 0, arg, len, IOV_SET,
4069 wlcif);
4070 break;
4071
4072 case WLC_GET_WPA_AUTH:
4073 *pval = (int)bsscfg->WPA_auth;
4074 break;
4075
4076 case WLC_SET_WPA_AUTH:
4077 /* change of WPA_Auth modifies the PS_ALLOWED state */
4078 if (BSSCFG_STA(bsscfg)) {
7d4df48e 4079 bsscfg->WPA_auth = (u16) val;
a9533e7e 4080 } else
7d4df48e 4081 bsscfg->WPA_auth = (u16) val;
a9533e7e
HP
4082 break;
4083#endif /* SUPPORT_HWKEY */
4084
4085 case WLC_GET_BANDLIST:
4086 /* count of number of bands, followed by each band type */
4087 *pval++ = NBANDS(wlc);
4088 *pval++ = wlc->band->bandtype;
4089 if (NBANDS(wlc) > 1)
4090 *pval++ = wlc->bandstate[OTHERBANDUNIT(wlc)]->bandtype;
4091 break;
4092
4093 case WLC_GET_BAND:
4094 *pval = wlc->bandlocked ? wlc->band->bandtype : WLC_BAND_AUTO;
4095 break;
4096
4097 case WLC_GET_PHYLIST:
4098 {
580a0bd9 4099 unsigned char *cp = arg;
a9533e7e
HP
4100 if (len < 3) {
4101 bcmerror = BCME_BUFTOOSHORT;
4102 break;
4103 }
4104
4105 if (WLCISNPHY(wlc->band)) {
4106 *cp++ = 'n';
4107 } else if (WLCISLCNPHY(wlc->band)) {
4108 *cp++ = 'c';
4109 } else if (WLCISSSLPNPHY(wlc->band)) {
4110 *cp++ = 's';
4111 }
4112 *cp = '\0';
4113 break;
4114 }
4115
4116 case WLC_GET_SHORTSLOT:
4117 *pval = wlc->shortslot;
4118 break;
4119
4120 case WLC_GET_SHORTSLOT_OVERRIDE:
4121 *pval = wlc->shortslot_override;
4122 break;
4123
4124 case WLC_SET_SHORTSLOT_OVERRIDE:
4125 if ((val != WLC_SHORTSLOT_AUTO) &&
4126 (val != WLC_SHORTSLOT_OFF) && (val != WLC_SHORTSLOT_ON)) {
4127 bcmerror = BCME_RANGE;
4128 break;
4129 }
4130
562c8850 4131 wlc->shortslot_override = (s8) val;
a9533e7e
HP
4132
4133 /* shortslot is an 11g feature, so no more work if we are
4134 * currently on the 5G band
4135 */
4136 if (BAND_5G(wlc->band->bandtype))
4137 break;
4138
4139 if (wlc->pub->up && wlc->pub->associated) {
4140 /* let watchdog or beacon processing update shortslot */
4141 } else if (wlc->pub->up) {
4142 /* unassociated shortslot is off */
0965ae88 4143 wlc_switch_shortslot(wlc, false);
a9533e7e
HP
4144 } else {
4145 /* driver is down, so just update the wlc_info value */
4146 if (wlc->shortslot_override == WLC_SHORTSLOT_AUTO) {
0965ae88 4147 wlc->shortslot = false;
a9533e7e
HP
4148 } else {
4149 wlc->shortslot =
4150 (wlc->shortslot_override ==
4151 WLC_SHORTSLOT_ON);
4152 }
4153 }
4154
4155 break;
4156
4157 case WLC_GET_LEGACY_ERP:
4158 *pval = wlc->include_legacy_erp;
4159 break;
4160
4161 case WLC_SET_LEGACY_ERP:
4162 if (wlc->include_legacy_erp == bool_val)
4163 break;
4164
4165 wlc->include_legacy_erp = bool_val;
4166
4167 if (AP_ENAB(wlc->pub) && wlc->clk) {
4168 wlc_update_beacon(wlc);
0f0881b0 4169 wlc_update_probe_resp(wlc, true);
a9533e7e
HP
4170 }
4171 break;
4172
4173 case WLC_GET_GMODE:
4174 if (wlc->band->bandtype == WLC_BAND_2G)
4175 *pval = wlc->band->gmode;
4176 else if (NBANDS(wlc) > 1)
4177 *pval = wlc->bandstate[OTHERBANDUNIT(wlc)]->gmode;
4178 break;
4179
4180 case WLC_SET_GMODE:
4181 if (!wlc->pub->associated)
0f0881b0 4182 bcmerror = wlc_set_gmode(wlc, (u8) val, true);
a9533e7e
HP
4183 else {
4184 bcmerror = BCME_ASSOCIATED;
4185 break;
4186 }
4187 break;
4188
4189 case WLC_GET_GMODE_PROTECTION:
4190 *pval = wlc->protection->_g;
4191 break;
4192
4193 case WLC_GET_PROTECTION_CONTROL:
4194 *pval = wlc->protection->overlap;
4195 break;
4196
4197 case WLC_SET_PROTECTION_CONTROL:
4198 if ((val != WLC_PROTECTION_CTL_OFF) &&
4199 (val != WLC_PROTECTION_CTL_LOCAL) &&
4200 (val != WLC_PROTECTION_CTL_OVERLAP)) {
4201 bcmerror = BCME_RANGE;
4202 break;
4203 }
4204
562c8850 4205 wlc_protection_upd(wlc, WLC_PROT_OVERLAP, (s8) val);
a9533e7e
HP
4206
4207 /* Current g_protection will sync up to the specified control alg in watchdog
4208 * if the driver is up and associated.
4209 * If the driver is down or not associated, the control setting has no effect.
4210 */
4211 break;
4212
4213 case WLC_GET_GMODE_PROTECTION_OVERRIDE:
4214 *pval = wlc->protection->g_override;
4215 break;
4216
4217 case WLC_SET_GMODE_PROTECTION_OVERRIDE:
4218 if ((val != WLC_PROTECTION_AUTO) &&
4219 (val != WLC_PROTECTION_OFF) && (val != WLC_PROTECTION_ON)) {
4220 bcmerror = BCME_RANGE;
4221 break;
4222 }
4223
562c8850 4224 wlc_protection_upd(wlc, WLC_PROT_G_OVR, (s8) val);
a9533e7e
HP
4225
4226 break;
4227
4228 case WLC_SET_SUP_RATESET_OVERRIDE:{
4229 wlc_rateset_t rs, new;
4230
4231 /* copyin */
4232 if (len < (int)sizeof(wlc_rateset_t)) {
4233 bcmerror = BCME_BUFTOOSHORT;
4234 break;
4235 }
4236 bcopy((char *)arg, (char *)&rs, sizeof(wlc_rateset_t));
4237
4238 /* check for bad count value */
4239 if (rs.count > WLC_NUMRATES) {
4240 bcmerror = BCME_BADRATESET; /* invalid rateset */
4241 break;
4242 }
4243
4244 /* this command is only appropriate for gmode operation */
4245 if (!(wlc->band->gmode ||
4246 ((NBANDS(wlc) > 1)
4247 && wlc->bandstate[OTHERBANDUNIT(wlc)]->gmode))) {
4248 bcmerror = BCME_BADBAND; /* gmode only command when not in gmode */
4249 break;
4250 }
4251
4252 /* check for an empty rateset to clear the override */
4253 if (rs.count == 0) {
4254 bzero(&wlc->sup_rates_override,
4255 sizeof(wlc_rateset_t));
4256 break;
4257 }
4258
4259 /* validate rateset by comparing pre and post sorted against 11g hw rates */
0965ae88 4260 wlc_rateset_filter(&rs, &new, false, WLC_RATES_CCK_OFDM,
a9533e7e
HP
4261 RATE_MASK, BSS_N_ENAB(wlc, bsscfg));
4262 wlc_rate_hwrs_filter_sort_validate(&new,
4263 &cck_ofdm_rates,
0965ae88 4264 false,
a9533e7e
HP
4265 wlc->stf->txstreams);
4266 if (rs.count != new.count) {
4267 bcmerror = BCME_BADRATESET; /* invalid rateset */
4268 break;
4269 }
4270
4271 /* apply new rateset to the override */
4272 bcopy((char *)&new, (char *)&wlc->sup_rates_override,
4273 sizeof(wlc_rateset_t));
4274
4275 /* update bcn and probe resp if needed */
4276 if (wlc->pub->up && AP_ENAB(wlc->pub)
4277 && wlc->pub->associated) {
4278 wlc_update_beacon(wlc);
0f0881b0 4279 wlc_update_probe_resp(wlc, true);
a9533e7e
HP
4280 }
4281 break;
4282 }
4283
4284 case WLC_GET_SUP_RATESET_OVERRIDE:
4285 /* this command is only appropriate for gmode operation */
4286 if (!(wlc->band->gmode ||
4287 ((NBANDS(wlc) > 1)
4288 && wlc->bandstate[OTHERBANDUNIT(wlc)]->gmode))) {
4289 bcmerror = BCME_BADBAND; /* gmode only command when not in gmode */
4290 break;
4291 }
4292 if (len < (int)sizeof(wlc_rateset_t)) {
4293 bcmerror = BCME_BUFTOOSHORT;
4294 break;
4295 }
4296 bcopy((char *)&wlc->sup_rates_override, (char *)arg,
4297 sizeof(wlc_rateset_t));
4298
4299 break;
4300
4301 case WLC_GET_PRB_RESP_TIMEOUT:
4302 *pval = wlc->prb_resp_timeout;
4303 break;
4304
4305 case WLC_SET_PRB_RESP_TIMEOUT:
4306 if (wlc->pub->up) {
4307 bcmerror = BCME_NOTDOWN;
4308 break;
4309 }
4310 if (val < 0 || val >= 0xFFFF) {
4311 bcmerror = BCME_RANGE; /* bad value */
4312 break;
4313 }
7d4df48e 4314 wlc->prb_resp_timeout = (u16) val;
a9533e7e
HP
4315 break;
4316
4317 case WLC_GET_KEY_PRIMARY:{
4318 wsec_key_t *key;
4319
4320 /* treat the 'val' parm as the key id */
ca8c1e59
JC
4321 key = WSEC_BSS_DEFAULT_KEY(bsscfg);
4322 if (key != NULL) {
0965ae88 4323 *pval = key->id == val ? true : false;
a9533e7e
HP
4324 } else {
4325 bcmerror = BCME_BADKEYIDX;
4326 }
4327 break;
4328 }
4329
4330 case WLC_SET_KEY_PRIMARY:{
4331 wsec_key_t *key, *old_key;
4332
4333 bcmerror = BCME_BADKEYIDX;
4334
4335 /* treat the 'val' parm as the key id */
4336 for (i = 0; i < WSEC_MAX_DEFAULT_KEYS; i++) {
ca8c1e59
JC
4337 key = bsscfg->bss_def_keys[i];
4338 if (key != NULL && key->id == val) {
4339 old_key = WSEC_BSS_DEFAULT_KEY(bsscfg);
4340 if (old_key != NULL)
a9533e7e
HP
4341 old_key->flags &=
4342 ~WSEC_PRIMARY_KEY;
4343 key->flags |= WSEC_PRIMARY_KEY;
4344 bsscfg->wsec_index = i;
4345 bcmerror = BCME_OK;
4346 }
4347 }
4348 break;
4349 }
4350
4351#ifdef BCMDBG
4352 case WLC_INIT:
4353 wl_init(wlc->wl);
4354 break;
4355#endif
4356
4357 case WLC_SET_VAR:
4358 case WLC_GET_VAR:{
4359 char *name;
4360 /* validate the name value */
4361 name = (char *)arg;
4362 for (i = 0; i < (uint) len && *name != '\0';
62145822
JC
4363 i++, name++)
4364 ;
a9533e7e
HP
4365
4366 if (i == (uint) len) {
4367 bcmerror = BCME_BUFTOOSHORT;
4368 break;
4369 }
4370 i++; /* include the null in the string length */
4371
4372 if (cmd == WLC_GET_VAR) {
4373 bcmerror =
4374 wlc_iovar_op(wlc, arg,
562c8850 4375 (void *)((s8 *) arg + i),
a9533e7e
HP
4376 len - i, arg, len, IOV_GET,
4377 wlcif);
4378 } else
4379 bcmerror =
4380 wlc_iovar_op(wlc, arg, NULL, 0,
562c8850 4381 (void *)((s8 *) arg + i),
a9533e7e
HP
4382 len - i, IOV_SET, wlcif);
4383
4384 break;
4385 }
4386
4387 case WLC_SET_WSEC_PMK:
4388 bcmerror = BCME_UNSUPPORTED;
4389 break;
4390
4391#if defined(BCMDBG)
4392 case WLC_CURRENT_PWR:
4393 if (!wlc->pub->up)
4394 bcmerror = BCME_NOTUP;
4395 else
4396 bcmerror = wlc_get_current_txpwr(wlc, arg, len);
4397 break;
4398#endif
4399
4400 case WLC_LAST:
4401 WL_ERROR(("%s: WLC_LAST\n", __func__));
4402 }
4403 done:
4404
4405 if (bcmerror) {
4406 if (VALID_BCMERROR(bcmerror))
4407 wlc->pub->bcmerror = bcmerror;
4408 else {
4409 bcmerror = 0;
4410 }
4411
4412 }
4413#ifdef WLC_LOW
4414 /* BMAC_NOTE: for HIGH_ONLY driver, this seems being called after RPC bus failed */
4415 /* In hw_off condition, IOCTLs that reach here are deemed safe but taclear would
4416 * certainly result in getting -1 for register reads. So skip ta_clear altogether
4417 */
4418 if (!(wlc->pub->hw_off))
4419 ASSERT(wlc_bmac_taclear(wlc->hw, ta_ok) || !ta_ok);
4420#endif
4421
90ea2296 4422 return bcmerror;
a9533e7e
HP
4423}
4424
4425#if defined(BCMDBG)
4426/* consolidated register access ioctl error checking */
7cc4a4c0 4427int wlc_iocregchk(wlc_info_t *wlc, uint band)
a9533e7e
HP
4428{
4429 /* if band is specified, it must be the current band */
4430 if ((band != WLC_BAND_AUTO) && (band != (uint) wlc->band->bandtype))
90ea2296 4431 return BCME_BADBAND;
a9533e7e
HP
4432
4433 /* if multiband and band is not specified, band must be locked */
4434 if ((band == WLC_BAND_AUTO) && IS_MBAND_UNLOCKED(wlc))
90ea2296 4435 return BCME_NOTBANDLOCKED;
a9533e7e
HP
4436
4437 /* must have core clocks */
4438 if (!wlc->clk)
90ea2296 4439 return BCME_NOCLK;
a9533e7e 4440
90ea2296 4441 return 0;
a9533e7e
HP
4442}
4443#endif /* defined(BCMDBG) */
4444
4445#if defined(BCMDBG)
4446/* For some ioctls, make sure that the pi pointer matches the current phy */
7cc4a4c0 4447int wlc_iocpichk(wlc_info_t *wlc, uint phytype)
a9533e7e
HP
4448{
4449 if (wlc->band->phytype != phytype)
4450 return BCME_BADBAND;
4451 return 0;
4452}
4453#endif
4454
4455/* Look up the given var name in the given table */
7cc4a4c0 4456static const bcm_iovar_t *wlc_iovar_lookup(const bcm_iovar_t *table,
a9533e7e
HP
4457 const char *name)
4458{
4459 const bcm_iovar_t *vi;
4460 const char *lookup_name;
4461
4462 /* skip any ':' delimited option prefixes */
4463 lookup_name = strrchr(name, ':');
4464 if (lookup_name != NULL)
4465 lookup_name++;
4466 else
4467 lookup_name = name;
4468
4469 ASSERT(table != NULL);
4470
4471 for (vi = table; vi->name; vi++) {
4472 if (!strcmp(vi->name, lookup_name))
4473 return vi;
4474 }
4475 /* ran to end of table */
4476
4477 return NULL; /* var name not found */
4478}
4479
4480/* simplified integer get interface for common WLC_GET_VAR ioctl handler */
7cc4a4c0 4481int wlc_iovar_getint(wlc_info_t *wlc, const char *name, int *arg)
a9533e7e 4482{
3e26416e 4483 return wlc_iovar_op(wlc, name, NULL, 0, arg, sizeof(s32), IOV_GET,
a9533e7e
HP
4484 NULL);
4485}
4486
4487/* simplified integer set interface for common WLC_SET_VAR ioctl handler */
7cc4a4c0 4488int wlc_iovar_setint(wlc_info_t *wlc, const char *name, int arg)
a9533e7e
HP
4489{
4490 return wlc_iovar_op(wlc, name, NULL, 0, (void *)&arg, sizeof(arg),
4491 IOV_SET, NULL);
4492}
4493
562c8850
GKH
4494/* simplified s8 get interface for common WLC_GET_VAR ioctl handler */
4495int wlc_iovar_gets8(wlc_info_t *wlc, const char *name, s8 *arg)
a9533e7e
HP
4496{
4497 int iovar_int;
4498 int err;
4499
4500 err =
4501 wlc_iovar_op(wlc, name, NULL, 0, &iovar_int, sizeof(iovar_int),
4502 IOV_GET, NULL);
4503 if (!err)
562c8850 4504 *arg = (s8) iovar_int;
a9533e7e
HP
4505
4506 return err;
4507}
4508
4509/*
4510 * register iovar table, watchdog and down handlers.
4511 * calling function must keep 'iovars' until wlc_module_unregister is called.
4512 * 'iovar' must have the last entry's name field being NULL as terminator.
4513 */
0d2f0724
GKH
4514int wlc_module_register(wlc_pub_t *pub, const bcm_iovar_t *iovars,
4515 const char *name, void *hdl, iovar_fn_t i_fn,
4516 watchdog_fn_t w_fn, down_fn_t d_fn)
4517{
a9533e7e
HP
4518 wlc_info_t *wlc = (wlc_info_t *) pub->wlc;
4519 int i;
4520
4521 ASSERT(name != NULL);
4522 ASSERT(i_fn != NULL || w_fn != NULL || d_fn != NULL);
4523
4524 /* find an empty entry and just add, no duplication check! */
4525 for (i = 0; i < WLC_MAXMODULES; i++) {
4526 if (wlc->modulecb[i].name[0] == '\0') {
4527 strncpy(wlc->modulecb[i].name, name,
4528 sizeof(wlc->modulecb[i].name) - 1);
4529 wlc->modulecb[i].iovars = iovars;
4530 wlc->modulecb[i].hdl = hdl;
4531 wlc->modulecb[i].iovar_fn = i_fn;
4532 wlc->modulecb[i].watchdog_fn = w_fn;
4533 wlc->modulecb[i].down_fn = d_fn;
4534 return 0;
4535 }
4536 }
4537
4538 /* it is time to increase the capacity */
4539 ASSERT(i < WLC_MAXMODULES);
4540 return BCME_NORESOURCE;
4541}
4542
4543/* unregister module callbacks */
0d2f0724
GKH
4544int wlc_module_unregister(wlc_pub_t *pub, const char *name, void *hdl)
4545{
a9533e7e
HP
4546 wlc_info_t *wlc = (wlc_info_t *) pub->wlc;
4547 int i;
4548
4549 if (wlc == NULL)
4550 return BCME_NOTFOUND;
4551
4552 ASSERT(name != NULL);
4553
4554 for (i = 0; i < WLC_MAXMODULES; i++) {
4555 if (!strcmp(wlc->modulecb[i].name, name) &&
4556 (wlc->modulecb[i].hdl == hdl)) {
4557 bzero(&wlc->modulecb[i], sizeof(modulecb_t));
4558 return 0;
4559 }
4560 }
4561
4562 /* table not found! */
4563 return BCME_NOTFOUND;
4564}
4565
4566/* Write WME tunable parameters for retransmit/max rate from wlc struct to ucode */
7cc4a4c0 4567static void wlc_wme_retries_write(wlc_info_t *wlc)
a9533e7e
HP
4568{
4569 int ac;
4570
4571 /* Need clock to do this */
4572 if (!wlc->clk)
4573 return;
4574
4575 for (ac = 0; ac < AC_COUNT; ac++) {
4576 wlc_write_shm(wlc, M_AC_TXLMT_ADDR(ac), wlc->wme_retries[ac]);
4577 }
4578}
4579
4580/* Get or set an iovar. The params/p_len pair specifies any additional
4581 * qualifying parameters (e.g. an "element index") for a get, while the
4582 * arg/len pair is the buffer for the value to be set or retrieved.
4583 * Operation (get/set) is specified by the last argument.
4584 * interface context provided by wlcif
4585 *
4586 * All pointers may point into the same buffer.
4587 */
4588int
7cc4a4c0 4589wlc_iovar_op(wlc_info_t *wlc, const char *name,
a9533e7e
HP
4590 void *params, int p_len, void *arg, int len,
4591 bool set, struct wlc_if *wlcif)
4592{
4593 int err = 0;
4594 int val_size;
4595 const bcm_iovar_t *vi = NULL;
66cbd3ab 4596 u32 actionid;
a9533e7e
HP
4597 int i;
4598
4599 ASSERT(name != NULL);
4600
4601 ASSERT(len >= 0);
4602
4603 /* Get MUST have return space */
4604 ASSERT(set || (arg && len));
4605
4606 ASSERT(!(wlc->pub->hw_off && wlc->pub->up));
4607
4608 /* Set does NOT take qualifiers */
4609 ASSERT(!set || (!params && !p_len));
4610
4611 if (!set && (len == sizeof(int)) &&
f024c48a 4612 !(IS_ALIGNED((unsigned long)(arg), (uint) sizeof(int)))) {
a9533e7e
HP
4613 WL_ERROR(("wl%d: %s unaligned get ptr for %s\n",
4614 wlc->pub->unit, __func__, name));
4615 ASSERT(0);
4616 }
4617
4618 /* find the given iovar name */
4619 for (i = 0; i < WLC_MAXMODULES; i++) {
4620 if (!wlc->modulecb[i].iovars)
4621 continue;
ca8c1e59
JC
4622 vi = wlc_iovar_lookup(wlc->modulecb[i].iovars, name);
4623 if (vi)
a9533e7e
HP
4624 break;
4625 }
4626 /* iovar name not found */
4627 if (i >= WLC_MAXMODULES) {
4628 err = BCME_UNSUPPORTED;
4629#ifdef WLC_HIGH_ONLY
4630 err =
4631 bcmsdh_iovar_op(wlc->btparam, name, params, p_len, arg, len,
4632 set);
4633#endif
4634 goto exit;
4635 }
4636
4637 /* set up 'params' pointer in case this is a set command so that
4638 * the convenience int and bool code can be common to set and get
4639 */
4640 if (params == NULL) {
4641 params = arg;
4642 p_len = len;
4643 }
4644
4645 if (vi->type == IOVT_VOID)
4646 val_size = 0;
4647 else if (vi->type == IOVT_BUFFER)
4648 val_size = len;
4649 else
4650 /* all other types are integer sized */
4651 val_size = sizeof(int);
4652
4653 actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
4654
4655 /* Do the actual parameter implementation */
4656 err = wlc->modulecb[i].iovar_fn(wlc->modulecb[i].hdl, vi, actionid,
4657 name, params, p_len, arg, len, val_size,
4658 wlcif);
4659
4660 exit:
4661 return err;
4662}
4663
4664int
7cc4a4c0 4665wlc_iovar_check(wlc_pub_t *pub, const bcm_iovar_t *vi, void *arg, int len,
a9533e7e
HP
4666 bool set)
4667{
4668 wlc_info_t *wlc = (wlc_info_t *) pub->wlc;
4669 int err = 0;
3e26416e 4670 s32 int_val = 0;
a9533e7e
HP
4671
4672 /* check generic condition flags */
4673 if (set) {
4674 if (((vi->flags & IOVF_SET_DOWN) && wlc->pub->up) ||
4675 ((vi->flags & IOVF_SET_UP) && !wlc->pub->up)) {
4676 err = (wlc->pub->up ? BCME_NOTDOWN : BCME_NOTUP);
4677 } else if ((vi->flags & IOVF_SET_BAND)
4678 && IS_MBAND_UNLOCKED(wlc)) {
4679 err = BCME_NOTBANDLOCKED;
4680 } else if ((vi->flags & IOVF_SET_CLK) && !wlc->clk) {
4681 err = BCME_NOCLK;
4682 }
4683 } else {
4684 if (((vi->flags & IOVF_GET_DOWN) && wlc->pub->up) ||
4685 ((vi->flags & IOVF_GET_UP) && !wlc->pub->up)) {
4686 err = (wlc->pub->up ? BCME_NOTDOWN : BCME_NOTUP);
4687 } else if ((vi->flags & IOVF_GET_BAND)
4688 && IS_MBAND_UNLOCKED(wlc)) {
4689 err = BCME_NOTBANDLOCKED;
4690 } else if ((vi->flags & IOVF_GET_CLK) && !wlc->clk) {
4691 err = BCME_NOCLK;
4692 }
4693 }
4694
4695 if (err)
4696 goto exit;
4697
4698 /* length check on io buf */
ca8c1e59
JC
4699 err = bcm_iovar_lencheck(vi, arg, len, set);
4700 if (err)
a9533e7e
HP
4701 goto exit;
4702
4703 /* On set, check value ranges for integer types */
4704 if (set) {
4705 switch (vi->type) {
4706 case IOVT_BOOL:
4707 case IOVT_INT8:
4708 case IOVT_INT16:
4709 case IOVT_INT32:
4710 case IOVT_UINT8:
4711 case IOVT_UINT16:
4712 case IOVT_UINT32:
4713 bcopy(arg, &int_val, sizeof(int));
4714 err = wlc_iovar_rangecheck(wlc, int_val, vi);
4715 break;
4716 }
4717 }
4718 exit:
4719 return err;
4720}
4721
4722/* handler for iovar table wlc_iovars */
4723/*
4724 * IMPLEMENTATION NOTE: In order to avoid checking for get/set in each
4725 * iovar case, the switch statement maps the iovar id into separate get
4726 * and set values. If you add a new iovar to the switch you MUST use
4727 * IOV_GVAL and/or IOV_SVAL in the case labels to avoid conflict with
4728 * another case.
4729 * Please use params for additional qualifying parameters.
4730 */
4731int
66cbd3ab 4732wlc_doiovar(void *hdl, const bcm_iovar_t *vi, u32 actionid,
a9533e7e
HP
4733 const char *name, void *params, uint p_len, void *arg, int len,
4734 int val_size, struct wlc_if *wlcif)
4735{
4736 wlc_info_t *wlc = hdl;
4737 wlc_bsscfg_t *bsscfg;
4738 int err = 0;
3e26416e
GKH
4739 s32 int_val = 0;
4740 s32 int_val2 = 0;
4741 s32 *ret_int_ptr;
a9533e7e
HP
4742 bool bool_val;
4743 bool bool_val2;
4744 wlc_bss_info_t *current_bss;
4745
4746 WL_TRACE(("wl%d: %s\n", wlc->pub->unit, __func__));
4747
4748 bsscfg = NULL;
4749 current_bss = NULL;
4750
ca8c1e59
JC
4751 err = wlc_iovar_check(wlc->pub, vi, arg, len, IOV_ISSET(actionid));
4752 if (err != 0)
a9533e7e
HP
4753 return err;
4754
4755 /* convenience int and bool vals for first 8 bytes of buffer */
4756 if (p_len >= (int)sizeof(int_val))
4757 bcopy(params, &int_val, sizeof(int_val));
4758
4759 if (p_len >= (int)sizeof(int_val) * 2)
f024c48a 4760 bcopy((void *)((unsigned long)params + sizeof(int_val)), &int_val2,
a9533e7e
HP
4761 sizeof(int_val));
4762
4763 /* convenience int ptr for 4-byte gets (requires int aligned arg) */
3e26416e 4764 ret_int_ptr = (s32 *) arg;
a9533e7e 4765
0965ae88
GKH
4766 bool_val = (int_val != 0) ? true : false;
4767 bool_val2 = (int_val2 != 0) ? true : false;
a9533e7e
HP
4768
4769 WL_TRACE(("wl%d: %s: id %d\n", wlc->pub->unit, __func__,
4770 IOV_ID(actionid)));
4771 /* Do the actual parameter implementation */
4772 switch (actionid) {
4773
4774 case IOV_GVAL(IOV_QTXPOWER):{
4775 uint qdbm;
4776 bool override;
4777
ca8c1e59
JC
4778 err = wlc_phy_txpower_get(wlc->band->pi, &qdbm,
4779 &override);
4780 if (err != BCME_OK)
a9533e7e
HP
4781 return err;
4782
4783 /* Return qdbm units */
4784 *ret_int_ptr =
4785 qdbm | (override ? WL_TXPWR_OVERRIDE : 0);
4786 break;
4787 }
4788
4789 /* As long as override is false, this only sets the *user* targets.
4790 User can twiddle this all he wants with no harm.
4791 wlc_phy_txpower_set() explicitly sets override to false if
4792 not internal or test.
4793 */
4794 case IOV_SVAL(IOV_QTXPOWER):{
41feb5ed 4795 u8 qdbm;
a9533e7e
HP
4796 bool override;
4797
4798 /* Remove override bit and clip to max qdbm value */
a300ce95 4799 qdbm = (u8)min_t(u32, (int_val & ~WL_TXPWR_OVERRIDE), 0xff);
a9533e7e 4800 /* Extract override setting */
0965ae88 4801 override = (int_val & WL_TXPWR_OVERRIDE) ? true : false;
a9533e7e
HP
4802 err =
4803 wlc_phy_txpower_set(wlc->band->pi, qdbm, override);
4804 break;
4805 }
4806
4807 case IOV_GVAL(IOV_MPC):
3e26416e 4808 *ret_int_ptr = (s32) wlc->mpc;
a9533e7e
HP
4809 break;
4810
4811 case IOV_SVAL(IOV_MPC):
4812 wlc->mpc = bool_val;
4813 wlc_radio_mpc_upd(wlc);
4814
4815 break;
4816
4817 case IOV_GVAL(IOV_BCN_LI_BCN):
4818 *ret_int_ptr = wlc->bcn_li_bcn;
4819 break;
4820
4821 case IOV_SVAL(IOV_BCN_LI_BCN):
41feb5ed 4822 wlc->bcn_li_bcn = (u8) int_val;
a9533e7e
HP
4823 if (wlc->pub->up)
4824 wlc_bcn_li_upd(wlc);
4825 break;
4826
4827 default:
4828 WL_ERROR(("wl%d: %s: unsupported\n", wlc->pub->unit, __func__));
4829 err = BCME_UNSUPPORTED;
4830 break;
4831 }
4832
4833 goto exit; /* avoid unused label warning */
4834
4835 exit:
4836 return err;
4837}
4838
4839static int
66cbd3ab 4840wlc_iovar_rangecheck(wlc_info_t *wlc, u32 val, const bcm_iovar_t *vi)
a9533e7e
HP
4841{
4842 int err = 0;
66cbd3ab
GKH
4843 u32 min_val = 0;
4844 u32 max_val = 0;
a9533e7e
HP
4845
4846 /* Only ranged integers are checked */
4847 switch (vi->type) {
4848 case IOVT_INT32:
4849 max_val |= 0x7fffffff;
4850 /* fall through */
4851 case IOVT_INT16:
4852 max_val |= 0x00007fff;
4853 /* fall through */
4854 case IOVT_INT8:
4855 max_val |= 0x0000007f;
4856 min_val = ~max_val;
4857 if (vi->flags & IOVF_NTRL)
4858 min_val = 1;
4859 else if (vi->flags & IOVF_WHL)
4860 min_val = 0;
4861 /* Signed values are checked against max_val and min_val */
3e26416e
GKH
4862 if ((s32) val < (s32) min_val
4863 || (s32) val > (s32) max_val)
a9533e7e
HP
4864 err = BCME_RANGE;
4865 break;
4866
4867 case IOVT_UINT32:
4868 max_val |= 0xffffffff;
4869 /* fall through */
4870 case IOVT_UINT16:
4871 max_val |= 0x0000ffff;
4872 /* fall through */
4873 case IOVT_UINT8:
4874 max_val |= 0x000000ff;
4875 if (vi->flags & IOVF_NTRL)
4876 min_val = 1;
4877 if ((val < min_val) || (val > max_val))
4878 err = BCME_RANGE;
4879 break;
4880 }
4881
4882 return err;
4883}
4884
a9533e7e
HP
4885#ifdef BCMDBG
4886static const char *supr_reason[] = {
4887 "None", "PMQ Entry", "Flush request",
4888 "Previous frag failure", "Channel mismatch",
4889 "Lifetime Expiry", "Underflow"
4890};
4891
7d4df48e 4892static void wlc_print_txs_status(u16 s)
a9533e7e
HP
4893{
4894 printf("[15:12] %d frame attempts\n", (s & TX_STATUS_FRM_RTX_MASK) >>
4895 TX_STATUS_FRM_RTX_SHIFT);
4896 printf(" [11:8] %d rts attempts\n", (s & TX_STATUS_RTS_RTX_MASK) >>
4897 TX_STATUS_RTS_RTX_SHIFT);
4898 printf(" [7] %d PM mode indicated\n",
4899 ((s & TX_STATUS_PMINDCTD) ? 1 : 0));
4900 printf(" [6] %d intermediate status\n",
4901 ((s & TX_STATUS_INTERMEDIATE) ? 1 : 0));
4902 printf(" [5] %d AMPDU\n", (s & TX_STATUS_AMPDU) ? 1 : 0);
4903 printf(" [4:2] %d Frame Suppressed Reason (%s)\n",
4904 ((s & TX_STATUS_SUPR_MASK) >> TX_STATUS_SUPR_SHIFT),
4905 supr_reason[(s & TX_STATUS_SUPR_MASK) >> TX_STATUS_SUPR_SHIFT]);
4906 printf(" [1] %d acked\n", ((s & TX_STATUS_ACK_RCV) ? 1 : 0));
4907}
4908#endif /* BCMDBG */
4909
7cc4a4c0 4910void wlc_print_txstatus(tx_status_t *txs)
a9533e7e
HP
4911{
4912#if defined(BCMDBG)
7d4df48e
GKH
4913 u16 s = txs->status;
4914 u16 ackphyrxsh = txs->ackphyrxsh;
a9533e7e
HP
4915
4916 printf("\ntxpkt (MPDU) Complete\n");
4917
4918 printf("FrameID: %04x ", txs->frameid);
4919 printf("TxStatus: %04x", s);
4920 printf("\n");
4921#ifdef BCMDBG
4922 wlc_print_txs_status(s);
4923#endif
4924 printf("LastTxTime: %04x ", txs->lasttxtime);
4925 printf("Seq: %04x ", txs->sequence);
4926 printf("PHYTxStatus: %04x ", txs->phyerr);
4927 printf("RxAckRSSI: %04x ",
4928 (ackphyrxsh & PRXS1_JSSI_MASK) >> PRXS1_JSSI_SHIFT);
4929 printf("RxAckSQ: %04x", (ackphyrxsh & PRXS1_SQ_MASK) >> PRXS1_SQ_SHIFT);
4930 printf("\n");
4931#endif /* defined(BCMDBG) */
4932}
4933
4934#define MACSTATUPD(name) \
4935 wlc_ctrupd_cache(macstats.name, &wlc->core->macstat_snapshot->name, &wlc->pub->_cnt->name)
4936
7cc4a4c0 4937void wlc_statsupd(wlc_info_t *wlc)
a9533e7e
HP
4938{
4939 int i;
4940#ifdef BCMDBG
7d4df48e
GKH
4941 u16 delta;
4942 u16 rxf0ovfl;
4943 u16 txfunfl[NFIFO];
a9533e7e
HP
4944#endif /* BCMDBG */
4945
4946 /* if driver down, make no sense to update stats */
4947 if (!wlc->pub->up)
4948 return;
4949
4950#ifdef BCMDBG
4951 /* save last rx fifo 0 overflow count */
4952 rxf0ovfl = wlc->core->macstat_snapshot->rxf0ovfl;
4953
4954 /* save last tx fifo underflow count */
4955 for (i = 0; i < NFIFO; i++)
4956 txfunfl[i] = wlc->core->macstat_snapshot->txfunfl[i];
4957#endif /* BCMDBG */
4958
4959#ifdef BCMDBG
4960 /* check for rx fifo 0 overflow */
7d4df48e 4961 delta = (u16) (wlc->core->macstat_snapshot->rxf0ovfl - rxf0ovfl);
a9533e7e
HP
4962 if (delta)
4963 WL_ERROR(("wl%d: %u rx fifo 0 overflows!\n", wlc->pub->unit,
4964 delta));
4965
4966 /* check for tx fifo underflows */
4967 for (i = 0; i < NFIFO; i++) {
4968 delta =
7d4df48e 4969 (u16) (wlc->core->macstat_snapshot->txfunfl[i] -
a9533e7e
HP
4970 txfunfl[i]);
4971 if (delta)
4972 WL_ERROR(("wl%d: %u tx fifo %d underflows!\n",
4973 wlc->pub->unit, delta, i));
4974 }
4975#endif /* BCMDBG */
4976
4977 /* dot11 counter update */
4978
4979 WLCNTSET(wlc->pub->_cnt->txrts,
4980 (wlc->pub->_cnt->rxctsucast -
4981 wlc->pub->_cnt->d11cnt_txrts_off));
4982 WLCNTSET(wlc->pub->_cnt->rxcrc,
4983 (wlc->pub->_cnt->rxbadfcs - wlc->pub->_cnt->d11cnt_rxcrc_off));
4984 WLCNTSET(wlc->pub->_cnt->txnocts,
4985 ((wlc->pub->_cnt->txrtsfrm - wlc->pub->_cnt->rxctsucast) -
4986 wlc->pub->_cnt->d11cnt_txnocts_off));
4987
4988 /* merge counters from dma module */
4989 for (i = 0; i < NFIFO; i++) {
4990 if (wlc->hw->di[i]) {
4991 WLCNTADD(wlc->pub->_cnt->txnobuf,
4992 (wlc->hw->di[i])->txnobuf);
4993 WLCNTADD(wlc->pub->_cnt->rxnobuf,
4994 (wlc->hw->di[i])->rxnobuf);
4995 WLCNTADD(wlc->pub->_cnt->rxgiant,
4996 (wlc->hw->di[i])->rxgiants);
4997 dma_counterreset(wlc->hw->di[i]);
4998 }
4999 }
5000
5001 /*
5002 * Aggregate transmit and receive errors that probably resulted
5003 * in the loss of a frame are computed on the fly.
5004 */
5005 WLCNTSET(wlc->pub->_cnt->txerror,
5006 wlc->pub->_cnt->txnobuf + wlc->pub->_cnt->txnoassoc +
5007 wlc->pub->_cnt->txuflo + wlc->pub->_cnt->txrunt +
5008 wlc->pub->_cnt->dmade + wlc->pub->_cnt->dmada +
5009 wlc->pub->_cnt->dmape);
5010 WLCNTSET(wlc->pub->_cnt->rxerror,
5011 wlc->pub->_cnt->rxoflo + wlc->pub->_cnt->rxnobuf +
5012 wlc->pub->_cnt->rxfragerr + wlc->pub->_cnt->rxrunt +
5013 wlc->pub->_cnt->rxgiant + wlc->pub->_cnt->rxnoscb +
5014 wlc->pub->_cnt->rxbadsrcmac);
5015 for (i = 0; i < NFIFO; i++)
5016 WLCNTADD(wlc->pub->_cnt->rxerror, wlc->pub->_cnt->rxuflo[i]);
5017}
5018
7d4df48e 5019bool wlc_chipmatch(u16 vendor, u16 device)
a9533e7e
HP
5020{
5021 if (vendor != VENDOR_BROADCOM) {
5022 WL_ERROR(("wlc_chipmatch: unknown vendor id %04x\n", vendor));
0965ae88 5023 return false;
a9533e7e
HP
5024 }
5025
5026 if ((device == BCM43224_D11N_ID) || (device == BCM43225_D11N2G_ID))
0f0881b0 5027 return true;
a9533e7e
HP
5028
5029 if (device == BCM4313_D11N2G_ID)
0f0881b0 5030 return true;
a9533e7e 5031 if ((device == BCM43236_D11N_ID) || (device == BCM43236_D11N2G_ID))
0f0881b0 5032 return true;
a9533e7e
HP
5033
5034 WL_ERROR(("wlc_chipmatch: unknown device id %04x\n", device));
0965ae88 5035 return false;
a9533e7e
HP
5036}
5037
a9533e7e 5038#if defined(BCMDBG)
7cc4a4c0 5039void wlc_print_txdesc(d11txh_t *txh)
a9533e7e 5040{
7d4df48e
GKH
5041 u16 mtcl = ltoh16(txh->MacTxControlLow);
5042 u16 mtch = ltoh16(txh->MacTxControlHigh);
5043 u16 mfc = ltoh16(txh->MacFrameControl);
5044 u16 tfest = ltoh16(txh->TxFesTimeNormal);
5045 u16 ptcw = ltoh16(txh->PhyTxControlWord);
5046 u16 ptcw_1 = ltoh16(txh->PhyTxControlWord_1);
5047 u16 ptcw_1_Fbr = ltoh16(txh->PhyTxControlWord_1_Fbr);
5048 u16 ptcw_1_Rts = ltoh16(txh->PhyTxControlWord_1_Rts);
5049 u16 ptcw_1_FbrRts = ltoh16(txh->PhyTxControlWord_1_FbrRts);
5050 u16 mainrates = ltoh16(txh->MainRates);
5051 u16 xtraft = ltoh16(txh->XtraFrameTypes);
41feb5ed
GKH
5052 u8 *iv = txh->IV;
5053 u8 *ra = txh->TxFrameRA;
7d4df48e 5054 u16 tfestfb = ltoh16(txh->TxFesTimeFallback);
41feb5ed 5055 u8 *rtspfb = txh->RTSPLCPFallback;
7d4df48e 5056 u16 rtsdfb = ltoh16(txh->RTSDurFallback);
41feb5ed 5057 u8 *fragpfb = txh->FragPLCPFallback;
7d4df48e
GKH
5058 u16 fragdfb = ltoh16(txh->FragDurFallback);
5059 u16 mmodelen = ltoh16(txh->MModeLen);
5060 u16 mmodefbrlen = ltoh16(txh->MModeFbrLen);
5061 u16 tfid = ltoh16(txh->TxFrameID);
5062 u16 txs = ltoh16(txh->TxStatus);
5063 u16 mnmpdu = ltoh16(txh->MaxNMpdus);
5064 u16 mabyte = ltoh16(txh->MaxABytes_MRT);
5065 u16 mabyte_f = ltoh16(txh->MaxABytes_FBR);
5066 u16 mmbyte = ltoh16(txh->MinMBytes);
a9533e7e 5067
41feb5ed 5068 u8 *rtsph = txh->RTSPhyHeader;
a9533e7e
HP
5069 struct dot11_rts_frame rts = txh->rts_frame;
5070 char hexbuf[256];
5071
5072 /* add plcp header along with txh descriptor */
580a0bd9 5073 prhex("Raw TxDesc + plcp header", (unsigned char *) txh, sizeof(d11txh_t) + 48);
a9533e7e
HP
5074
5075 printf("TxCtlLow: %04x ", mtcl);
5076 printf("TxCtlHigh: %04x ", mtch);
5077 printf("FC: %04x ", mfc);
5078 printf("FES Time: %04x\n", tfest);
5079 printf("PhyCtl: %04x%s ", ptcw,
5080 (ptcw & PHY_TXC_SHORT_HDR) ? " short" : "");
5081 printf("PhyCtl_1: %04x ", ptcw_1);
5082 printf("PhyCtl_1_Fbr: %04x\n", ptcw_1_Fbr);
5083 printf("PhyCtl_1_Rts: %04x ", ptcw_1_Rts);
5084 printf("PhyCtl_1_Fbr_Rts: %04x\n", ptcw_1_FbrRts);
5085 printf("MainRates: %04x ", mainrates);
5086 printf("XtraFrameTypes: %04x ", xtraft);
5087 printf("\n");
5088
5089 bcm_format_hex(hexbuf, iv, sizeof(txh->IV));
5090 printf("SecIV: %s\n", hexbuf);
5091 bcm_format_hex(hexbuf, ra, sizeof(txh->TxFrameRA));
5092 printf("RA: %s\n", hexbuf);
5093
5094 printf("Fb FES Time: %04x ", tfestfb);
5095 bcm_format_hex(hexbuf, rtspfb, sizeof(txh->RTSPLCPFallback));
5096 printf("RTS PLCP: %s ", hexbuf);
5097 printf("RTS DUR: %04x ", rtsdfb);
5098 bcm_format_hex(hexbuf, fragpfb, sizeof(txh->FragPLCPFallback));
5099 printf("PLCP: %s ", hexbuf);
5100 printf("DUR: %04x", fragdfb);
5101 printf("\n");
5102
5103 printf("MModeLen: %04x ", mmodelen);
5104 printf("MModeFbrLen: %04x\n", mmodefbrlen);
5105
5106 printf("FrameID: %04x\n", tfid);
5107 printf("TxStatus: %04x\n", txs);
5108
5109 printf("MaxNumMpdu: %04x\n", mnmpdu);
5110 printf("MaxAggbyte: %04x\n", mabyte);
5111 printf("MaxAggbyte_fb: %04x\n", mabyte_f);
5112 printf("MinByte: %04x\n", mmbyte);
5113
5114 bcm_format_hex(hexbuf, rtsph, sizeof(txh->RTSPhyHeader));
5115 printf("RTS PLCP: %s ", hexbuf);
41feb5ed 5116 bcm_format_hex(hexbuf, (u8 *) &rts, sizeof(txh->rts_frame));
a9533e7e
HP
5117 printf("RTS Frame: %s", hexbuf);
5118 printf("\n");
5119
a9533e7e
HP
5120}
5121#endif /* defined(BCMDBG) */
5122
5123#if defined(BCMDBG)
7cc4a4c0 5124void wlc_print_rxh(d11rxhdr_t *rxh)
a9533e7e 5125{
7d4df48e
GKH
5126 u16 len = rxh->RxFrameSize;
5127 u16 phystatus_0 = rxh->PhyRxStatus_0;
5128 u16 phystatus_1 = rxh->PhyRxStatus_1;
5129 u16 phystatus_2 = rxh->PhyRxStatus_2;
5130 u16 phystatus_3 = rxh->PhyRxStatus_3;
5131 u16 macstatus1 = rxh->RxStatus1;
5132 u16 macstatus2 = rxh->RxStatus2;
a9533e7e
HP
5133 char flagstr[64];
5134 char lenbuf[20];
5135 static const bcm_bit_desc_t macstat_flags[] = {
5136 {RXS_FCSERR, "FCSErr"},
5137 {RXS_RESPFRAMETX, "Reply"},
5138 {RXS_PBPRES, "PADDING"},
5139 {RXS_DECATMPT, "DeCr"},
5140 {RXS_DECERR, "DeCrErr"},
5141 {RXS_BCNSENT, "Bcn"},
5142 {0, NULL}
5143 };
5144
580a0bd9 5145 prhex("Raw RxDesc", (unsigned char *) rxh, sizeof(d11rxhdr_t));
a9533e7e
HP
5146
5147 bcm_format_flags(macstat_flags, macstatus1, flagstr, 64);
5148
5149 snprintf(lenbuf, sizeof(lenbuf), "0x%x", len);
5150
5151 printf("RxFrameSize: %6s (%d)%s\n", lenbuf, len,
5152 (rxh->PhyRxStatus_0 & PRXS0_SHORTH) ? " short preamble" : "");
5153 printf("RxPHYStatus: %04x %04x %04x %04x\n",
5154 phystatus_0, phystatus_1, phystatus_2, phystatus_3);
5155 printf("RxMACStatus: %x %s\n", macstatus1, flagstr);
5156 printf("RXMACaggtype: %x\n", (macstatus2 & RXS_AGGTYPE_MASK));
5157 printf("RxTSFTime: %04x\n", rxh->RxTSFTime);
5158}
a9533e7e
HP
5159#endif /* defined(BCMDBG) */
5160
5161#if defined(BCMDBG)
580a0bd9 5162int wlc_format_ssid(char *buf, const unsigned char ssid[], uint ssid_len)
a9533e7e
HP
5163{
5164 uint i, c;
5165 char *p = buf;
5166 char *endp = buf + SSID_FMT_BUF_LEN;
5167
5168 if (ssid_len > DOT11_MAX_SSID_LEN)
5169 ssid_len = DOT11_MAX_SSID_LEN;
5170
5171 for (i = 0; i < ssid_len; i++) {
5172 c = (uint) ssid[i];
5173 if (c == '\\') {
5174 *p++ = '\\';
5175 *p++ = '\\';
580a0bd9 5176 } else if (isprint((unsigned char) c)) {
a9533e7e
HP
5177 *p++ = (char)c;
5178 } else {
5179 p += snprintf(p, (endp - p), "\\x%02X", c);
5180 }
5181 }
5182 *p = '\0';
5183 ASSERT(p < endp);
5184
5185 return (int)(p - buf);
5186}
5187#endif /* defined(BCMDBG) */
5188
7d4df48e 5189u16 wlc_rate_shm_offset(wlc_info_t *wlc, u8 rate)
a9533e7e 5190{
90ea2296 5191 return wlc_bmac_rate_shm_offset(wlc->hw, rate);
a9533e7e
HP
5192}
5193
5194/* Callback for device removed */
5195#if defined(WLC_HIGH_ONLY)
5196void wlc_device_removed(void *arg)
5197{
5198 wlc_info_t *wlc = (wlc_info_t *) arg;
5199
0965ae88 5200 wlc->device_present = false;
a9533e7e
HP
5201}
5202#endif /* WLC_HIGH_ONLY */
5203
5204/*
5205 * Attempts to queue a packet onto a multiple-precedence queue,
5206 * if necessary evicting a lower precedence packet from the queue.
5207 *
5208 * 'prec' is the precedence number that has already been mapped
5209 * from the packet priority.
5210 *
0965ae88 5211 * Returns true if packet consumed (queued), false if not.
a9533e7e
HP
5212 */
5213bool BCMFASTPATH
7cc4a4c0 5214wlc_prec_enq(wlc_info_t *wlc, struct pktq *q, void *pkt, int prec)
a9533e7e 5215{
0965ae88 5216 return wlc_prec_enq_head(wlc, q, pkt, prec, false);
a9533e7e
HP
5217}
5218
5219bool BCMFASTPATH
7cc4a4c0 5220wlc_prec_enq_head(wlc_info_t *wlc, struct pktq *q, void *pkt, int prec,
a9533e7e
HP
5221 bool head)
5222{
5223 void *p;
5224 int eprec = -1; /* precedence to evict from */
5225
5226 /* Determine precedence from which to evict packet, if any */
5227 if (pktq_pfull(q, prec))
5228 eprec = prec;
5229 else if (pktq_full(q)) {
5230 p = pktq_peek_tail(q, &eprec);
5231 ASSERT(p != NULL);
5232 if (eprec > prec) {
5233 WL_ERROR(("%s: Failing: eprec %d > prec %d\n", __func__,
5234 eprec, prec));
0965ae88 5235 return false;
a9533e7e
HP
5236 }
5237 }
5238
5239 /* Evict if needed */
5240 if (eprec >= 0) {
5241 bool discard_oldest;
5242
5243 /* Detect queueing to unconfigured precedence */
5244 ASSERT(!pktq_pempty(q, eprec));
5245
5246 discard_oldest = AC_BITMAP_TST(wlc->wme_dp, eprec);
5247
5248 /* Refuse newer packet unless configured to discard oldest */
5249 if (eprec == prec && !discard_oldest) {
5250 WL_ERROR(("%s: No where to go, prec == %d\n", __func__,
5251 prec));
0965ae88 5252 return false;
a9533e7e
HP
5253 }
5254
5255 /* Evict packet according to discard policy */
5256 p = discard_oldest ? pktq_pdeq(q, eprec) : pktq_pdeq_tail(q,
5257 eprec);
5258 ASSERT(p != NULL);
5259
5260 /* Increment wme stats */
5261 if (WME_ENAB(wlc->pub)) {
5262 WLCNTINCR(wlc->pub->_wme_cnt->
5263 tx_failed[WME_PRIO2AC(PKTPRIO(p))].packets);
5264 WLCNTADD(wlc->pub->_wme_cnt->
5265 tx_failed[WME_PRIO2AC(PKTPRIO(p))].bytes,
5266 pkttotlen(wlc->osh, p));
5267 }
5268
5269 ASSERT(0);
0f0881b0 5270 PKTFREE(wlc->osh, p, true);
a9533e7e
HP
5271 WLCNTINCR(wlc->pub->_cnt->txnobuf);
5272 }
5273
5274 /* Enqueue */
5275 if (head)
5276 p = pktq_penq_head(q, prec, pkt);
5277 else
5278 p = pktq_penq(q, prec, pkt);
5279 ASSERT(p != NULL);
5280
0f0881b0 5281 return true;
a9533e7e
HP
5282}
5283
5284void BCMFASTPATH wlc_txq_enq(void *ctx, struct scb *scb, void *sdu, uint prec)
5285{
5286 wlc_info_t *wlc = (wlc_info_t *) ctx;
5287 wlc_txq_info_t *qi = wlc->active_queue; /* Check me */
5288 struct pktq *q = &qi->q;
5289 int prio;
5290
5291 prio = PKTPRIO(sdu);
5292
5293 ASSERT(pktq_max(q) >= wlc->pub->tunables->datahiwat);
5294
5295 if (!wlc_prec_enq(wlc, q, sdu, prec)) {
5296 if (!EDCF_ENAB(wlc->pub)
5297 || (wlc->pub->wlfeatureflag & WL_SWFL_FLOWCONTROL))
5298 WL_ERROR(("wl%d: wlc_txq_enq: txq overflow\n",
5299 wlc->pub->unit));
5300
5301 /* ASSERT(9 == 8); *//* XXX we might hit this condtion in case packet flooding from mac80211 stack */
0f0881b0 5302 PKTFREE(wlc->osh, sdu, true);
a9533e7e
HP
5303 WLCNTINCR(wlc->pub->_cnt->txnobuf);
5304 }
5305
5306 /* Check if flow control needs to be turned on after enqueuing the packet
5307 * Don't turn on flow control if EDCF is enabled. Driver would make the decision on what
5308 * to drop instead of relying on stack to make the right decision
5309 */
5310 if (!EDCF_ENAB(wlc->pub)
5311 || (wlc->pub->wlfeatureflag & WL_SWFL_FLOWCONTROL)) {
5312 if (pktq_len(q) >= wlc->pub->tunables->datahiwat) {
5313 wlc_txflowcontrol(wlc, qi, ON, ALLPRIO);
5314 }
5315 } else if (wlc->pub->_priofc) {
5316 if (pktq_plen(q, wlc_prio2prec_map[prio]) >=
5317 wlc->pub->tunables->datahiwat) {
5318 wlc_txflowcontrol(wlc, qi, ON, prio);
5319 }
5320 }
5321}
5322
5323bool BCMFASTPATH
7cc4a4c0 5324wlc_sendpkt_mac80211(wlc_info_t *wlc, void *sdu, struct ieee80211_hw *hw)
a9533e7e 5325{
41feb5ed 5326 u8 prio;
a9533e7e
HP
5327 uint fifo;
5328 void *pkt;
5329 struct scb *scb = &global_scb;
5330 struct dot11_header *d11_header = (struct dot11_header *)PKTDATA(sdu);
7d4df48e 5331 u16 type, fc;
a9533e7e
HP
5332
5333 ASSERT(sdu);
5334
5335 fc = ltoh16(d11_header->fc);
5336 type = FC_TYPE(fc);
5337
5338 /* 802.11 standard requires management traffic to go at highest priority */
5339 prio = (type == FC_TYPE_DATA ? PKTPRIO(sdu) : MAXPRIO);
5340 fifo = prio2fifo[prio];
5341
5342 ASSERT((uint) PKTHEADROOM(sdu) >= TXOFF);
5343 ASSERT(!PKTSHARED(sdu));
5344 ASSERT(!PKTNEXT(sdu));
5345 ASSERT(!PKTLINK(sdu));
5346 ASSERT(fifo < NFIFO);
5347
5348 pkt = sdu;
5349 if (unlikely
5350 (wlc_d11hdrs_mac80211(wlc, hw, pkt, scb, 0, 1, fifo, 0, NULL, 0)))
5351 return -EINVAL;
5352 wlc_txq_enq(wlc, scb, pkt, WLC_PRIO_TO_PREC(prio));
5353 wlc_send_q(wlc, wlc->active_queue);
5354
5355 WLCNTINCR(wlc->pub->_cnt->ieee_tx);
5356 return 0;
5357}
5358
7cc4a4c0 5359void BCMFASTPATH wlc_send_q(wlc_info_t *wlc, wlc_txq_info_t *qi)
a9533e7e
HP
5360{
5361 void *pkt[DOT11_MAXNUMFRAGS];
5362 int prec;
7d4df48e 5363 u16 prec_map;
a9533e7e
HP
5364 int err = 0, i, count;
5365 uint fifo;
5366 struct pktq *q = &qi->q;
5367 struct ieee80211_tx_info *tx_info;
5368
5369 /* only do work for the active queue */
5370 if (qi != wlc->active_queue)
5371 return;
5372
5373 if (in_send_q)
5374 return;
5375 else
0f0881b0 5376 in_send_q = true;
a9533e7e
HP
5377
5378 prec_map = wlc->tx_prec_map;
5379
5380 /* Send all the enq'd pkts that we can.
5381 * Dequeue packets with precedence with empty HW fifo only
5382 */
5383 while (prec_map && (pkt[0] = pktq_mdeq(q, prec_map, &prec))) {
5384 tx_info = IEEE80211_SKB_CB(pkt[0]);
5385 if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
5386 err = wlc_sendampdu(wlc->ampdu, qi, pkt, prec);
5387 } else {
5388 count = 1;
5389 err = wlc_prep_pdu(wlc, pkt[0], &fifo);
5390 if (!err) {
5391 for (i = 0; i < count; i++) {
0f0881b0 5392 wlc_txfifo(wlc, fifo, pkt[i], true, 1);
a9533e7e
HP
5393 }
5394 }
5395 }
5396
5397 if (err == BCME_BUSY) {
5398 pktq_penq_head(q, prec, pkt[0]);
5399 /* If send failed due to any other reason than a change in
5400 * HW FIFO condition, quit. Otherwise, read the new prec_map!
5401 */
5402 if (prec_map == wlc->tx_prec_map)
5403 break;
5404 prec_map = wlc->tx_prec_map;
5405 }
5406 }
5407
5408 /* Check if flow control needs to be turned off after sending the packet */
5409 if (!EDCF_ENAB(wlc->pub)
5410 || (wlc->pub->wlfeatureflag & WL_SWFL_FLOWCONTROL)) {
5411 if (wlc_txflowcontrol_prio_isset(wlc, qi, ALLPRIO)
5412 && (pktq_len(q) < wlc->pub->tunables->datahiwat / 2)) {
5413 wlc_txflowcontrol(wlc, qi, OFF, ALLPRIO);
5414 }
5415 } else if (wlc->pub->_priofc) {
5416 int prio;
5417 for (prio = MAXPRIO; prio >= 0; prio--) {
5418 if (wlc_txflowcontrol_prio_isset(wlc, qi, prio) &&
5419 (pktq_plen(q, wlc_prio2prec_map[prio]) <
5420 wlc->pub->tunables->datahiwat / 2)) {
5421 wlc_txflowcontrol(wlc, qi, OFF, prio);
5422 }
5423 }
5424 }
0965ae88 5425 in_send_q = false;
a9533e7e
HP
5426}
5427
5428/*
5429 * bcmc_fid_generate:
5430 * Generate frame ID for a BCMC packet. The frag field is not used
5431 * for MC frames so is used as part of the sequence number.
5432 */
7d4df48e 5433static inline u16
7cc4a4c0 5434bcmc_fid_generate(wlc_info_t *wlc, wlc_bsscfg_t *bsscfg, d11txh_t *txh)
a9533e7e 5435{
7d4df48e 5436 u16 frameid;
a9533e7e
HP
5437
5438 frameid = ltoh16(txh->TxFrameID) & ~(TXFID_SEQ_MASK | TXFID_QUEUE_MASK);
5439 frameid |=
5440 (((wlc->
5441 mc_fid_counter++) << TXFID_SEQ_SHIFT) & TXFID_SEQ_MASK) |
5442 TX_BCMC_FIFO;
5443
5444 return frameid;
5445}
5446
5447void BCMFASTPATH
562c8850 5448wlc_txfifo(wlc_info_t *wlc, uint fifo, void *p, bool commit, s8 txpktpend)
a9533e7e 5449{
7d4df48e 5450 u16 frameid = INVALIDFID;
a9533e7e
HP
5451 d11txh_t *txh;
5452
5453 ASSERT(fifo < NFIFO);
5454 txh = (d11txh_t *) PKTDATA(p);
5455
5456 /* When a BC/MC frame is being committed to the BCMC fifo via DMA (NOT PIO), update
5457 * ucode or BSS info as appropriate.
5458 */
5459 if (fifo == TX_BCMC_FIFO) {
5460 frameid = ltoh16(txh->TxFrameID);
5461
5462 }
5463
5464 if (WLC_WAR16165(wlc))
0f0881b0 5465 wlc_war16165(wlc, true);
a9533e7e
HP
5466
5467#ifdef WLC_HIGH_ONLY
5468 if (RPCTX_ENAB(wlc->pub)) {
5469 (void)wlc_rpctx_tx(wlc->rpctx, fifo, p, commit, frameid,
5470 txpktpend);
5471 return;
5472 }
5473#else
5474
5475 /* Bump up pending count for if not using rpc. If rpc is used, this will be handled
5476 * in wlc_bmac_txfifo()
5477 */
5478 if (commit) {
5479 TXPKTPENDINC(wlc, fifo, txpktpend);
5480 WL_TRACE(("wlc_txfifo, pktpend inc %d to %d\n", txpktpend,
5481 TXPKTPENDGET(wlc, fifo)));
5482 }
5483
5484 /* Commit BCMC sequence number in the SHM frame ID location */
5485 if (frameid != INVALIDFID)
5486 BCMCFID(wlc, frameid);
5487
5488 if (dma_txfast(wlc->hw->di[fifo], p, commit) < 0) {
5489 WL_ERROR(("wlc_txfifo: fatal, toss frames !!!\n"));
5490 }
5491#endif /* WLC_HIGH_ONLY */
5492}
5493
7d4df48e 5494static u16
7cc4a4c0 5495wlc_compute_airtime(wlc_info_t *wlc, ratespec_t rspec, uint length)
a9533e7e 5496{
7d4df48e 5497 u16 usec = 0;
a9533e7e
HP
5498 uint mac_rate = RSPEC2RATE(rspec);
5499 uint nsyms;
5500
5501 if (IS_MCS(rspec)) {
5502 /* not supported yet */
5503 ASSERT(0);
5504 } else if (IS_OFDM(rspec)) {
5505 /* nsyms = Ceiling(Nbits / (Nbits/sym))
5506 *
5507 * Nbits = length * 8
5508 * Nbits/sym = Mbps * 4 = mac_rate * 2
5509 */
5510 nsyms = CEIL((length * 8), (mac_rate * 2));
5511
5512 /* usec = symbols * usec/symbol */
7d4df48e 5513 usec = (u16) (nsyms * APHY_SYMBOL_TIME);
90ea2296 5514 return usec;
a9533e7e
HP
5515 } else {
5516 switch (mac_rate) {
5517 case WLC_RATE_1M:
5518 usec = length << 3;
5519 break;
5520 case WLC_RATE_2M:
5521 usec = length << 2;
5522 break;
5523 case WLC_RATE_5M5:
5524 usec = (length << 4) / 11;
5525 break;
5526 case WLC_RATE_11M:
5527 usec = (length << 3) / 11;
5528 break;
5529 default:
5530 WL_ERROR(("wl%d: wlc_compute_airtime: unsupported rspec 0x%x\n", wlc->pub->unit, rspec));
5531 ASSERT((const char *)"Bad phy_rate" == NULL);
5532 break;
5533 }
5534 }
5535
90ea2296 5536 return usec;
a9533e7e
HP
5537}
5538
5539void BCMFASTPATH
41feb5ed 5540wlc_compute_plcp(wlc_info_t *wlc, ratespec_t rspec, uint length, u8 *plcp)
a9533e7e
HP
5541{
5542 if (IS_MCS(rspec)) {
5543 wlc_compute_mimo_plcp(rspec, length, plcp);
5544 } else if (IS_OFDM(rspec)) {
5545 wlc_compute_ofdm_plcp(rspec, length, plcp);
5546 } else {
5547 wlc_compute_cck_plcp(rspec, length, plcp);
5548 }
5549 return;
5550}
5551
5552/* Rate: 802.11 rate code, length: PSDU length in octets */
41feb5ed 5553static void wlc_compute_mimo_plcp(ratespec_t rspec, uint length, u8 *plcp)
a9533e7e 5554{
41feb5ed 5555 u8 mcs = (u8) (rspec & RSPEC_RATE_MASK);
a9533e7e
HP
5556 ASSERT(IS_MCS(rspec));
5557 plcp[0] = mcs;
5558 if (RSPEC_IS40MHZ(rspec) || (mcs == 32))
5559 plcp[0] |= MIMO_PLCP_40MHZ;
5560 WLC_SET_MIMO_PLCP_LEN(plcp, length);
5561 plcp[3] = RSPEC_MIMOPLCP3(rspec); /* rspec already holds this byte */
5562 plcp[3] |= 0x7; /* set smoothing, not sounding ppdu & reserved */
5563 plcp[4] = 0; /* number of extension spatial streams bit 0 & 1 */
5564 plcp[5] = 0;
5565}
5566
5567/* Rate: 802.11 rate code, length: PSDU length in octets */
5568static void BCMFASTPATH
66cbd3ab 5569wlc_compute_ofdm_plcp(ratespec_t rspec, u32 length, u8 *plcp)
a9533e7e 5570{
41feb5ed 5571 u8 rate_signal;
66cbd3ab 5572 u32 tmp = 0;
a9533e7e
HP
5573 int rate = RSPEC2RATE(rspec);
5574
5575 ASSERT(IS_OFDM(rspec));
5576
5577 /* encode rate per 802.11a-1999 sec 17.3.4.1, with lsb transmitted first */
5578 rate_signal = rate_info[rate] & RATE_MASK;
5579 ASSERT(rate_signal != 0);
5580
5581 bzero(plcp, D11_PHY_HDR_LEN);
5582 D11A_PHY_HDR_SRATE((ofdm_phy_hdr_t *) plcp, rate_signal);
5583
5584 tmp = (length & 0xfff) << 5;
5585 plcp[2] |= (tmp >> 16) & 0xff;
5586 plcp[1] |= (tmp >> 8) & 0xff;
5587 plcp[0] |= tmp & 0xff;
5588
5589 return;
5590}
5591
5592/*
5593 * Compute PLCP, but only requires actual rate and length of pkt.
5594 * Rate is given in the driver standard multiple of 500 kbps.
5595 * le is set for 11 Mbps rate if necessary.
5596 * Broken out for PRQ.
5597 */
5598
41feb5ed 5599static void wlc_cck_plcp_set(int rate_500, uint length, u8 *plcp)
a9533e7e 5600{
7d4df48e 5601 u16 usec = 0;
41feb5ed 5602 u8 le = 0;
a9533e7e
HP
5603
5604 switch (rate_500) {
5605 case WLC_RATE_1M:
5606 usec = length << 3;
5607 break;
5608 case WLC_RATE_2M:
5609 usec = length << 2;
5610 break;
5611 case WLC_RATE_5M5:
5612 usec = (length << 4) / 11;
5613 if ((length << 4) - (usec * 11) > 0)
5614 usec++;
5615 break;
5616 case WLC_RATE_11M:
5617 usec = (length << 3) / 11;
5618 if ((length << 3) - (usec * 11) > 0) {
5619 usec++;
5620 if ((usec * 11) - (length << 3) >= 8)
5621 le = D11B_PLCP_SIGNAL_LE;
5622 }
5623 break;
5624
5625 default:
5626 WL_ERROR(("wlc_cck_plcp_set: unsupported rate %d\n", rate_500));
5627 rate_500 = WLC_RATE_1M;
5628 usec = length << 3;
5629 break;
5630 }
5631 /* PLCP signal byte */
5632 plcp[0] = rate_500 * 5; /* r (500kbps) * 5 == r (100kbps) */
5633 /* PLCP service byte */
41feb5ed 5634 plcp[1] = (u8) (le | D11B_PLCP_SIGNAL_LOCKED);
7d4df48e 5635 /* PLCP length u16, little endian */
a9533e7e
HP
5636 plcp[2] = usec & 0xff;
5637 plcp[3] = (usec >> 8) & 0xff;
5638 /* PLCP CRC16 */
5639 plcp[4] = 0;
5640 plcp[5] = 0;
5641}
5642
5643/* Rate: 802.11 rate code, length: PSDU length in octets */
41feb5ed 5644static void wlc_compute_cck_plcp(ratespec_t rspec, uint length, u8 *plcp)
a9533e7e
HP
5645{
5646 int rate = RSPEC2RATE(rspec);
5647
5648 ASSERT(IS_CCK(rspec));
5649
5650 wlc_cck_plcp_set(rate, length, plcp);
5651}
5652
5653/* wlc_compute_frame_dur()
5654 *
5655 * Calculate the 802.11 MAC header DUR field for MPDU
5656 * DUR for a single frame = 1 SIFS + 1 ACK
5657 * DUR for a frame with following frags = 3 SIFS + 2 ACK + next frag time
5658 *
5659 * rate MPDU rate in unit of 500kbps
5660 * next_frag_len next MPDU length in bytes
5661 * preamble_type use short/GF or long/MM PLCP header
5662 */
7d4df48e 5663static u16 BCMFASTPATH
41feb5ed 5664wlc_compute_frame_dur(wlc_info_t *wlc, ratespec_t rate, u8 preamble_type,
a9533e7e
HP
5665 uint next_frag_len)
5666{
7d4df48e 5667 u16 dur, sifs;
a9533e7e
HP
5668
5669 sifs = SIFS(wlc->band);
5670
5671 dur = sifs;
7d4df48e 5672 dur += (u16) wlc_calc_ack_time(wlc, rate, preamble_type);
a9533e7e
HP
5673
5674 if (next_frag_len) {
5675 /* Double the current DUR to get 2 SIFS + 2 ACKs */
5676 dur *= 2;
5677 /* add another SIFS and the frag time */
5678 dur += sifs;
5679 dur +=
7d4df48e 5680 (u16) wlc_calc_frame_time(wlc, rate, preamble_type,
a9533e7e
HP
5681 next_frag_len);
5682 }
90ea2296 5683 return dur;
a9533e7e
HP
5684}
5685
5686/* wlc_compute_rtscts_dur()
5687 *
5688 * Calculate the 802.11 MAC header DUR field for an RTS or CTS frame
5689 * DUR for normal RTS/CTS w/ frame = 3 SIFS + 1 CTS + next frame time + 1 ACK
5690 * DUR for CTS-TO-SELF w/ frame = 2 SIFS + next frame time + 1 ACK
5691 *
5692 * cts cts-to-self or rts/cts
5693 * rts_rate rts or cts rate in unit of 500kbps
5694 * rate next MPDU rate in unit of 500kbps
5695 * frame_len next MPDU frame length in bytes
5696 */
7d4df48e 5697u16 BCMFASTPATH
7cc4a4c0 5698wlc_compute_rtscts_dur(wlc_info_t *wlc, bool cts_only, ratespec_t rts_rate,
41feb5ed
GKH
5699 ratespec_t frame_rate, u8 rts_preamble_type,
5700 u8 frame_preamble_type, uint frame_len, bool ba)
a9533e7e 5701{
7d4df48e 5702 u16 dur, sifs;
a9533e7e
HP
5703
5704 sifs = SIFS(wlc->band);
5705
5706 if (!cts_only) { /* RTS/CTS */
5707 dur = 3 * sifs;
5708 dur +=
7d4df48e 5709 (u16) wlc_calc_cts_time(wlc, rts_rate,
a9533e7e
HP
5710 rts_preamble_type);
5711 } else { /* CTS-TO-SELF */
5712 dur = 2 * sifs;
5713 }
5714
5715 dur +=
7d4df48e 5716 (u16) wlc_calc_frame_time(wlc, frame_rate, frame_preamble_type,
a9533e7e
HP
5717 frame_len);
5718 if (ba)
5719 dur +=
7d4df48e 5720 (u16) wlc_calc_ba_time(wlc, frame_rate,
a9533e7e
HP
5721 WLC_SHORT_PREAMBLE);
5722 else
5723 dur +=
7d4df48e 5724 (u16) wlc_calc_ack_time(wlc, frame_rate,
a9533e7e 5725 frame_preamble_type);
90ea2296 5726 return dur;
a9533e7e
HP
5727}
5728
7d4df48e 5729static bool wlc_phy_rspec_check(wlc_info_t *wlc, u16 bw, ratespec_t rspec)
a9533e7e
HP
5730{
5731 if (IS_MCS(rspec)) {
5732 uint mcs = rspec & RSPEC_RATE_MASK;
5733
5734 if (mcs < 8) {
5735 ASSERT(RSPEC_STF(rspec) < PHY_TXC1_MODE_SDM);
5736 } else if ((mcs >= 8) && (mcs <= 23)) {
5737 ASSERT(RSPEC_STF(rspec) == PHY_TXC1_MODE_SDM);
5738 } else if (mcs == 32) {
5739 ASSERT(RSPEC_STF(rspec) < PHY_TXC1_MODE_SDM);
5740 ASSERT(bw == PHY_TXC1_BW_40MHZ_DUP);
5741 }
5742 } else if (IS_OFDM(rspec)) {
5743 ASSERT(RSPEC_STF(rspec) < PHY_TXC1_MODE_STBC);
5744 } else {
5745 ASSERT(IS_CCK(rspec));
5746
5747 ASSERT((bw == PHY_TXC1_BW_20MHZ)
5748 || (bw == PHY_TXC1_BW_20MHZ_UP));
5749 ASSERT(RSPEC_STF(rspec) == PHY_TXC1_MODE_SISO);
5750 }
5751
0f0881b0 5752 return true;
a9533e7e
HP
5753}
5754
7d4df48e 5755u16 BCMFASTPATH wlc_phytxctl1_calc(wlc_info_t *wlc, ratespec_t rspec)
a9533e7e 5756{
7d4df48e
GKH
5757 u16 phyctl1 = 0;
5758 u16 bw;
a9533e7e
HP
5759
5760 if (WLCISLCNPHY(wlc->band)) {
5761 bw = PHY_TXC1_BW_20MHZ;
5762 } else {
5763 bw = RSPEC_GET_BW(rspec);
5764 /* 10Mhz is not supported yet */
5765 if (bw < PHY_TXC1_BW_20MHZ) {
5766 WL_ERROR(("wlc_phytxctl1_calc: bw %d is not supported yet, set to 20L\n", bw));
5767 bw = PHY_TXC1_BW_20MHZ;
5768 }
5769
5770 wlc_phy_rspec_check(wlc, bw, rspec);
5771 }
5772
5773 if (IS_MCS(rspec)) {
5774 uint mcs = rspec & RSPEC_RATE_MASK;
5775
5776 /* bw, stf, coding-type is part of RSPEC_PHYTXBYTE2 returns */
5777 phyctl1 = RSPEC_PHYTXBYTE2(rspec);
5778 /* set the upper byte of phyctl1 */
5779 phyctl1 |= (mcs_table[mcs].tx_phy_ctl3 << 8);
5780 } else if (IS_CCK(rspec) && !WLCISLCNPHY(wlc->band)
5781 && !WLCISSSLPNPHY(wlc->band)) {
5782 /* In CCK mode LPPHY overloads OFDM Modulation bits with CCK Data Rate */
5783 /* Eventually MIMOPHY would also be converted to this format */
5784 /* 0 = 1Mbps; 1 = 2Mbps; 2 = 5.5Mbps; 3 = 11Mbps */
5785 phyctl1 = (bw | (RSPEC_STF(rspec) << PHY_TXC1_MODE_SHIFT));
5786 } else { /* legacy OFDM/CCK */
e59fe083 5787 s16 phycfg;
a9533e7e 5788 /* get the phyctl byte from rate phycfg table */
ca8c1e59
JC
5789 phycfg = wlc_rate_legacy_phyctl(RSPEC2RATE(rspec));
5790 if (phycfg == -1) {
a9533e7e
HP
5791 WL_ERROR(("wlc_phytxctl1_calc: wrong legacy OFDM/CCK rate\n"));
5792 ASSERT(0);
5793 phycfg = 0;
5794 }
5795 /* set the upper byte of phyctl1 */
5796 phyctl1 =
5797 (bw | (phycfg << 8) |
5798 (RSPEC_STF(rspec) << PHY_TXC1_MODE_SHIFT));
5799 }
5800
5801#ifdef BCMDBG
5802 /* phy clock must support 40Mhz if tx descriptor uses it */
5803 if ((phyctl1 & PHY_TXC1_BW_MASK) >= PHY_TXC1_BW_40MHZ) {
5804 ASSERT(CHSPEC_WLC_BW(wlc->chanspec) == WLC_40_MHZ);
5805#ifndef WLC_HIGH_ONLY
5806 ASSERT(wlc->chanspec == wlc_phy_chanspec_get(wlc->band->pi));
5807#endif
5808 }
5809#endif /* BCMDBG */
5810 return phyctl1;
5811}
5812
5813ratespec_t BCMFASTPATH
7cc4a4c0 5814wlc_rspec_to_rts_rspec(wlc_info_t *wlc, ratespec_t rspec, bool use_rspec,
7d4df48e 5815 u16 mimo_ctlchbw)
a9533e7e
HP
5816{
5817 ratespec_t rts_rspec = 0;
5818
5819 if (use_rspec) {
5820 /* use frame rate as rts rate */
5821 rts_rspec = rspec;
5822
5823 } else if (wlc->band->gmode && wlc->protection->_g && !IS_CCK(rspec)) {
5824 /* Use 11Mbps as the g protection RTS target rate and fallback.
5825 * Use the WLC_BASIC_RATE() lookup to find the best basic rate under the
5826 * target in case 11 Mbps is not Basic.
5827 * 6 and 9 Mbps are not usually selected by rate selection, but even
5828 * if the OFDM rate we are protecting is 6 or 9 Mbps, 11 is more robust.
5829 */
5830 rts_rspec = WLC_BASIC_RATE(wlc, WLC_RATE_11M);
5831 } else {
5832 /* calculate RTS rate and fallback rate based on the frame rate
5833 * RTS must be sent at a basic rate since it is a
5834 * control frame, sec 9.6 of 802.11 spec
5835 */
5836 rts_rspec = WLC_BASIC_RATE(wlc, rspec);
5837 }
5838
5839 if (WLC_PHY_11N_CAP(wlc->band)) {
5840 /* set rts txbw to correct side band */
5841 rts_rspec &= ~RSPEC_BW_MASK;
5842
5843 /* if rspec/rspec_fallback is 40MHz, then send RTS on both 20MHz channel
5844 * (DUP), otherwise send RTS on control channel
5845 */
5846 if (RSPEC_IS40MHZ(rspec) && !IS_CCK(rts_rspec))
5847 rts_rspec |= (PHY_TXC1_BW_40MHZ_DUP << RSPEC_BW_SHIFT);
5848 else
5849 rts_rspec |= (mimo_ctlchbw << RSPEC_BW_SHIFT);
5850
5851 /* pick siso/cdd as default for ofdm */
5852 if (IS_OFDM(rts_rspec)) {
5853 rts_rspec &= ~RSPEC_STF_MASK;
5854 rts_rspec |= (wlc->stf->ss_opmode << RSPEC_STF_SHIFT);
5855 }
5856 }
5857 return rts_rspec;
5858}
5859
5860/*
5861 * Add d11txh_t, cck_phy_hdr_t.
5862 *
5863 * 'p' data must start with 802.11 MAC header
5864 * 'p' must allow enough bytes of local headers to be "pushed" onto the packet
5865 *
5866 * headroom == D11_PHY_HDR_LEN + D11_TXH_LEN (D11_TXH_LEN is now 104 bytes)
5867 *
5868 */
7d4df48e 5869static u16 BCMFASTPATH
7cc4a4c0 5870wlc_d11hdrs_mac80211(wlc_info_t *wlc, struct ieee80211_hw *hw,
a9533e7e
HP
5871 void *p, struct scb *scb, uint frag,
5872 uint nfrags, uint queue, uint next_frag_len,
7cc4a4c0 5873 wsec_key_t *key, ratespec_t rspec_override)
a9533e7e
HP
5874{
5875 struct dot11_header *h;
5876 d11txh_t *txh;
41feb5ed 5877 u8 *plcp, plcp_fallback[D11_PHY_HDR_LEN];
a9533e7e
HP
5878 osl_t *osh;
5879 int len, phylen, rts_phylen;
7d4df48e
GKH
5880 u16 fc, type, frameid, mch, phyctl, xfts, mainrates;
5881 u16 seq = 0, mcl = 0, status = 0;
a9533e7e
HP
5882 ratespec_t rspec[2] = { WLC_RATE_1M, WLC_RATE_1M }, rts_rspec[2] = {
5883 WLC_RATE_1M, WLC_RATE_1M};
0965ae88
GKH
5884 bool use_rts = false;
5885 bool use_cts = false;
5886 bool use_rifs = false;
5887 bool short_preamble[2] = { false, false };
41feb5ed
GKH
5888 u8 preamble_type[2] = { WLC_LONG_PREAMBLE, WLC_LONG_PREAMBLE };
5889 u8 rts_preamble_type[2] = { WLC_LONG_PREAMBLE, WLC_LONG_PREAMBLE };
5890 u8 *rts_plcp, rts_plcp_fallback[D11_PHY_HDR_LEN];
a9533e7e
HP
5891 struct dot11_rts_frame *rts = NULL;
5892 bool qos;
5893 uint ac;
66cbd3ab 5894 u32 rate_val[2];
0965ae88 5895 bool hwtkmic = false;
7d4df48e 5896 u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
a9533e7e
HP
5897#ifdef WLANTSEL
5898#define ANTCFG_NONE 0xFF
41feb5ed
GKH
5899 u8 antcfg = ANTCFG_NONE;
5900 u8 fbantcfg = ANTCFG_NONE;
a9533e7e
HP
5901#endif
5902 uint phyctl1_stf = 0;
7d4df48e 5903 u16 durid = 0;
a9533e7e
HP
5904 struct ieee80211_tx_rate *txrate[2];
5905 int k;
5906 struct ieee80211_tx_info *tx_info;
5907 bool is_mcs[2];
7d4df48e 5908 u16 mimo_txbw;
41feb5ed 5909 u8 mimo_preamble_type;
a9533e7e
HP
5910
5911 frameid = 0;
5912
5913 ASSERT(queue < NFIFO);
5914
5915 osh = wlc->osh;
5916
5917 /* locate 802.11 MAC header */
5918 h = (struct dot11_header *)PKTDATA(p);
5919 fc = ltoh16(h->fc);
5920 type = FC_TYPE(fc);
5921
5922 qos = (type == FC_TYPE_DATA && FC_SUBTYPE_ANY_QOS(FC_SUBTYPE(fc)));
5923
5924 /* compute length of frame in bytes for use in PLCP computations */
5925 len = pkttotlen(osh, p);
5926 phylen = len + DOT11_FCS_LEN;
5927
5928 /* If WEP enabled, add room in phylen for the additional bytes of
5929 * ICV which MAC generates. We do NOT add the additional bytes to
5930 * the packet itself, thus phylen = packet length + ICV_LEN + FCS_LEN
5931 * in this case
5932 */
5933 if (key) {
5934 phylen += key->icv_len;
5935 }
5936
5937 /* Get tx_info */
5938 tx_info = IEEE80211_SKB_CB(p);
5939 ASSERT(tx_info);
5940
5941 /* add PLCP */
5942 plcp = PKTPUSH(p, D11_PHY_HDR_LEN);
5943
5944 /* add Broadcom tx descriptor header */
5945 txh = (d11txh_t *) PKTPUSH(p, D11_TXH_LEN);
5946 bzero((char *)txh, D11_TXH_LEN);
5947
5948 /* setup frameid */
5949 if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
5950 /* non-AP STA should never use BCMC queue */
5951 ASSERT(queue != TX_BCMC_FIFO);
5952 if (queue == TX_BCMC_FIFO) {
5953 WL_ERROR(("wl%d: %s: ASSERT queue == TX_BCMC!\n",
5954 WLCWLUNIT(wlc), __func__));
5955 frameid = bcmc_fid_generate(wlc, NULL, txh);
5956 } else {
5957 /* Increment the counter for first fragment */
5958 if (tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) {
5959 SCB_SEQNUM(scb, PKTPRIO(p))++;
5960 }
5961
5962 /* extract fragment number from frame first */
5963 seq = ltoh16(seq) & FRAGNUM_MASK;
5964 seq |= (SCB_SEQNUM(scb, PKTPRIO(p)) << SEQNUM_SHIFT);
5965 h->seq = htol16(seq);
5966
5967 frameid = ((seq << TXFID_SEQ_SHIFT) & TXFID_SEQ_MASK) |
5968 (queue & TXFID_QUEUE_MASK);
5969 }
5970 }
5971 frameid |= queue & TXFID_QUEUE_MASK;
5972
5973 /* set the ignpmq bit for all pkts tx'd in PS mode and for beacons */
5974 if (SCB_PS(scb) || ((fc & FC_KIND_MASK) == FC_BEACON))
5975 mcl |= TXC_IGNOREPMQ;
5976
5977 ASSERT(hw->max_rates <= IEEE80211_TX_MAX_RATES);
5978 ASSERT(hw->max_rates == 2);
5979
5980 txrate[0] = tx_info->control.rates;
5981 txrate[1] = txrate[0] + 1;
5982
5983 ASSERT(txrate[0]->idx >= 0);
5984 /* if rate control algorithm didn't give us a fallback rate, use the primary rate */
5985 if (txrate[1]->idx < 0) {
5986 txrate[1] = txrate[0];
5987 }
5988#ifdef WLC_HIGH_ONLY
5989 /* Double protection , just in case */
5990 if (txrate[0]->idx > HIGHEST_SINGLE_STREAM_MCS)
5991 txrate[0]->idx = HIGHEST_SINGLE_STREAM_MCS;
5992 if (txrate[1]->idx > HIGHEST_SINGLE_STREAM_MCS)
5993 txrate[1]->idx = HIGHEST_SINGLE_STREAM_MCS;
5994#endif
5995
5996 for (k = 0; k < hw->max_rates; k++) {
5997 is_mcs[k] =
0965ae88 5998 txrate[k]->flags & IEEE80211_TX_RC_MCS ? true : false;
a9533e7e
HP
5999 if (!is_mcs[k]) {
6000 ASSERT(!(tx_info->flags & IEEE80211_TX_CTL_AMPDU));
6001 if ((txrate[k]->idx >= 0)
6002 && (txrate[k]->idx <
6003 hw->wiphy->bands[tx_info->band]->n_bitrates)) {
6004 rate_val[k] =
6005 hw->wiphy->bands[tx_info->band]->
6006 bitrates[txrate[k]->idx].hw_value;
6007 short_preamble[k] =
6008 txrate[k]->
6009 flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE ?
0965ae88 6010 true : false;
a9533e7e
HP
6011 } else {
6012 ASSERT((txrate[k]->idx >= 0) &&
6013 (txrate[k]->idx <
6014 hw->wiphy->bands[tx_info->band]->
6015 n_bitrates));
6016 rate_val[k] = WLC_RATE_1M;
6017 }
6018 } else {
6019 rate_val[k] = txrate[k]->idx;
6020 }
6021 /* Currently only support same setting for primay and fallback rates.
6022 * Unify flags for each rate into a single value for the frame
6023 */
6024 use_rts |=
6025 txrate[k]->
0965ae88 6026 flags & IEEE80211_TX_RC_USE_RTS_CTS ? true : false;
a9533e7e
HP
6027 use_cts |=
6028 txrate[k]->
0965ae88 6029 flags & IEEE80211_TX_RC_USE_CTS_PROTECT ? true : false;
a9533e7e
HP
6030
6031 if (is_mcs[k])
6032 rate_val[k] |= NRATE_MCS_INUSE;
6033
6034 rspec[k] = mac80211_wlc_set_nrate(wlc, wlc->band, rate_val[k]);
6035
6036 /* (1) RATE: determine and validate primary rate and fallback rates */
6037 if (!RSPEC_ACTIVE(rspec[k])) {
6038 ASSERT(RSPEC_ACTIVE(rspec[k]));
6039 rspec[k] = WLC_RATE_1M;
6040 } else {
6041 if (WLANTSEL_ENAB(wlc) && !ETHER_ISMULTI(&h->a1)) {
6042 /* set tx antenna config */
0965ae88 6043 wlc_antsel_antcfg_get(wlc->asi, false, false, 0,
a9533e7e
HP
6044 0, &antcfg, &fbantcfg);
6045 }
6046 }
6047 }
6048
6049 phyctl1_stf = wlc->stf->ss_opmode;
6050
6051 if (N_ENAB(wlc->pub)) {
6052 for (k = 0; k < hw->max_rates; k++) {
6053 /* apply siso/cdd to single stream mcs's or ofdm if rspec is auto selected */
6054 if (((IS_MCS(rspec[k]) &&
6055 IS_SINGLE_STREAM(rspec[k] & RSPEC_RATE_MASK)) ||
6056 IS_OFDM(rspec[k]))
6057 && ((rspec[k] & RSPEC_OVERRIDE_MCS_ONLY)
6058 || !(rspec[k] & RSPEC_OVERRIDE))) {
6059 rspec[k] &= ~(RSPEC_STF_MASK | RSPEC_STC_MASK);
6060
6061 /* For SISO MCS use STBC if possible */
6062 if (IS_MCS(rspec[k])
6063 && WLC_STF_SS_STBC_TX(wlc, scb)) {
41feb5ed 6064 u8 stc;
a9533e7e
HP
6065
6066 ASSERT(WLC_STBC_CAP_PHY(wlc));
6067 stc = 1; /* Nss for single stream is always 1 */
6068 rspec[k] |=
6069 (PHY_TXC1_MODE_STBC <<
6070 RSPEC_STF_SHIFT) | (stc <<
6071 RSPEC_STC_SHIFT);
6072 } else
6073 rspec[k] |=
6074 (phyctl1_stf << RSPEC_STF_SHIFT);
6075 }
6076
6077 /* Is the phy configured to use 40MHZ frames? If so then pick the desired txbw */
6078 if (CHSPEC_WLC_BW(wlc->chanspec) == WLC_40_MHZ) {
6079 /* default txbw is 20in40 SB */
6080 mimo_ctlchbw = mimo_txbw =
6081 CHSPEC_SB_UPPER(WLC_BAND_PI_RADIO_CHANSPEC)
6082 ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
6083
6084 if (IS_MCS(rspec[k])) {
6085 /* mcs 32 must be 40b/w DUP */
6086 if ((rspec[k] & RSPEC_RATE_MASK) == 32) {
6087 mimo_txbw =
6088 PHY_TXC1_BW_40MHZ_DUP;
6089 /* use override */
6090 } else if (wlc->mimo_40txbw != AUTO)
6091 mimo_txbw = wlc->mimo_40txbw;
6092 /* else check if dst is using 40 Mhz */
6093 else if (scb->flags & SCB_IS40)
6094 mimo_txbw = PHY_TXC1_BW_40MHZ;
6095 } else if (IS_OFDM(rspec[k])) {
6096 if (wlc->ofdm_40txbw != AUTO)
6097 mimo_txbw = wlc->ofdm_40txbw;
6098 } else {
6099 ASSERT(IS_CCK(rspec[k]));
6100 if (wlc->cck_40txbw != AUTO)
6101 mimo_txbw = wlc->cck_40txbw;
6102 }
6103 } else {
6104 /* mcs32 is 40 b/w only.
6105 * This is possible for probe packets on a STA during SCAN
6106 */
6107 if ((rspec[k] & RSPEC_RATE_MASK) == 32) {
6108 /* mcs 0 */
6109 rspec[k] = RSPEC_MIMORATE;
6110 }
6111 mimo_txbw = PHY_TXC1_BW_20MHZ;
6112 }
6113
6114 /* Set channel width */
6115 rspec[k] &= ~RSPEC_BW_MASK;
6116 if ((k == 0) || ((k > 0) && IS_MCS(rspec[k])))
6117 rspec[k] |= (mimo_txbw << RSPEC_BW_SHIFT);
6118 else
6119 rspec[k] |= (mimo_ctlchbw << RSPEC_BW_SHIFT);
6120
6121 /* Set Short GI */
6122#ifdef NOSGIYET
6123 if (IS_MCS(rspec[k])
6124 && (txrate[k]->flags & IEEE80211_TX_RC_SHORT_GI))
6125 rspec[k] |= RSPEC_SHORT_GI;
6126 else if (!(txrate[k]->flags & IEEE80211_TX_RC_SHORT_GI))
6127 rspec[k] &= ~RSPEC_SHORT_GI;
6128#else
6129 rspec[k] &= ~RSPEC_SHORT_GI;
6130#endif
6131
6132 mimo_preamble_type = WLC_MM_PREAMBLE;
6133 if (txrate[k]->flags & IEEE80211_TX_RC_GREEN_FIELD)
6134 mimo_preamble_type = WLC_GF_PREAMBLE;
6135
6136 if ((txrate[k]->flags & IEEE80211_TX_RC_MCS)
6137 && (!IS_MCS(rspec[k]))) {
6138 WL_ERROR(("wl%d: %s: IEEE80211_TX_RC_MCS != IS_MCS(rspec)\n", WLCWLUNIT(wlc), __func__));
6139 ASSERT(0 && "Rate mismatch");
6140 }
6141
6142 if (IS_MCS(rspec[k])) {
6143 preamble_type[k] = mimo_preamble_type;
6144
6145 /* if SGI is selected, then forced mm for single stream */
6146 if ((rspec[k] & RSPEC_SHORT_GI)
6147 && IS_SINGLE_STREAM(rspec[k] &
6148 RSPEC_RATE_MASK)) {
6149 preamble_type[k] = WLC_MM_PREAMBLE;
6150 }
6151 }
6152
6153 /* mimo bw field MUST now be valid in the rspec (it affects duration calculations) */
6154 ASSERT(VALID_RATE_DBG(wlc, rspec[0]));
6155
6156 /* should be better conditionalized */
6157 if (!IS_MCS(rspec[0])
6158 && (tx_info->control.rates[0].
6159 flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE))
6160 preamble_type[k] = WLC_SHORT_PREAMBLE;
6161
6162 ASSERT(!IS_MCS(rspec[0])
6163 || WLC_IS_MIMO_PREAMBLE(preamble_type[k]));
6164 }
6165 } else {
6166 for (k = 0; k < hw->max_rates; k++) {
6167 /* Set ctrlchbw as 20Mhz */
6168 ASSERT(!IS_MCS(rspec[k]));
6169 rspec[k] &= ~RSPEC_BW_MASK;
6170 rspec[k] |= (PHY_TXC1_BW_20MHZ << RSPEC_BW_SHIFT);
6171
6172 /* for nphy, stf of ofdm frames must follow policies */
6173 if (WLCISNPHY(wlc->band) && IS_OFDM(rspec[k])) {
6174 rspec[k] &= ~RSPEC_STF_MASK;
6175 rspec[k] |= phyctl1_stf << RSPEC_STF_SHIFT;
6176 }
6177 }
6178 }
6179
6180 /* Reset these for use with AMPDU's */
6181 txrate[0]->count = 0;
6182 txrate[1]->count = 0;
6183
6184 /* (3) PLCP: determine PLCP header and MAC duration, fill d11txh_t */
6185 wlc_compute_plcp(wlc, rspec[0], phylen, plcp);
6186 wlc_compute_plcp(wlc, rspec[1], phylen, plcp_fallback);
6187 bcopy(plcp_fallback, (char *)&txh->FragPLCPFallback,
6188 sizeof(txh->FragPLCPFallback));
6189
6190 /* Length field now put in CCK FBR CRC field */
6191 if (IS_CCK(rspec[1])) {
6192 txh->FragPLCPFallback[4] = phylen & 0xff;
6193 txh->FragPLCPFallback[5] = (phylen & 0xff00) >> 8;
6194 }
6195
6196 /* MIMO-RATE: need validation ?? */
6197 mainrates =
6198 IS_OFDM(rspec[0]) ? D11A_PHY_HDR_GRATE((ofdm_phy_hdr_t *) plcp) :
6199 plcp[0];
6200
6201 /* DUR field for main rate */
6202 if ((fc != FC_PS_POLL) && !ETHER_ISMULTI(&h->a1) && !use_rifs) {
6203 durid =
6204 wlc_compute_frame_dur(wlc, rspec[0], preamble_type[0],
6205 next_frag_len);
6206 h->durid = htol16(durid);
6207 } else if (use_rifs) {
6208 /* NAV protect to end of next max packet size */
6209 durid =
7d4df48e 6210 (u16) wlc_calc_frame_time(wlc, rspec[0],
a9533e7e
HP
6211 preamble_type[0],
6212 DOT11_MAX_FRAG_LEN);
6213 durid += RIFS_11N_TIME;
6214 h->durid = htol16(durid);
6215 }
6216
6217 /* DUR field for fallback rate */
6218 if (fc == FC_PS_POLL)
6219 txh->FragDurFallback = h->durid;
6220 else if (ETHER_ISMULTI(&h->a1) || use_rifs)
6221 txh->FragDurFallback = 0;
6222 else {
6223 durid = wlc_compute_frame_dur(wlc, rspec[1],
6224 preamble_type[1], next_frag_len);
6225 txh->FragDurFallback = htol16(durid);
6226 }
6227
6228 /* (4) MAC-HDR: MacTxControlLow */
6229 if (frag == 0)
6230 mcl |= TXC_STARTMSDU;
6231
6232 if (!ETHER_ISMULTI(&h->a1))
6233 mcl |= TXC_IMMEDACK;
6234
6235 if (BAND_5G(wlc->band->bandtype))
6236 mcl |= TXC_FREQBAND_5G;
6237
6238 if (CHSPEC_IS40(WLC_BAND_PI_RADIO_CHANSPEC))
6239 mcl |= TXC_BW_40;
6240
6241 /* set AMIC bit if using hardware TKIP MIC */
6242 if (hwtkmic)
6243 mcl |= TXC_AMIC;
6244
6245 txh->MacTxControlLow = htol16(mcl);
6246
6247 /* MacTxControlHigh */
6248 mch = 0;
6249
6250 /* Set fallback rate preamble type */
6251 if ((preamble_type[1] == WLC_SHORT_PREAMBLE) ||
6252 (preamble_type[1] == WLC_GF_PREAMBLE)) {
6253 ASSERT((preamble_type[1] == WLC_GF_PREAMBLE) ||
6254 (!IS_MCS(rspec[1])));
6255 if (RSPEC2RATE(rspec[1]) != WLC_RATE_1M)
6256 mch |= TXC_PREAMBLE_DATA_FB_SHORT;
6257 }
6258
6259 /* MacFrameControl */
7d4df48e 6260 bcopy((char *)&h->fc, (char *)&txh->MacFrameControl, sizeof(u16));
a9533e7e
HP
6261
6262 txh->TxFesTimeNormal = htol16(0);
6263
6264 txh->TxFesTimeFallback = htol16(0);
6265
6266 /* TxFrameRA */
6267 bcopy((char *)&h->a1, (char *)&txh->TxFrameRA, ETHER_ADDR_LEN);
6268
6269 /* TxFrameID */
6270 txh->TxFrameID = htol16(frameid);
6271
6272 /* TxStatus, Note the case of recreating the first frag of a suppressed frame
6273 * then we may need to reset the retry cnt's via the status reg
6274 */
6275 txh->TxStatus = htol16(status);
6276
6277 if (D11REV_GE(wlc->pub->corerev, 16)) {
6278 /* extra fields for ucode AMPDU aggregation, the new fields are added to
6279 * the END of previous structure so that it's compatible in driver.
6280 * In old rev ucode, these fields should be ignored
6281 */
6282 txh->MaxNMpdus = htol16(0);
6283 txh->MaxABytes_MRT = htol16(0);
6284 txh->MaxABytes_FBR = htol16(0);
6285 txh->MinMBytes = htol16(0);
6286 }
6287
6288 /* (5) RTS/CTS: determine RTS/CTS PLCP header and MAC duration, furnish d11txh_t */
6289 /* RTS PLCP header and RTS frame */
6290 if (use_rts || use_cts) {
6291 if (use_rts && use_cts)
0965ae88 6292 use_cts = false;
a9533e7e
HP
6293
6294 for (k = 0; k < 2; k++) {
6295 rts_rspec[k] = wlc_rspec_to_rts_rspec(wlc, rspec[k],
0965ae88 6296 false,
a9533e7e
HP
6297 mimo_ctlchbw);
6298 }
6299
6300 if (!IS_OFDM(rts_rspec[0]) &&
6301 !((RSPEC2RATE(rts_rspec[0]) == WLC_RATE_1M) ||
6302 (wlc->PLCPHdr_override == WLC_PLCP_LONG))) {
6303 rts_preamble_type[0] = WLC_SHORT_PREAMBLE;
6304 mch |= TXC_PREAMBLE_RTS_MAIN_SHORT;
6305 }
6306
6307 if (!IS_OFDM(rts_rspec[1]) &&
6308 !((RSPEC2RATE(rts_rspec[1]) == WLC_RATE_1M) ||
6309 (wlc->PLCPHdr_override == WLC_PLCP_LONG))) {
6310 rts_preamble_type[1] = WLC_SHORT_PREAMBLE;
6311 mch |= TXC_PREAMBLE_RTS_FB_SHORT;
6312 }
6313
6314 /* RTS/CTS additions to MacTxControlLow */
6315 if (use_cts) {
6316 txh->MacTxControlLow |= htol16(TXC_SENDCTS);
6317 } else {
6318 txh->MacTxControlLow |= htol16(TXC_SENDRTS);
6319 txh->MacTxControlLow |= htol16(TXC_LONGFRAME);
6320 }
6321
6322 /* RTS PLCP header */
f024c48a 6323 ASSERT(IS_ALIGNED((unsigned long)txh->RTSPhyHeader, sizeof(u16)));
a9533e7e
HP
6324 rts_plcp = txh->RTSPhyHeader;
6325 if (use_cts)
6326 rts_phylen = DOT11_CTS_LEN + DOT11_FCS_LEN;
6327 else
6328 rts_phylen = DOT11_RTS_LEN + DOT11_FCS_LEN;
6329
6330 wlc_compute_plcp(wlc, rts_rspec[0], rts_phylen, rts_plcp);
6331
6332 /* fallback rate version of RTS PLCP header */
6333 wlc_compute_plcp(wlc, rts_rspec[1], rts_phylen,
6334 rts_plcp_fallback);
6335 bcopy(rts_plcp_fallback, (char *)&txh->RTSPLCPFallback,
6336 sizeof(txh->RTSPLCPFallback));
6337
6338 /* RTS frame fields... */
6339 rts = (struct dot11_rts_frame *)&txh->rts_frame;
6340
6341 durid = wlc_compute_rtscts_dur(wlc, use_cts, rts_rspec[0],
6342 rspec[0], rts_preamble_type[0],
0965ae88 6343 preamble_type[0], phylen, false);
a9533e7e
HP
6344 rts->durid = htol16(durid);
6345 /* fallback rate version of RTS DUR field */
6346 durid = wlc_compute_rtscts_dur(wlc, use_cts,
6347 rts_rspec[1], rspec[1],
6348 rts_preamble_type[1],
0965ae88 6349 preamble_type[1], phylen, false);
a9533e7e
HP
6350 txh->RTSDurFallback = htol16(durid);
6351
6352 if (use_cts) {
6353 rts->fc = htol16(FC_CTS);
6354 bcopy((char *)&h->a2, (char *)&rts->ra, ETHER_ADDR_LEN);
6355 } else {
7d4df48e 6356 rts->fc = htol16((u16) FC_RTS);
a9533e7e
HP
6357 bcopy((char *)&h->a1, (char *)&rts->ra,
6358 2 * ETHER_ADDR_LEN);
6359 }
6360
6361 /* mainrate
6362 * low 8 bits: main frag rate/mcs,
6363 * high 8 bits: rts/cts rate/mcs
6364 */
6365 mainrates |= (IS_OFDM(rts_rspec[0]) ?
6366 D11A_PHY_HDR_GRATE((ofdm_phy_hdr_t *) rts_plcp) :
6367 rts_plcp[0]) << 8;
6368 } else {
6369 bzero((char *)txh->RTSPhyHeader, D11_PHY_HDR_LEN);
6370 bzero((char *)&txh->rts_frame, sizeof(struct dot11_rts_frame));
6371 bzero((char *)txh->RTSPLCPFallback,
6372 sizeof(txh->RTSPLCPFallback));
6373 txh->RTSDurFallback = 0;
6374 }
6375
6376#ifdef SUPPORT_40MHZ
6377 /* add null delimiter count */
6378 if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && IS_MCS(rspec)) {
6379 txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] =
6380 wlc_ampdu_null_delim_cnt(wlc->ampdu, scb, rspec, phylen);
6381 }
6382#endif
6383
6384 /* Now that RTS/RTS FB preamble types are updated, write the final value */
6385 txh->MacTxControlHigh = htol16(mch);
6386
6387 /* MainRates (both the rts and frag plcp rates have been calculated now) */
6388 txh->MainRates = htol16(mainrates);
6389
6390 /* XtraFrameTypes */
6391 xfts = FRAMETYPE(rspec[1], wlc->mimoft);
6392 xfts |= (FRAMETYPE(rts_rspec[0], wlc->mimoft) << XFTS_RTS_FT_SHIFT);
6393 xfts |= (FRAMETYPE(rts_rspec[1], wlc->mimoft) << XFTS_FBRRTS_FT_SHIFT);
6394 xfts |=
6395 CHSPEC_CHANNEL(WLC_BAND_PI_RADIO_CHANSPEC) << XFTS_CHANNEL_SHIFT;
6396 txh->XtraFrameTypes = htol16(xfts);
6397
6398 /* PhyTxControlWord */
6399 phyctl = FRAMETYPE(rspec[0], wlc->mimoft);
6400 if ((preamble_type[0] == WLC_SHORT_PREAMBLE) ||
6401 (preamble_type[0] == WLC_GF_PREAMBLE)) {
6402 ASSERT((preamble_type[0] == WLC_GF_PREAMBLE)
6403 || !IS_MCS(rspec[0]));
6404 if (RSPEC2RATE(rspec[0]) != WLC_RATE_1M)
6405 phyctl |= PHY_TXC_SHORT_HDR;
6406 WLCNTINCR(wlc->pub->_cnt->txprshort);
6407 }
6408
6409 /* phytxant is properly bit shifted */
6410 phyctl |= wlc_stf_d11hdrs_phyctl_txant(wlc, rspec[0]);
6411 txh->PhyTxControlWord = htol16(phyctl);
6412
6413 /* PhyTxControlWord_1 */
6414 if (WLC_PHY_11N_CAP(wlc->band)) {
7d4df48e 6415 u16 phyctl1 = 0;
a9533e7e
HP
6416
6417 phyctl1 = wlc_phytxctl1_calc(wlc, rspec[0]);
6418 txh->PhyTxControlWord_1 = htol16(phyctl1);
6419 phyctl1 = wlc_phytxctl1_calc(wlc, rspec[1]);
6420 txh->PhyTxControlWord_1_Fbr = htol16(phyctl1);
6421
6422 if (use_rts || use_cts) {
6423 phyctl1 = wlc_phytxctl1_calc(wlc, rts_rspec[0]);
6424 txh->PhyTxControlWord_1_Rts = htol16(phyctl1);
6425 phyctl1 = wlc_phytxctl1_calc(wlc, rts_rspec[1]);
6426 txh->PhyTxControlWord_1_FbrRts = htol16(phyctl1);
6427 }
6428
6429 /*
6430 * For mcs frames, if mixedmode(overloaded with long preamble) is going to be set,
6431 * fill in non-zero MModeLen and/or MModeFbrLen
6432 * it will be unnecessary if they are separated
6433 */
6434 if (IS_MCS(rspec[0]) && (preamble_type[0] == WLC_MM_PREAMBLE)) {
7d4df48e 6435 u16 mmodelen =
a9533e7e
HP
6436 wlc_calc_lsig_len(wlc, rspec[0], phylen);
6437 txh->MModeLen = htol16(mmodelen);
6438 }
6439
6440 if (IS_MCS(rspec[1]) && (preamble_type[1] == WLC_MM_PREAMBLE)) {
7d4df48e 6441 u16 mmodefbrlen =
a9533e7e
HP
6442 wlc_calc_lsig_len(wlc, rspec[1], phylen);
6443 txh->MModeFbrLen = htol16(mmodefbrlen);
6444 }
6445 }
6446
6447 if (IS_MCS(rspec[0]))
6448 ASSERT(IS_MCS(rspec[1]));
6449
6450 ASSERT(!IS_MCS(rspec[0]) ||
6451 ((preamble_type[0] == WLC_MM_PREAMBLE) == (txh->MModeLen != 0)));
6452 ASSERT(!IS_MCS(rspec[1]) ||
6453 ((preamble_type[1] == WLC_MM_PREAMBLE) ==
6454 (txh->MModeFbrLen != 0)));
6455
ca8c1e59
JC
6456 ac = wme_fifo2ac[queue];
6457 if (SCB_WME(scb) && qos && wlc->edcf_txop[ac]) {
a9533e7e
HP
6458 uint frag_dur, dur, dur_fallback;
6459
6460 ASSERT(!ETHER_ISMULTI(&h->a1));
6461
6462 /* WME: Update TXOP threshold */
6463 if ((!(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) && (frag == 0)) {
6464 frag_dur =
6465 wlc_calc_frame_time(wlc, rspec[0], preamble_type[0],
6466 phylen);
6467
6468 if (rts) {
6469 /* 1 RTS or CTS-to-self frame */
6470 dur =
6471 wlc_calc_cts_time(wlc, rts_rspec[0],
6472 rts_preamble_type[0]);
6473 dur_fallback =
6474 wlc_calc_cts_time(wlc, rts_rspec[1],
6475 rts_preamble_type[1]);
6476 /* (SIFS + CTS) + SIFS + frame + SIFS + ACK */
6477 dur += ltoh16(rts->durid);
6478 dur_fallback += ltoh16(txh->RTSDurFallback);
6479 } else if (use_rifs) {
6480 dur = frag_dur;
6481 dur_fallback = 0;
6482 } else {
6483 /* frame + SIFS + ACK */
6484 dur = frag_dur;
6485 dur +=
6486 wlc_compute_frame_dur(wlc, rspec[0],
6487 preamble_type[0], 0);
6488
6489 dur_fallback =
6490 wlc_calc_frame_time(wlc, rspec[1],
6491 preamble_type[1],
6492 phylen);
6493 dur_fallback +=
6494 wlc_compute_frame_dur(wlc, rspec[1],
6495 preamble_type[1], 0);
6496 }
6497 /* NEED to set TxFesTimeNormal (hard) */
7d4df48e 6498 txh->TxFesTimeNormal = htol16((u16) dur);
a9533e7e 6499 /* NEED to set fallback rate version of TxFesTimeNormal (hard) */
7d4df48e 6500 txh->TxFesTimeFallback = htol16((u16) dur_fallback);
a9533e7e
HP
6501
6502 /* update txop byte threshold (txop minus intraframe overhead) */
6503 if (wlc->edcf_txop[ac] >= (dur - frag_dur)) {
6504 {
6505 uint newfragthresh;
6506
6507 newfragthresh =
6508 wlc_calc_frame_len(wlc, rspec[0],
6509 preamble_type[0],
6510 (wlc->
6511 edcf_txop[ac] -
6512 (dur -
6513 frag_dur)));
6514 /* range bound the fragthreshold */
6515 if (newfragthresh < DOT11_MIN_FRAG_LEN)
6516 newfragthresh =
6517 DOT11_MIN_FRAG_LEN;
6518 else if (newfragthresh >
6519 wlc->usr_fragthresh)
6520 newfragthresh =
6521 wlc->usr_fragthresh;
6522 /* update the fragthresh and do txc update */
6523 if (wlc->fragthresh[queue] !=
7d4df48e 6524 (u16) newfragthresh) {
a9533e7e 6525 wlc->fragthresh[queue] =
7d4df48e 6526 (u16) newfragthresh;
a9533e7e
HP
6527 }
6528 }
6529 } else
6530 WL_ERROR(("wl%d: %s txop invalid for rate %d\n",
6531 wlc->pub->unit, fifo_names[queue],
6532 RSPEC2RATE(rspec[0])));
6533
6534 if (dur > wlc->edcf_txop[ac])
6535 WL_ERROR(("wl%d: %s: %s txop exceeded phylen %d/%d dur %d/%d\n", wlc->pub->unit, __func__, fifo_names[queue], phylen, wlc->fragthresh[queue], dur, wlc->edcf_txop[ac]));
6536 }
6537 }
6538
6539 return 0;
6540}
6541
7cc4a4c0 6542void wlc_tbtt(wlc_info_t *wlc, d11regs_t *regs)
a9533e7e
HP
6543{
6544 wlc_bsscfg_t *cfg = wlc->cfg;
6545
6546 WLCNTINCR(wlc->pub->_cnt->tbtt);
6547
6548 if (BSSCFG_STA(cfg)) {
6549 /* run watchdog here if the watchdog timer is not armed */
6550 if (WLC_WATCHDOG_TBTT(wlc)) {
66cbd3ab 6551 u32 cur, delta;
a9533e7e
HP
6552 if (wlc->WDarmed) {
6553 wl_del_timer(wlc->wl, wlc->wdtimer);
0965ae88 6554 wlc->WDarmed = false;
a9533e7e
HP
6555 }
6556
6557 cur = OSL_SYSUPTIME();
6558 delta = cur > wlc->WDlast ? cur - wlc->WDlast :
66cbd3ab 6559 (u32) ~0 - wlc->WDlast + cur + 1;
a9533e7e
HP
6560 if (delta >= TIMER_INTERVAL_WATCHDOG) {
6561 wlc_watchdog((void *)wlc);
6562 wlc->WDlast = cur;
6563 }
6564
6565 wl_add_timer(wlc->wl, wlc->wdtimer,
0f0881b0
GKH
6566 wlc_watchdog_backup_bi(wlc), true);
6567 wlc->WDarmed = true;
a9533e7e
HP
6568 }
6569 }
6570
6571 if (!cfg->BSS) {
6572 /* DirFrmQ is now valid...defer setting until end of ATIM window */
6573 wlc->qvalid |= MCMD_DIRFRMQVAL;
6574 }
6575}
6576
6577/* GP timer is a freerunning 32 bit counter, decrements at 1 us rate */
7cc4a4c0 6578void wlc_hwtimer_gptimer_set(wlc_info_t *wlc, uint us)
a9533e7e
HP
6579{
6580 ASSERT(wlc->pub->corerev >= 3); /* no gptimer in earlier revs */
6581 W_REG(wlc->osh, &wlc->regs->gptimer, us);
6582}
6583
7cc4a4c0 6584void wlc_hwtimer_gptimer_abort(wlc_info_t *wlc)
a9533e7e
HP
6585{
6586 ASSERT(wlc->pub->corerev >= 3);
6587 W_REG(wlc->osh, &wlc->regs->gptimer, 0);
6588}
6589
7cc4a4c0 6590static void wlc_hwtimer_gptimer_cb(wlc_info_t *wlc)
a9533e7e
HP
6591{
6592 /* when interrupt is generated, the counter is loaded with last value
6593 * written and continue to decrement. So it has to be cleaned first
6594 */
6595 W_REG(wlc->osh, &wlc->regs->gptimer, 0);
6596}
6597
6598/*
6599 * This fn has all the high level dpc processing from wlc_dpc.
6600 * POLICY: no macinstatus change, no bounding loop.
6601 * All dpc bounding should be handled in BMAC dpc, like txstatus and rxint
6602 */
66cbd3ab 6603void wlc_high_dpc(wlc_info_t *wlc, u32 macintstatus)
a9533e7e
HP
6604{
6605 d11regs_t *regs = wlc->regs;
6606#ifdef BCMDBG
6607 char flagstr[128];
6608 static const bcm_bit_desc_t int_flags[] = {
6609 {MI_MACSSPNDD, "MACSSPNDD"},
6610 {MI_BCNTPL, "BCNTPL"},
6611 {MI_TBTT, "TBTT"},
6612 {MI_BCNSUCCESS, "BCNSUCCESS"},
6613 {MI_BCNCANCLD, "BCNCANCLD"},
6614 {MI_ATIMWINEND, "ATIMWINEND"},
6615 {MI_PMQ, "PMQ"},
6616 {MI_NSPECGEN_0, "NSPECGEN_0"},
6617 {MI_NSPECGEN_1, "NSPECGEN_1"},
6618 {MI_MACTXERR, "MACTXERR"},
6619 {MI_NSPECGEN_3, "NSPECGEN_3"},
6620 {MI_PHYTXERR, "PHYTXERR"},
6621 {MI_PME, "PME"},
6622 {MI_GP0, "GP0"},
6623 {MI_GP1, "GP1"},
6624 {MI_DMAINT, "DMAINT"},
6625 {MI_TXSTOP, "TXSTOP"},
6626 {MI_CCA, "CCA"},
6627 {MI_BG_NOISE, "BG_NOISE"},
6628 {MI_DTIM_TBTT, "DTIM_TBTT"},
6629 {MI_PRQ, "PRQ"},
6630 {MI_PWRUP, "PWRUP"},
6631 {MI_RFDISABLE, "RFDISABLE"},
6632 {MI_TFS, "TFS"},
6633 {MI_PHYCHANGED, "PHYCHANGED"},
6634 {MI_TO, "TO"},
6635 {0, NULL}
6636 };
6637
6638 if (macintstatus & ~(MI_TBTT | MI_TXSTOP)) {
6639 bcm_format_flags(int_flags, macintstatus, flagstr,
6640 sizeof(flagstr));
6641 WL_TRACE(("wl%d: macintstatus 0x%x %s\n", wlc->pub->unit,
6642 macintstatus, flagstr));
6643 }
6644#endif /* BCMDBG */
6645
6646 if (macintstatus & MI_PRQ) {
6647 /* Process probe request FIFO */
6648 ASSERT(0 && "PRQ Interrupt in non-MBSS");
6649 }
6650
6651 /* TBTT indication */
6652 /* ucode only gives either TBTT or DTIM_TBTT, not both */
6653 if (macintstatus & (MI_TBTT | MI_DTIM_TBTT))
6654 wlc_tbtt(wlc, regs);
6655
6656 if (macintstatus & MI_GP0) {
6657 WL_ERROR(("wl%d: PSM microcode watchdog fired at %d (seconds). Resetting.\n", wlc->pub->unit, wlc->pub->now));
6658
6659 printk_once("%s : PSM Watchdog, chipid 0x%x, chiprev 0x%x\n",
6660 __func__, CHIPID(wlc->pub->sih->chip),
6661 CHIPREV(wlc->pub->sih->chiprev));
6662
6663 WLCNTINCR(wlc->pub->_cnt->psmwds);
6664
6665 /* big hammer */
6666 wl_init(wlc->wl);
6667 }
6668
6669 /* gptimer timeout */
6670 if (macintstatus & MI_TO) {
6671 wlc_hwtimer_gptimer_cb(wlc);
6672 }
6673
6674 if (macintstatus & MI_RFDISABLE) {
6675 WL_ERROR(("wl%d: MAC Detected a change on the RF Disable Input 0x%x\n", wlc->pub->unit, R_REG(wlc->osh, &regs->phydebug) & PDBG_RFD));
6676 /* delay the cleanup to wl_down in IBSS case */
6677 if ((R_REG(wlc->osh, &regs->phydebug) & PDBG_RFD)) {
6678 int idx;
6679 wlc_bsscfg_t *bsscfg;
6680 FOREACH_BSS(wlc, idx, bsscfg) {
6681 if (!BSSCFG_STA(bsscfg) || !bsscfg->enable
6682 || !bsscfg->BSS)
6683 continue;
6684 WL_ERROR(("wl%d: wlc_dpc: rfdisable -> wlc_bsscfg_disable()\n", wlc->pub->unit));
6685 }
6686 }
6687 }
6688
6689 /* send any enq'd tx packets. Just makes sure to jump start tx */
6690 if (!pktq_empty(&wlc->active_queue->q))
6691 wlc_send_q(wlc, wlc->active_queue);
6692
6693#ifndef WLC_HIGH_ONLY
6694 ASSERT(wlc_ps_check(wlc));
6695#endif
6696}
6697
7cc4a4c0 6698static void *wlc_15420war(wlc_info_t *wlc, uint queue)
a9533e7e
HP
6699{
6700 hnddma_t *di;
6701 void *p;
6702
6703 ASSERT(queue < NFIFO);
6704
6705 if ((D11REV_IS(wlc->pub->corerev, 4))
6706 || (D11REV_GT(wlc->pub->corerev, 6)))
90ea2296 6707 return NULL;
a9533e7e
HP
6708
6709 di = wlc->hw->di[queue];
6710 ASSERT(di != NULL);
6711
6712 /* get next packet, ignoring XmtStatus.Curr */
6713 p = dma_getnexttxp(di, HNDDMA_RANGE_ALL);
6714
6715 /* sw block tx dma */
6716 dma_txblock(di);
6717
6718 /* if tx ring is now empty, reset and re-init the tx dma channel */
6719 if (dma_txactive(wlc->hw->di[queue]) == 0) {
6720 WLCNTINCR(wlc->pub->_cnt->txdmawar);
6721 if (!dma_txreset(di))
6722 WL_ERROR(("wl%d: %s: dma_txreset[%d]: cannot stop dma\n", wlc->pub->unit, __func__, queue));
6723 dma_txinit(di);
6724 }
90ea2296 6725 return p;
a9533e7e
HP
6726}
6727
7cc4a4c0 6728static void wlc_war16165(wlc_info_t *wlc, bool tx)
a9533e7e
HP
6729{
6730 if (tx) {
6731 /* the post-increment is used in STAY_AWAKE macro */
6732 if (wlc->txpend16165war++ == 0)
6733 wlc_set_ps_ctrl(wlc);
6734 } else {
6735 wlc->txpend16165war--;
6736 if (wlc->txpend16165war == 0)
6737 wlc_set_ps_ctrl(wlc);
6738 }
6739}
6740
6741/* process an individual tx_status_t */
6742/* WLC_HIGH_API */
6743bool BCMFASTPATH
66cbd3ab 6744wlc_dotxstatus(wlc_info_t *wlc, tx_status_t *txs, u32 frm_tx2)
a9533e7e
HP
6745{
6746 void *p;
6747 uint queue;
6748 d11txh_t *txh;
6749 struct scb *scb = NULL;
6750 bool free_pdu;
6751 osl_t *osh;
6752 int tx_rts, tx_frame_count, tx_rts_count;
6753 uint totlen, supr_status;
6754 bool lastframe;
6755 struct dot11_header *h;
7d4df48e
GKH
6756 u16 fc;
6757 u16 mcl;
a9533e7e
HP
6758 struct ieee80211_tx_info *tx_info;
6759 struct ieee80211_tx_rate *txrate;
6760 int i;
6761
6762 (void)(frm_tx2); /* Compiler reference to avoid unused variable warning */
6763
6764 /* discard intermediate indications for ucode with one legitimate case:
6765 * e.g. if "useRTS" is set. ucode did a successful rts/cts exchange, but the subsequent
6766 * tx of DATA failed. so it will start rts/cts from the beginning (resetting the rts
6767 * transmission count)
6768 */
6769 if (!(txs->status & TX_STATUS_AMPDU)
6770 && (txs->status & TX_STATUS_INTERMEDIATE)) {
6771 WLCNTADD(wlc->pub->_cnt->txnoack,
6772 ((txs->
6773 status & TX_STATUS_FRM_RTX_MASK) >>
6774 TX_STATUS_FRM_RTX_SHIFT));
6775 WL_ERROR(("%s: INTERMEDIATE but not AMPDU\n", __func__));
0965ae88 6776 return false;
a9533e7e
HP
6777 }
6778
6779 osh = wlc->osh;
6780 queue = txs->frameid & TXFID_QUEUE_MASK;
6781 ASSERT(queue < NFIFO);
6782 if (queue >= NFIFO) {
6783 p = NULL;
6784 goto fatal;
6785 }
6786
6787 p = GETNEXTTXP(wlc, queue);
6788 if (WLC_WAR16165(wlc))
0965ae88 6789 wlc_war16165(wlc, false);
a9533e7e
HP
6790 if (p == NULL)
6791 p = wlc_15420war(wlc, queue);
6792 ASSERT(p != NULL);
6793 if (p == NULL)
6794 goto fatal;
6795
6796 txh = (d11txh_t *) PKTDATA(p);
6797 mcl = ltoh16(txh->MacTxControlLow);
6798
6799 if (txs->phyerr) {
6800 WL_ERROR(("phyerr 0x%x, rate 0x%x\n", txs->phyerr,
6801 txh->MainRates));
6802 wlc_print_txdesc(txh);
6803 wlc_print_txstatus(txs);
6804 }
6805
6806 ASSERT(txs->frameid == htol16(txh->TxFrameID));
6807 if (txs->frameid != htol16(txh->TxFrameID))
6808 goto fatal;
6809
6810 tx_info = IEEE80211_SKB_CB(p);
41feb5ed 6811 h = (struct dot11_header *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
a9533e7e
HP
6812 fc = ltoh16(h->fc);
6813
6814 scb = (struct scb *)tx_info->control.sta->drv_priv;
6815
6816 if (N_ENAB(wlc->pub)) {
41feb5ed 6817 u8 *plcp = (u8 *) (txh + 1);
a9533e7e
HP
6818 if (PLCP3_ISSGI(plcp[3]))
6819 WLCNTINCR(wlc->pub->_cnt->txmpdu_sgi);
6820 if (PLCP3_ISSTBC(plcp[3]))
6821 WLCNTINCR(wlc->pub->_cnt->txmpdu_stbc);
6822 }
6823
6824 if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
6825 ASSERT((mcl & TXC_AMPDU_MASK) != TXC_AMPDU_NONE);
6826 wlc_ampdu_dotxstatus(wlc->ampdu, scb, p, txs);
0965ae88 6827 return false;
a9533e7e
HP
6828 }
6829
6830 supr_status = txs->status & TX_STATUS_SUPR_MASK;
6831 if (supr_status == TX_STATUS_SUPR_BADCH)
6832 WL_NONE(("%s: Pkt tx suppressed, possibly channel %d\n",
6833 __func__, CHSPEC_CHANNEL(wlc->default_bss->chanspec)));
6834
6835 tx_rts = htol16(txh->MacTxControlLow) & TXC_SENDRTS;
6836 tx_frame_count =
6837 (txs->status & TX_STATUS_FRM_RTX_MASK) >> TX_STATUS_FRM_RTX_SHIFT;
6838 tx_rts_count =
6839 (txs->status & TX_STATUS_RTS_RTX_MASK) >> TX_STATUS_RTS_RTX_SHIFT;
6840
6841 lastframe = (fc & FC_MOREFRAG) == 0;
6842
6843 if (!lastframe) {
6844 WL_ERROR(("Not last frame!\n"));
6845 } else {
7d4df48e 6846 u16 sfbl, lfbl;
a9533e7e
HP
6847 ieee80211_tx_info_clear_status(tx_info);
6848 if (queue < AC_COUNT) {
6849 sfbl = WLC_WME_RETRY_SFB_GET(wlc, wme_fifo2ac[queue]);
6850 lfbl = WLC_WME_RETRY_LFB_GET(wlc, wme_fifo2ac[queue]);
6851 } else {
6852 sfbl = wlc->SFBL;
6853 lfbl = wlc->LFBL;
6854 }
6855
6856 txrate = tx_info->status.rates;
6857 /* FIXME: this should use a combination of sfbl, lfbl depending on frame length and RTS setting */
6858 if ((tx_frame_count > sfbl) && (txrate[1].idx >= 0)) {
6859 /* rate selection requested a fallback rate and we used it */
6860 txrate->count = lfbl;
6861 txrate[1].count = tx_frame_count - lfbl;
6862 } else {
6863 /* rate selection did not request fallback rate, or we didn't need it */
6864 txrate->count = tx_frame_count;
6865 /* rc80211_minstrel.c:minstrel_tx_status() expects unused rates to be marked with idx = -1 */
6866 txrate[1].idx = -1;
6867 txrate[1].count = 0;
6868 }
6869
6870 /* clear the rest of the rates */
6871 for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) {
6872 txrate[i].idx = -1;
6873 txrate[i].count = 0;
6874 }
6875
6876 if (txs->status & TX_STATUS_ACK_RCV)
6877 tx_info->flags |= IEEE80211_TX_STAT_ACK;
6878 }
6879
6880 totlen = pkttotlen(osh, p);
0f0881b0 6881 free_pdu = true;
a9533e7e
HP
6882
6883 wlc_txfifo_complete(wlc, queue, 1);
6884
6885 if (lastframe) {
6886 PKTSETNEXT(p, NULL);
6887 PKTSETLINK(p, NULL);
6888 wlc->txretried = 0;
6889 /* remove PLCP & Broadcom tx descriptor header */
6890 PKTPULL(p, D11_PHY_HDR_LEN);
6891 PKTPULL(p, D11_TXH_LEN);
6892 ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, p);
6893 WLCNTINCR(wlc->pub->_cnt->ieee_tx_status);
6894 } else {
6895 WL_ERROR(("%s: Not last frame => not calling tx_status\n",
6896 __func__));
6897 }
6898
0965ae88 6899 return false;
a9533e7e
HP
6900
6901 fatal:
6902 ASSERT(0);
6903 if (p)
0f0881b0 6904 PKTFREE(osh, p, true);
a9533e7e
HP
6905
6906#ifdef WLC_HIGH_ONLY
6907 /* If this is a split driver, do the big-hammer here.
6908 * If this is a monolithic driver, wlc_bmac.c:wlc_dpc() will do the big-hammer.
6909 */
6910 wl_init(wlc->wl);
6911#endif
0f0881b0 6912 return true;
a9533e7e
HP
6913
6914}
6915
6916void BCMFASTPATH
562c8850 6917wlc_txfifo_complete(wlc_info_t *wlc, uint fifo, s8 txpktpend)
a9533e7e
HP
6918{
6919 TXPKTPENDDEC(wlc, fifo, txpktpend);
6920 WL_TRACE(("wlc_txfifo_complete, pktpend dec %d to %d\n", txpktpend,
6921 TXPKTPENDGET(wlc, fifo)));
6922
6923 /* There is more room; mark precedences related to this FIFO sendable */
6924 WLC_TX_FIFO_ENAB(wlc, fifo);
6925 ASSERT(TXPKTPENDGET(wlc, fifo) >= 0);
6926
6927 if (!TXPKTPENDTOT(wlc)) {
6928 if (wlc->block_datafifo & DATA_BLOCK_TX_SUPR)
6929 wlc_bsscfg_tx_check(wlc);
6930 }
6931
6932 /* Clear MHF2_TXBCMC_NOW flag if BCMC fifo has drained */
6933 if (AP_ENAB(wlc->pub) &&
6934 wlc->bcmcfifo_drain && !TXPKTPENDGET(wlc, TX_BCMC_FIFO)) {
0965ae88 6935 wlc->bcmcfifo_drain = false;
a9533e7e
HP
6936 wlc_mhf(wlc, MHF2, MHF2_TXBCMC_NOW, 0, WLC_BAND_AUTO);
6937 }
6938
6939 /* figure out which bsscfg is being worked on... */
6940}
6941
6942/* Given the beacon interval in kus, and a 64 bit TSF in us,
6943 * return the offset (in us) of the TSF from the last TBTT
6944 */
66cbd3ab 6945u32 wlc_calc_tbtt_offset(u32 bp, u32 tsf_h, u32 tsf_l)
a9533e7e 6946{
66cbd3ab 6947 u32 k, btklo, btkhi, offset;
a9533e7e
HP
6948
6949 /* TBTT is always an even multiple of the beacon_interval,
6950 * so the TBTT less than or equal to the beacon timestamp is
6951 * the beacon timestamp minus the beacon timestamp modulo
6952 * the beacon interval.
6953 *
6954 * TBTT = BT - (BT % BIu)
6955 * = (BTk - (BTk % BP)) * 2^10
6956 *
6957 * BT = beacon timestamp (usec, 64bits)
6958 * BTk = beacon timestamp (Kusec, 54bits)
6959 * BP = beacon interval (Kusec, 16bits)
6960 * BIu = BP * 2^10 = beacon interval (usec, 26bits)
6961 *
66cbd3ab 6962 * To keep the calculations in u32s, the modulo operation
a9533e7e
HP
6963 * on the high part of BT needs to be done in parts using the
6964 * relations:
6965 * X*Y mod Z = ((X mod Z) * (Y mod Z)) mod Z
6966 * and
6967 * (X + Y) mod Z = ((X mod Z) + (Y mod Z)) mod Z
6968 *
7d4df48e 6969 * So, if BTk[n] = u16 n [0,3] of BTk.
a9533e7e
HP
6970 * BTk % BP = SUM((BTk[n] * 2^16n) % BP , 0<=n<4) % BP
6971 * and the SUM term can be broken down:
6972 * (BTk[n] * 2^16n) % BP
6973 * (BTk[n] * (2^16n % BP)) % BP
6974 *
6975 * Create a set of power of 2 mod BP constants:
6976 * K[n] = 2^(16n) % BP
6977 * = (K[n-1] * 2^16) % BP
6978 * K[2] = 2^32 % BP = ((2^16 % BP) * 2^16) % BP
6979 *
6980 * BTk % BP = BTk[0-1] % BP +
6981 * (BTk[2] * K[2]) % BP +
6982 * (BTk[3] * K[3]) % BP
6983 *
6984 * Since K[n] < 2^16 and BTk[n] is < 2^16, then BTk[n] * K[n] < 2^32
6985 */
6986
6987 /* BTk = BT >> 10, btklo = BTk[0-3], bkthi = BTk[4-6] */
6988 btklo = (tsf_h << 22) | (tsf_l >> 10);
6989 btkhi = tsf_h >> 10;
6990
6991 /* offset = BTk % BP */
6992 offset = btklo % bp;
6993
6994 /* K[2] = ((2^16 % BP) * 2^16) % BP */
66cbd3ab
GKH
6995 k = (u32) (1 << 16) % bp;
6996 k = (u32) (k * 1 << 16) % (u32) bp;
a9533e7e
HP
6997
6998 /* offset += (BTk[2] * K[2]) % BP */
6999 offset += ((btkhi & 0xffff) * k) % bp;
7000
7001 /* BTk[3] */
7002 btkhi = btkhi >> 16;
7003
7004 /* k[3] = (K[2] * 2^16) % BP */
7005 k = (k << 16) % bp;
7006
7007 /* offset += (BTk[3] * K[3]) % BP */
7008 offset += ((btkhi & 0xffff) * k) % bp;
7009
7010 offset = offset % bp;
7011
7012 /* convert offset from kus to us by shifting up 10 bits and
7013 * add in the low 10 bits of tsf that we ignored
7014 */
7015 offset = (offset << 10) + (tsf_l & 0x3FF);
7016
7017 return offset;
7018}
7019
7020/* Update beacon listen interval in shared memory */
7cc4a4c0 7021void wlc_bcn_li_upd(wlc_info_t *wlc)
a9533e7e
HP
7022{
7023 if (AP_ENAB(wlc->pub))
7024 return;
7025
7026 /* wake up every DTIM is the default */
7027 if (wlc->bcn_li_dtim == 1)
7028 wlc_write_shm(wlc, M_BCN_LI, 0);
7029 else
7030 wlc_write_shm(wlc, M_BCN_LI,
7031 (wlc->bcn_li_dtim << 8) | wlc->bcn_li_bcn);
7032}
7033
7034static void
7cc4a4c0 7035prep_mac80211_status(wlc_info_t *wlc, d11rxhdr_t *rxh, void *p,
a9533e7e
HP
7036 struct ieee80211_rx_status *rx_status)
7037{
66cbd3ab 7038 u32 tsf_l, tsf_h;
a9533e7e
HP
7039 wlc_d11rxhdr_t *wlc_rxh = (wlc_d11rxhdr_t *) rxh;
7040 int preamble;
7041 int channel;
7042 ratespec_t rspec;
580a0bd9 7043 unsigned char *plcp;
a9533e7e
HP
7044
7045 wlc_read_tsf(wlc, &tsf_l, &tsf_h); /* mactime */
7046 rx_status->mactime = tsf_h;
7047 rx_status->mactime <<= 32;
7048 rx_status->mactime |= tsf_l;
7049 rx_status->flag |= RX_FLAG_TSFT;
7050
7051 channel = WLC_CHAN_CHANNEL(rxh->RxChan);
7052
7053 /* XXX Channel/badn needs to be filtered against whether we are single/dual band card */
7054 if (channel > 14) {
7055 rx_status->band = IEEE80211_BAND_5GHZ;
7056 rx_status->freq = wf_channel2mhz(channel, WF_CHAN_FACTOR_5_G);
7057 } else {
7058 rx_status->band = IEEE80211_BAND_2GHZ;
7059 rx_status->freq = wf_channel2mhz(channel, WF_CHAN_FACTOR_2_4_G);
7060 }
7061
7062 rx_status->signal = wlc_rxh->rssi; /* signal */
7063
7064 /* noise */
7065 /* qual */
7066 rx_status->antenna = (rxh->PhyRxStatus_0 & PRXS0_RXANT_UPSUBBAND) ? 1 : 0; /* ant */
7067
7068 plcp = PKTDATA(p);
7069
7070 rspec = wlc_compute_rspec(rxh, plcp);
7071 if (IS_MCS(rspec)) {
7072 rx_status->rate_idx = rspec & RSPEC_RATE_MASK;
7073 rx_status->flag |= RX_FLAG_HT;
7074 if (RSPEC_IS40MHZ(rspec))
7075 rx_status->flag |= RX_FLAG_40MHZ;
7076 } else {
7077 switch (RSPEC2RATE(rspec)) {
7078 case WLC_RATE_1M:
7079 rx_status->rate_idx = 0;
7080 break;
7081 case WLC_RATE_2M:
7082 rx_status->rate_idx = 1;
7083 break;
7084 case WLC_RATE_5M5:
7085 rx_status->rate_idx = 2;
7086 break;
7087 case WLC_RATE_11M:
7088 rx_status->rate_idx = 3;
7089 break;
7090 case WLC_RATE_6M:
7091 rx_status->rate_idx = 4;
7092 break;
7093 case WLC_RATE_9M:
7094 rx_status->rate_idx = 5;
7095 break;
7096 case WLC_RATE_12M:
7097 rx_status->rate_idx = 6;
7098 break;
7099 case WLC_RATE_18M:
7100 rx_status->rate_idx = 7;
7101 break;
7102 case WLC_RATE_24M:
7103 rx_status->rate_idx = 8;
7104 break;
7105 case WLC_RATE_36M:
7106 rx_status->rate_idx = 9;
7107 break;
7108 case WLC_RATE_48M:
7109 rx_status->rate_idx = 10;
7110 break;
7111 case WLC_RATE_54M:
7112 rx_status->rate_idx = 11;
7113 break;
7114 default:
7115 WL_ERROR(("%s: Unknown rate\n", __func__));
7116 }
7117
7118 /* Determine short preamble and rate_idx */
7119 preamble = 0;
7120 if (IS_CCK(rspec)) {
7121 if (rxh->PhyRxStatus_0 & PRXS0_SHORTH)
7122 WL_ERROR(("Short CCK\n"));
7123 rx_status->flag |= RX_FLAG_SHORTPRE;
7124 } else if (IS_OFDM(rspec)) {
7125 rx_status->flag |= RX_FLAG_SHORTPRE;
7126 } else {
7127 WL_ERROR(("%s: Unknown modulation\n", __func__));
7128 }
7129 }
7130
7131 if (PLCP3_ISSGI(plcp[3]))
7132 rx_status->flag |= RX_FLAG_SHORT_GI;
7133
7134 if (rxh->RxStatus1 & RXS_DECERR) {
7135 rx_status->flag |= RX_FLAG_FAILED_PLCP_CRC;
7136 WL_ERROR(("%s: RX_FLAG_FAILED_PLCP_CRC\n", __func__));
7137 }
7138 if (rxh->RxStatus1 & RXS_FCSERR) {
7139 rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
7140 WL_ERROR(("%s: RX_FLAG_FAILED_FCS_CRC\n", __func__));
7141 }
7142}
7143
a9533e7e 7144static void
7cc4a4c0 7145wlc_recvctl(wlc_info_t *wlc, osl_t *osh, d11rxhdr_t *rxh, void *p)
a9533e7e
HP
7146{
7147 int len_mpdu;
7148 struct ieee80211_rx_status rx_status;
7149#if defined(BCMDBG)
7150 struct sk_buff *skb = p;
7151#endif /* BCMDBG */
7152 /* Todo:
7153 * Cache plcp for first MPDU of AMPD and use chacched version for INTERMEDIATE.
7154 * Test for INTERMEDIATE like so:
7155 * if (!(plcp[0] | plcp[1] | plcp[2]))
7156 */
7157
7158 memset(&rx_status, 0, sizeof(rx_status));
7159 prep_mac80211_status(wlc, rxh, p, &rx_status);
7160
7161 /* mac header+body length, exclude CRC and plcp header */
7162 len_mpdu = PKTLEN(p) - D11_PHY_HDR_LEN - DOT11_FCS_LEN;
7163 PKTPULL(p, D11_PHY_HDR_LEN);
7164 PKTSETLEN(p, len_mpdu);
7165
7166 ASSERT(!PKTNEXT(p));
7167 ASSERT(!PKTLINK(p));
7168
f024c48a 7169 ASSERT(IS_ALIGNED((unsigned long)skb->data, 2));
a9533e7e
HP
7170
7171 memcpy(IEEE80211_SKB_RXCB(p), &rx_status, sizeof(rx_status));
7172 ieee80211_rx_irqsafe(wlc->pub->ieee_hw, p);
7173
7174 WLCNTINCR(wlc->pub->_cnt->ieee_rx);
7175 PKTUNALLOC(osh);
7176 return;
7177}
7178
7cc4a4c0 7179void wlc_bss_list_free(wlc_info_t *wlc, wlc_bss_list_t *bss_list)
a9533e7e
HP
7180{
7181 uint index;
7182 wlc_bss_info_t *bi;
7183
7184 if (!bss_list) {
7185 WL_ERROR(("%s: Attempting to free NULL list\n", __func__));
7186 return;
7187 }
7188 /* inspect all BSS descriptor */
7189 for (index = 0; index < bss_list->count; index++) {
7190 bi = bss_list->ptrs[index];
7191 if (bi) {
7192 if (bi->bcn_prb) {
182acb3c 7193 kfree(bi->bcn_prb);
a9533e7e 7194 }
182acb3c 7195 kfree(bi);
a9533e7e
HP
7196 bss_list->ptrs[index] = NULL;
7197 }
7198 }
7199 bss_list->count = 0;
7200}
7201
7202/* Process received frames */
7203/*
0965ae88 7204 * Return true if more frames need to be processed. false otherwise.
a9533e7e
HP
7205 * Param 'bound' indicates max. # frames to process before break out.
7206 */
7207/* WLC_HIGH_API */
7cc4a4c0 7208void BCMFASTPATH wlc_recv(wlc_info_t *wlc, void *p)
a9533e7e
HP
7209{
7210 d11rxhdr_t *rxh;
7211 struct dot11_header *h;
7212 osl_t *osh;
7d4df48e 7213 u16 fc;
a9533e7e
HP
7214 uint len;
7215 bool is_amsdu;
a9533e7e
HP
7216
7217 WL_TRACE(("wl%d: wlc_recv\n", wlc->pub->unit));
7218
7219 osh = wlc->osh;
7220
7221 /* frame starts with rxhdr */
7222 rxh = (d11rxhdr_t *) PKTDATA(p);
7223
7224 /* strip off rxhdr */
7225 PKTPULL(p, wlc->hwrxoff);
7226
7227 /* fixup rx header endianness */
7228 ltoh16_buf((void *)rxh, sizeof(d11rxhdr_t));
7229
7230 /* MAC inserts 2 pad bytes for a4 headers or QoS or A-MSDU subframes */
7231 if (rxh->RxStatus1 & RXS_PBPRES) {
7232 if (PKTLEN(p) < 2) {
7233 WLCNTINCR(wlc->pub->_cnt->rxrunt);
7234 WL_ERROR(("wl%d: wlc_recv: rcvd runt of len %d\n",
7235 wlc->pub->unit, PKTLEN(p)));
7236 goto toss;
7237 }
7238 PKTPULL(p, 2);
7239 }
7240
7241 h = (struct dot11_header *)(PKTDATA(p) + D11_PHY_HDR_LEN);
7242 len = PKTLEN(p);
7243
7244 if (rxh->RxStatus1 & RXS_FCSERR) {
7245 if (wlc->pub->mac80211_state & MAC80211_PROMISC_BCNS) {
7246 WL_ERROR(("FCSERR while scanning******* - tossing\n"));
7247 goto toss;
7248 } else {
7249 WL_ERROR(("RCSERR!!!\n"));
7250 goto toss;
7251 }
7252 }
7253
7254 /* check received pkt has at least frame control field */
7255 if (len >= D11_PHY_HDR_LEN + sizeof(h->fc)) {
7256 fc = ltoh16(h->fc);
7257 } else {
7258 WLCNTINCR(wlc->pub->_cnt->rxrunt);
7259 goto toss;
7260 }
7261
7262 is_amsdu = rxh->RxStatus2 & RXS_AMSDU_MASK;
7263
7264 /* explicitly test bad src address to avoid sending bad deauth */
7265 if (!is_amsdu) {
7266 /* CTS and ACK CTL frames are w/o a2 */
7267 if (FC_TYPE(fc) == FC_TYPE_DATA || FC_TYPE(fc) == FC_TYPE_MNG) {
7268 if ((ETHER_ISNULLADDR(&h->a2) || ETHER_ISMULTI(&h->a2))) {
ba07d0cb
AS
7269 WL_ERROR(("wl%d: %s: dropping a frame with "
7270 "invalid src mac address, a2: %pM\n",
7271 wlc->pub->unit, __func__, &h->a2));
a9533e7e
HP
7272 WLCNTINCR(wlc->pub->_cnt->rxbadsrcmac);
7273 goto toss;
7274 }
7275 WLCNTINCR(wlc->pub->_cnt->rxfrag);
7276 }
7277 }
7278
7279 /* due to sheer numbers, toss out probe reqs for now */
7280 if (FC_TYPE(fc) == FC_TYPE_MNG) {
7281 if ((fc & FC_KIND_MASK) == FC_PROBE_REQ)
7282 goto toss;
7283 }
7284
7285 if (is_amsdu) {
7286 WL_ERROR(("%s: is_amsdu causing toss\n", __func__));
7287 goto toss;
7288 }
7289
7290 wlc_recvctl(wlc, osh, rxh, p);
7291 return;
7292
7293 toss:
0965ae88 7294 PKTFREE(osh, p, false);
a9533e7e
HP
7295}
7296
7297/* calculate frame duration for Mixed-mode L-SIG spoofing, return
7298 * number of bytes goes in the length field
7299 *
7300 * Formula given by HT PHY Spec v 1.13
7301 * len = 3(nsyms + nstream + 3) - 3
7302 */
7d4df48e 7303u16 BCMFASTPATH
7cc4a4c0 7304wlc_calc_lsig_len(wlc_info_t *wlc, ratespec_t ratespec, uint mac_len)
a9533e7e
HP
7305{
7306 uint nsyms, len = 0, kNdps;
7307
7308 WL_TRACE(("wl%d: wlc_calc_lsig_len: rate %d, len%d\n", wlc->pub->unit,
7309 RSPEC2RATE(ratespec), mac_len));
7310
7311 if (IS_MCS(ratespec)) {
7312 uint mcs = ratespec & RSPEC_RATE_MASK;
7313 /* MCS_TXS(mcs) returns num tx streams - 1 */
7314 int tot_streams = (MCS_TXS(mcs) + 1) + RSPEC_STC(ratespec);
7315
7316 ASSERT(WLC_PHY_11N_CAP(wlc->band));
7317 /* the payload duration calculation matches that of regular ofdm */
7318 /* 1000Ndbps = kbps * 4 */
7319 kNdps =
7320 MCS_RATE(mcs, RSPEC_IS40MHZ(ratespec),
7321 RSPEC_ISSGI(ratespec)) * 4;
7322
7323 if (RSPEC_STC(ratespec) == 0)
7324 /* NSyms = CEILING((SERVICE + 8*NBytes + TAIL) / Ndbps) */
7325 nsyms =
7326 CEIL((APHY_SERVICE_NBITS + 8 * mac_len +
7327 APHY_TAIL_NBITS) * 1000, kNdps);
7328 else
7329 /* STBC needs to have even number of symbols */
7330 nsyms =
7331 2 *
7332 CEIL((APHY_SERVICE_NBITS + 8 * mac_len +
7333 APHY_TAIL_NBITS) * 1000, 2 * kNdps);
7334
7335 nsyms += (tot_streams + 3); /* (+3) account for HT-SIG(2) and HT-STF(1) */
7336 /* 3 bytes/symbol @ legacy 6Mbps rate */
7337 len = (3 * nsyms) - 3; /* (-3) excluding service bits and tail bits */
7338 }
7339
7d4df48e 7340 return (u16) len;
a9533e7e
HP
7341}
7342
7343/* calculate frame duration of a given rate and length, return time in usec unit */
7344uint BCMFASTPATH
41feb5ed 7345wlc_calc_frame_time(wlc_info_t *wlc, ratespec_t ratespec, u8 preamble_type,
a9533e7e
HP
7346 uint mac_len)
7347{
7348 uint nsyms, dur = 0, Ndps, kNdps;
7349 uint rate = RSPEC2RATE(ratespec);
7350
7351 if (rate == 0) {
7352 ASSERT(0);
7353 WL_ERROR(("wl%d: WAR: using rate of 1 mbps\n", wlc->pub->unit));
7354 rate = WLC_RATE_1M;
7355 }
7356
7357 WL_TRACE(("wl%d: wlc_calc_frame_time: rspec 0x%x, preamble_type %d, len%d\n", wlc->pub->unit, ratespec, preamble_type, mac_len));
7358
7359 if (IS_MCS(ratespec)) {
7360 uint mcs = ratespec & RSPEC_RATE_MASK;
7361 int tot_streams = MCS_TXS(mcs) + RSPEC_STC(ratespec);
7362 ASSERT(WLC_PHY_11N_CAP(wlc->band));
7363 ASSERT(WLC_IS_MIMO_PREAMBLE(preamble_type));
7364
7365 dur = PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT);
7366 if (preamble_type == WLC_MM_PREAMBLE)
7367 dur += PREN_MM_EXT;
7368 /* 1000Ndbps = kbps * 4 */
7369 kNdps =
7370 MCS_RATE(mcs, RSPEC_IS40MHZ(ratespec),
7371 RSPEC_ISSGI(ratespec)) * 4;
7372
7373 if (RSPEC_STC(ratespec) == 0)
7374 /* NSyms = CEILING((SERVICE + 8*NBytes + TAIL) / Ndbps) */
7375 nsyms =
7376 CEIL((APHY_SERVICE_NBITS + 8 * mac_len +
7377 APHY_TAIL_NBITS) * 1000, kNdps);
7378 else
7379 /* STBC needs to have even number of symbols */
7380 nsyms =
7381 2 *
7382 CEIL((APHY_SERVICE_NBITS + 8 * mac_len +
7383 APHY_TAIL_NBITS) * 1000, 2 * kNdps);
7384
7385 dur += APHY_SYMBOL_TIME * nsyms;
7386 if (BAND_2G(wlc->band->bandtype))
7387 dur += DOT11_OFDM_SIGNAL_EXTENSION;
7388 } else if (IS_OFDM(rate)) {
7389 dur = APHY_PREAMBLE_TIME;
7390 dur += APHY_SIGNAL_TIME;
7391 /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */
7392 Ndps = rate * 2;
7393 /* NSyms = CEILING((SERVICE + 8*NBytes + TAIL) / Ndbps) */
7394 nsyms =
7395 CEIL((APHY_SERVICE_NBITS + 8 * mac_len + APHY_TAIL_NBITS),
7396 Ndps);
7397 dur += APHY_SYMBOL_TIME * nsyms;
7398 if (BAND_2G(wlc->band->bandtype))
7399 dur += DOT11_OFDM_SIGNAL_EXTENSION;
7400 } else {
7401 /* calc # bits * 2 so factor of 2 in rate (1/2 mbps) will divide out */
7402 mac_len = mac_len * 8 * 2;
7403 /* calc ceiling of bits/rate = microseconds of air time */
7404 dur = (mac_len + rate - 1) / rate;
7405 if (preamble_type & WLC_SHORT_PREAMBLE)
7406 dur += BPHY_PLCP_SHORT_TIME;
7407 else
7408 dur += BPHY_PLCP_TIME;
7409 }
7410 return dur;
7411}
7412
7413/* The opposite of wlc_calc_frame_time */
7414static uint
41feb5ed 7415wlc_calc_frame_len(wlc_info_t *wlc, ratespec_t ratespec, u8 preamble_type,
a9533e7e
HP
7416 uint dur)
7417{
7418 uint nsyms, mac_len, Ndps, kNdps;
7419 uint rate = RSPEC2RATE(ratespec);
7420
7421 WL_TRACE(("wl%d: wlc_calc_frame_len: rspec 0x%x, preamble_type %d, dur %d\n", wlc->pub->unit, ratespec, preamble_type, dur));
7422
7423 if (IS_MCS(ratespec)) {
7424 uint mcs = ratespec & RSPEC_RATE_MASK;
7425 int tot_streams = MCS_TXS(mcs) + RSPEC_STC(ratespec);
7426 ASSERT(WLC_PHY_11N_CAP(wlc->band));
7427 dur -= PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT);
7428 /* payload calculation matches that of regular ofdm */
7429 if (BAND_2G(wlc->band->bandtype))
7430 dur -= DOT11_OFDM_SIGNAL_EXTENSION;
7431 /* kNdbps = kbps * 4 */
7432 kNdps =
7433 MCS_RATE(mcs, RSPEC_IS40MHZ(ratespec),
7434 RSPEC_ISSGI(ratespec)) * 4;
7435 nsyms = dur / APHY_SYMBOL_TIME;
7436 mac_len =
7437 ((nsyms * kNdps) -
7438 ((APHY_SERVICE_NBITS + APHY_TAIL_NBITS) * 1000)) / 8000;
7439 } else if (IS_OFDM(ratespec)) {
7440 dur -= APHY_PREAMBLE_TIME;
7441 dur -= APHY_SIGNAL_TIME;
7442 /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */
7443 Ndps = rate * 2;
7444 nsyms = dur / APHY_SYMBOL_TIME;
7445 mac_len =
7446 ((nsyms * Ndps) -
7447 (APHY_SERVICE_NBITS + APHY_TAIL_NBITS)) / 8;
7448 } else {
7449 if (preamble_type & WLC_SHORT_PREAMBLE)
7450 dur -= BPHY_PLCP_SHORT_TIME;
7451 else
7452 dur -= BPHY_PLCP_TIME;
7453 mac_len = dur * rate;
7454 /* divide out factor of 2 in rate (1/2 mbps) */
7455 mac_len = mac_len / 8 / 2;
7456 }
7457 return mac_len;
7458}
7459
7460static uint
41feb5ed 7461wlc_calc_ba_time(wlc_info_t *wlc, ratespec_t rspec, u8 preamble_type)
a9533e7e
HP
7462{
7463 WL_TRACE(("wl%d: wlc_calc_ba_time: rspec 0x%x, preamble_type %d\n",
7464 wlc->pub->unit, rspec, preamble_type));
7465 /* Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that is less than
7466 * or equal to the rate of the immediately previous frame in the FES
7467 */
7468 rspec = WLC_BASIC_RATE(wlc, rspec);
7469 ASSERT(VALID_RATE_DBG(wlc, rspec));
7470
7471 /* BA len == 32 == 16(ctl hdr) + 4(ba len) + 8(bitmap) + 4(fcs) */
7472 return wlc_calc_frame_time(wlc, rspec, preamble_type,
7473 (DOT11_BA_LEN + DOT11_BA_BITMAP_LEN +
7474 DOT11_FCS_LEN));
7475}
7476
7477static uint BCMFASTPATH
41feb5ed 7478wlc_calc_ack_time(wlc_info_t *wlc, ratespec_t rspec, u8 preamble_type)
a9533e7e
HP
7479{
7480 uint dur = 0;
7481
7482 WL_TRACE(("wl%d: wlc_calc_ack_time: rspec 0x%x, preamble_type %d\n",
7483 wlc->pub->unit, rspec, preamble_type));
7484 /* Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that is less than
7485 * or equal to the rate of the immediately previous frame in the FES
7486 */
7487 rspec = WLC_BASIC_RATE(wlc, rspec);
7488 ASSERT(VALID_RATE_DBG(wlc, rspec));
7489
7490 /* ACK frame len == 14 == 2(fc) + 2(dur) + 6(ra) + 4(fcs) */
7491 dur =
7492 wlc_calc_frame_time(wlc, rspec, preamble_type,
7493 (DOT11_ACK_LEN + DOT11_FCS_LEN));
7494 return dur;
7495}
7496
7497static uint
41feb5ed 7498wlc_calc_cts_time(wlc_info_t *wlc, ratespec_t rspec, u8 preamble_type)
a9533e7e
HP
7499{
7500 WL_TRACE(("wl%d: wlc_calc_cts_time: ratespec 0x%x, preamble_type %d\n",
7501 wlc->pub->unit, rspec, preamble_type));
7502 return wlc_calc_ack_time(wlc, rspec, preamble_type);
7503}
7504
7505/* derive wlc->band->basic_rate[] table from 'rateset' */
7cc4a4c0 7506void wlc_rate_lookup_init(wlc_info_t *wlc, wlc_rateset_t *rateset)
a9533e7e 7507{
41feb5ed
GKH
7508 u8 rate;
7509 u8 mandatory;
7510 u8 cck_basic = 0;
7511 u8 ofdm_basic = 0;
7512 u8 *br = wlc->band->basic_rate;
a9533e7e
HP
7513 uint i;
7514
7515 /* incoming rates are in 500kbps units as in 802.11 Supported Rates */
7516 bzero(br, WLC_MAXRATE + 1);
7517
7518 /* For each basic rate in the rates list, make an entry in the
7519 * best basic lookup.
7520 */
7521 for (i = 0; i < rateset->count; i++) {
7522 /* only make an entry for a basic rate */
7523 if (!(rateset->rates[i] & WLC_RATE_FLAG))
7524 continue;
7525
7526 /* mask off basic bit */
7527 rate = (rateset->rates[i] & RATE_MASK);
7528
7529 if (rate > WLC_MAXRATE) {
7530 WL_ERROR(("wlc_rate_lookup_init: invalid rate 0x%X in rate set\n", rateset->rates[i]));
7531 continue;
7532 }
7533
7534 br[rate] = rate;
7535 }
7536
7537 /* The rate lookup table now has non-zero entries for each
7538 * basic rate, equal to the basic rate: br[basicN] = basicN
7539 *
7540 * To look up the best basic rate corresponding to any
7541 * particular rate, code can use the basic_rate table
7542 * like this
7543 *
7544 * basic_rate = wlc->band->basic_rate[tx_rate]
7545 *
7546 * Make sure there is a best basic rate entry for
7547 * every rate by walking up the table from low rates
7548 * to high, filling in holes in the lookup table
7549 */
7550
7551 for (i = 0; i < wlc->band->hw_rateset.count; i++) {
7552 rate = wlc->band->hw_rateset.rates[i];
7553 ASSERT(rate <= WLC_MAXRATE);
7554
7555 if (br[rate] != 0) {
7556 /* This rate is a basic rate.
7557 * Keep track of the best basic rate so far by
7558 * modulation type.
7559 */
7560 if (IS_OFDM(rate))
7561 ofdm_basic = rate;
7562 else
7563 cck_basic = rate;
7564
7565 continue;
7566 }
7567
7568 /* This rate is not a basic rate so figure out the
7569 * best basic rate less than this rate and fill in
7570 * the hole in the table
7571 */
7572
7573 br[rate] = IS_OFDM(rate) ? ofdm_basic : cck_basic;
7574
7575 if (br[rate] != 0)
7576 continue;
7577
7578 if (IS_OFDM(rate)) {
7579 /* In 11g and 11a, the OFDM mandatory rates are 6, 12, and 24 Mbps */
7580 if (rate >= WLC_RATE_24M)
7581 mandatory = WLC_RATE_24M;
7582 else if (rate >= WLC_RATE_12M)
7583 mandatory = WLC_RATE_12M;
7584 else
7585 mandatory = WLC_RATE_6M;
7586 } else {
7587 /* In 11b, all the CCK rates are mandatory 1 - 11 Mbps */
7588 mandatory = rate;
7589 }
7590
7591 br[rate] = mandatory;
7592 }
7593}
7594
41feb5ed 7595static void wlc_write_rate_shm(wlc_info_t *wlc, u8 rate, u8 basic_rate)
a9533e7e 7596{
41feb5ed
GKH
7597 u8 phy_rate, index;
7598 u8 basic_phy_rate, basic_index;
7d4df48e
GKH
7599 u16 dir_table, basic_table;
7600 u16 basic_ptr;
a9533e7e
HP
7601
7602 /* Shared memory address for the table we are reading */
7603 dir_table = IS_OFDM(basic_rate) ? M_RT_DIRMAP_A : M_RT_DIRMAP_B;
7604
7605 /* Shared memory address for the table we are writing */
7606 basic_table = IS_OFDM(rate) ? M_RT_BBRSMAP_A : M_RT_BBRSMAP_B;
7607
7608 /*
7609 * for a given rate, the LS-nibble of the PLCP SIGNAL field is
7610 * the index into the rate table.
7611 */
7612 phy_rate = rate_info[rate] & RATE_MASK;
7613 basic_phy_rate = rate_info[basic_rate] & RATE_MASK;
7614 index = phy_rate & 0xf;
7615 basic_index = basic_phy_rate & 0xf;
7616
7617 /* Find the SHM pointer to the ACK rate entry by looking in the
7618 * Direct-map Table
7619 */
7620 basic_ptr = wlc_read_shm(wlc, (dir_table + basic_index * 2));
7621
7622 /* Update the SHM BSS-basic-rate-set mapping table with the pointer
7623 * to the correct basic rate for the given incoming rate
7624 */
7625 wlc_write_shm(wlc, (basic_table + index * 2), basic_ptr);
7626}
7627
7cc4a4c0 7628static const wlc_rateset_t *wlc_rateset_get_hwrs(wlc_info_t *wlc)
a9533e7e
HP
7629{
7630 const wlc_rateset_t *rs_dflt;
7631
7632 if (WLC_PHY_11N_CAP(wlc->band)) {
7633 if (BAND_5G(wlc->band->bandtype))
7634 rs_dflt = &ofdm_mimo_rates;
7635 else
7636 rs_dflt = &cck_ofdm_mimo_rates;
7637 } else if (wlc->band->gmode)
7638 rs_dflt = &cck_ofdm_rates;
7639 else
7640 rs_dflt = &cck_rates;
7641
7642 return rs_dflt;
7643}
7644
7cc4a4c0 7645void wlc_set_ratetable(wlc_info_t *wlc)
a9533e7e
HP
7646{
7647 const wlc_rateset_t *rs_dflt;
7648 wlc_rateset_t rs;
41feb5ed 7649 u8 rate, basic_rate;
a9533e7e
HP
7650 uint i;
7651
7652 rs_dflt = wlc_rateset_get_hwrs(wlc);
7653 ASSERT(rs_dflt != NULL);
7654
7655 wlc_rateset_copy(rs_dflt, &rs);
7656 wlc_rateset_mcs_upd(&rs, wlc->stf->txstreams);
7657
7658 /* walk the phy rate table and update SHM basic rate lookup table */
7659 for (i = 0; i < rs.count; i++) {
7660 rate = rs.rates[i] & RATE_MASK;
7661
7662 /* for a given rate WLC_BASIC_RATE returns the rate at
7663 * which a response ACK/CTS should be sent.
7664 */
7665 basic_rate = WLC_BASIC_RATE(wlc, rate);
7666 if (basic_rate == 0) {
7667 /* This should only happen if we are using a
7668 * restricted rateset.
7669 */
7670 basic_rate = rs.rates[0] & RATE_MASK;
7671 }
7672
7673 wlc_write_rate_shm(wlc, rate, basic_rate);
7674 }
7675}
7676
7677/*
7678 * Return true if the specified rate is supported by the specified band.
7679 * WLC_BAND_AUTO indicates the current band.
7680 */
7cc4a4c0 7681bool wlc_valid_rate(wlc_info_t *wlc, ratespec_t rspec, int band, bool verbose)
a9533e7e
HP
7682{
7683 wlc_rateset_t *hw_rateset;
7684 uint i;
7685
7686 if ((band == WLC_BAND_AUTO) || (band == wlc->band->bandtype)) {
7687 hw_rateset = &wlc->band->hw_rateset;
7688 } else if (NBANDS(wlc) > 1) {
7689 hw_rateset = &wlc->bandstate[OTHERBANDUNIT(wlc)]->hw_rateset;
7690 } else {
7691 /* other band specified and we are a single band device */
0965ae88 7692 return false;
a9533e7e
HP
7693 }
7694
7695 /* check if this is a mimo rate */
7696 if (IS_MCS(rspec)) {
7697 if (!VALID_MCS((rspec & RSPEC_RATE_MASK)))
7698 goto error;
7699
7700 return isset(hw_rateset->mcs, (rspec & RSPEC_RATE_MASK));
7701 }
7702
7703 for (i = 0; i < hw_rateset->count; i++)
7704 if (hw_rateset->rates[i] == RSPEC2RATE(rspec))
0f0881b0 7705 return true;
a9533e7e
HP
7706 error:
7707 if (verbose) {
7708 WL_ERROR(("wl%d: wlc_valid_rate: rate spec 0x%x not in hw_rateset\n", wlc->pub->unit, rspec));
7709 }
7710
0965ae88 7711 return false;
a9533e7e
HP
7712}
7713
41feb5ed 7714static void wlc_update_mimo_band_bwcap(wlc_info_t *wlc, u8 bwcap)
a9533e7e
HP
7715{
7716 uint i;
7717 wlcband_t *band;
7718
7719 for (i = 0; i < NBANDS(wlc); i++) {
7720 if (IS_SINGLEBAND_5G(wlc->deviceid))
7721 i = BAND_5G_INDEX;
7722 band = wlc->bandstate[i];
7723 if (band->bandtype == WLC_BAND_5G) {
7724 if ((bwcap == WLC_N_BW_40ALL)
7725 || (bwcap == WLC_N_BW_20IN2G_40IN5G))
0f0881b0 7726 band->mimo_cap_40 = true;
a9533e7e 7727 else
0965ae88 7728 band->mimo_cap_40 = false;
a9533e7e
HP
7729 } else {
7730 ASSERT(band->bandtype == WLC_BAND_2G);
7731 if (bwcap == WLC_N_BW_40ALL)
0f0881b0 7732 band->mimo_cap_40 = true;
a9533e7e 7733 else
0965ae88 7734 band->mimo_cap_40 = false;
a9533e7e
HP
7735 }
7736 }
7737
7738 wlc->mimo_band_bwcap = bwcap;
7739}
7740
7cc4a4c0 7741void wlc_mod_prb_rsp_rate_table(wlc_info_t *wlc, uint frame_len)
a9533e7e
HP
7742{
7743 const wlc_rateset_t *rs_dflt;
7744 wlc_rateset_t rs;
41feb5ed 7745 u8 rate;
7d4df48e 7746 u16 entry_ptr;
41feb5ed 7747 u8 plcp[D11_PHY_HDR_LEN];
7d4df48e 7748 u16 dur, sifs;
a9533e7e
HP
7749 uint i;
7750
7751 sifs = SIFS(wlc->band);
7752
7753 rs_dflt = wlc_rateset_get_hwrs(wlc);
7754 ASSERT(rs_dflt != NULL);
7755
7756 wlc_rateset_copy(rs_dflt, &rs);
7757 wlc_rateset_mcs_upd(&rs, wlc->stf->txstreams);
7758
7759 /* walk the phy rate table and update MAC core SHM basic rate table entries */
7760 for (i = 0; i < rs.count; i++) {
7761 rate = rs.rates[i] & RATE_MASK;
7762
7763 entry_ptr = wlc_rate_shm_offset(wlc, rate);
7764
7765 /* Calculate the Probe Response PLCP for the given rate */
7766 wlc_compute_plcp(wlc, rate, frame_len, plcp);
7767
7768 /* Calculate the duration of the Probe Response frame plus SIFS for the MAC */
7769 dur =
7d4df48e 7770 (u16) wlc_calc_frame_time(wlc, rate, WLC_LONG_PREAMBLE,
a9533e7e
HP
7771 frame_len);
7772 dur += sifs;
7773
7774 /* Update the SHM Rate Table entry Probe Response values */
7775 wlc_write_shm(wlc, entry_ptr + M_RT_PRS_PLCP_POS,
7d4df48e 7776 (u16) (plcp[0] + (plcp[1] << 8)));
a9533e7e 7777 wlc_write_shm(wlc, entry_ptr + M_RT_PRS_PLCP_POS + 2,
7d4df48e 7778 (u16) (plcp[2] + (plcp[3] << 8)));
a9533e7e
HP
7779 wlc_write_shm(wlc, entry_ptr + M_RT_PRS_DUR_POS, dur);
7780 }
7781}
7782
7d4df48e 7783u16
7cc4a4c0 7784wlc_compute_bcntsfoff(wlc_info_t *wlc, ratespec_t rspec, bool short_preamble,
a9533e7e
HP
7785 bool phydelay)
7786{
7787 uint bcntsfoff = 0;
7788
7789 if (IS_MCS(rspec)) {
7790 WL_ERROR(("wl%d: recd beacon with mcs rate; rspec 0x%x\n",
7791 wlc->pub->unit, rspec));
7792 } else if (IS_OFDM(rspec)) {
7793 /* tx delay from MAC through phy to air (2.1 usec) +
7794 * phy header time (preamble + PLCP SIGNAL == 20 usec) +
7795 * PLCP SERVICE + MAC header time (SERVICE + FC + DUR + A1 + A2 + A3 + SEQ == 26
7796 * bytes at beacon rate)
7797 */
7798 bcntsfoff += phydelay ? D11A_PHY_TX_DELAY : 0;
7799 bcntsfoff += APHY_PREAMBLE_TIME + APHY_SIGNAL_TIME;
7800 bcntsfoff +=
7801 wlc_compute_airtime(wlc, rspec,
7802 APHY_SERVICE_NBITS / 8 +
7803 DOT11_MAC_HDR_LEN);
7804 } else {
7805 /* tx delay from MAC through phy to air (3.4 usec) +
7806 * phy header time (long preamble + PLCP == 192 usec) +
7807 * MAC header time (FC + DUR + A1 + A2 + A3 + SEQ == 24 bytes at beacon rate)
7808 */
7809 bcntsfoff += phydelay ? D11B_PHY_TX_DELAY : 0;
7810 bcntsfoff +=
7811 short_preamble ? D11B_PHY_SPREHDR_TIME :
7812 D11B_PHY_LPREHDR_TIME;
7813 bcntsfoff += wlc_compute_airtime(wlc, rspec, DOT11_MAC_HDR_LEN);
7814 }
7d4df48e 7815 return (u16) (bcntsfoff);
a9533e7e
HP
7816}
7817
7818/* Max buffering needed for beacon template/prb resp template is 142 bytes.
7819 *
7820 * PLCP header is 6 bytes.
7821 * 802.11 A3 header is 24 bytes.
7822 * Max beacon frame body template length is 112 bytes.
7823 * Max probe resp frame body template length is 110 bytes.
7824 *
7825 * *len on input contains the max length of the packet available.
7826 *
7827 * The *len value is set to the number of bytes in buf used, and starts with the PLCP
7828 * and included up to, but not including, the 4 byte FCS.
7829 */
7830static void
7cc4a4c0 7831wlc_bcn_prb_template(wlc_info_t *wlc, uint type, ratespec_t bcn_rspec,
7d4df48e 7832 wlc_bsscfg_t *cfg, u16 *buf, int *len)
a9533e7e
HP
7833{
7834 cck_phy_hdr_t *plcp;
7835 struct dot11_management_header *h;
7836 int hdr_len, body_len;
7837
7838 ASSERT(*len >= 142);
7839 ASSERT(type == FC_BEACON || type == FC_PROBE_RESP);
7840
7841 if (MBSS_BCN_ENAB(cfg) && type == FC_BEACON)
7842 hdr_len = DOT11_MAC_HDR_LEN;
7843 else
7844 hdr_len = D11_PHY_HDR_LEN + DOT11_MAC_HDR_LEN;
7845 body_len = *len - hdr_len; /* calc buffer size provided for frame body */
7846
7847 *len = hdr_len + body_len; /* return actual size */
7848
7849 /* format PHY and MAC headers */
7850 bzero((char *)buf, hdr_len);
7851
7852 plcp = (cck_phy_hdr_t *) buf;
7853
7854 /* PLCP for Probe Response frames are filled in from core's rate table */
7855 if (type == FC_BEACON && !MBSS_BCN_ENAB(cfg)) {
7856 /* fill in PLCP */
7857 wlc_compute_plcp(wlc, bcn_rspec,
7858 (DOT11_MAC_HDR_LEN + body_len + DOT11_FCS_LEN),
41feb5ed 7859 (u8 *) plcp);
a9533e7e
HP
7860
7861 }
7862 /* "Regular" and 16 MBSS but not for 4 MBSS */
7863 /* Update the phytxctl for the beacon based on the rspec */
7864 if (!SOFTBCN_ENAB(cfg))
7865 wlc_beacon_phytxctl_txant_upd(wlc, bcn_rspec);
7866
7867 if (MBSS_BCN_ENAB(cfg) && type == FC_BEACON)
7868 h = (struct dot11_management_header *)&plcp[0];
7869 else
7870 h = (struct dot11_management_header *)&plcp[1];
7871
7872 /* fill in 802.11 header */
7d4df48e 7873 h->fc = htol16((u16) type);
a9533e7e
HP
7874
7875 /* DUR is 0 for multicast bcn, or filled in by MAC for prb resp */
7876 /* A1 filled in by MAC for prb resp, broadcast for bcn */
7877 if (type == FC_BEACON)
7878 bcopy((const char *)&ether_bcast, (char *)&h->da,
7879 ETHER_ADDR_LEN);
7880 bcopy((char *)&cfg->cur_etheraddr, (char *)&h->sa, ETHER_ADDR_LEN);
7881 bcopy((char *)&cfg->BSSID, (char *)&h->bssid, ETHER_ADDR_LEN);
7882
7883 /* SEQ filled in by MAC */
7884
7885 return;
7886}
7887
7888int wlc_get_header_len()
7889{
7890 return TXOFF;
7891}
7892
7893/* Update a beacon for a particular BSS
7894 * For MBSS, this updates the software template and sets "latest" to the index of the
7895 * template updated.
7896 * Otherwise, it updates the hardware template.
7897 */
7cc4a4c0 7898void wlc_bss_update_beacon(wlc_info_t *wlc, wlc_bsscfg_t *cfg)
a9533e7e
HP
7899{
7900 int len = BCN_TMPL_LEN;
7901
7902 /* Clear the soft intmask */
7903 wlc->defmacintmask &= ~MI_BCNTPL;
7904
7905 if (!cfg->up) { /* Only allow updates on an UP bss */
7906 return;
7907 }
7908
7909 if (MBSS_BCN_ENAB(cfg)) { /* Optimize: Some of if/else could be combined */
7910 } else if (HWBCN_ENAB(cfg)) { /* Hardware beaconing for this config */
7d4df48e 7911 u16 bcn[BCN_TMPL_LEN / 2];
66cbd3ab 7912 u32 both_valid = MCMD_BCN0VLD | MCMD_BCN1VLD;
a9533e7e
HP
7913 d11regs_t *regs = wlc->regs;
7914 osl_t *osh = NULL;
7915
7916 osh = wlc->osh;
7917
7918 /* Check if both templates are in use, if so sched. an interrupt
7919 * that will call back into this routine
7920 */
7921 if ((R_REG(osh, &regs->maccommand) & both_valid) == both_valid) {
7922 /* clear any previous status */
7923 W_REG(osh, &regs->macintstatus, MI_BCNTPL);
7924 }
7925 /* Check that after scheduling the interrupt both of the
7926 * templates are still busy. if not clear the int. & remask
7927 */
7928 if ((R_REG(osh, &regs->maccommand) & both_valid) == both_valid) {
7929 wlc->defmacintmask |= MI_BCNTPL;
7930 return;
7931 }
7932
7933 wlc->bcn_rspec =
7934 wlc_lowest_basic_rspec(wlc, &cfg->current_bss->rateset);
7935 ASSERT(wlc_valid_rate
7936 (wlc, wlc->bcn_rspec,
7937 CHSPEC_IS2G(cfg->current_bss->
7938 chanspec) ? WLC_BAND_2G : WLC_BAND_5G,
0f0881b0 7939 true));
a9533e7e
HP
7940
7941 /* update the template and ucode shm */
7942 wlc_bcn_prb_template(wlc, FC_BEACON, wlc->bcn_rspec, cfg, bcn,
7943 &len);
0965ae88 7944 wlc_write_hw_bcntemplates(wlc, bcn, len, false);
a9533e7e
HP
7945 }
7946}
7947
7948/*
7949 * Update all beacons for the system.
7950 */
7cc4a4c0 7951void wlc_update_beacon(wlc_info_t *wlc)
a9533e7e
HP
7952{
7953 int idx;
7954 wlc_bsscfg_t *bsscfg;
7955
7956 /* update AP or IBSS beacons */
7957 FOREACH_BSS(wlc, idx, bsscfg) {
7958 if (bsscfg->up && (BSSCFG_AP(bsscfg) || !bsscfg->BSS))
7959 wlc_bss_update_beacon(wlc, bsscfg);
7960 }
7961}
7962
7963/* Write ssid into shared memory */
7cc4a4c0 7964void wlc_shm_ssid_upd(wlc_info_t *wlc, wlc_bsscfg_t *cfg)
a9533e7e 7965{
41feb5ed 7966 u8 *ssidptr = cfg->SSID;
7d4df48e 7967 u16 base = M_SSID;
41feb5ed 7968 u8 ssidbuf[DOT11_MAX_SSID_LEN];
a9533e7e
HP
7969
7970 /* padding the ssid with zero and copy it into shm */
7971 bzero(ssidbuf, DOT11_MAX_SSID_LEN);
7972 bcopy(ssidptr, ssidbuf, cfg->SSID_len);
7973
7974 wlc_copyto_shm(wlc, base, ssidbuf, DOT11_MAX_SSID_LEN);
7975
7976 if (!MBSS_BCN_ENAB(cfg))
7d4df48e 7977 wlc_write_shm(wlc, M_SSIDLEN, (u16) cfg->SSID_len);
a9533e7e
HP
7978}
7979
7cc4a4c0 7980void wlc_update_probe_resp(wlc_info_t *wlc, bool suspend)
a9533e7e
HP
7981{
7982 int idx;
7983 wlc_bsscfg_t *bsscfg;
7984
7985 /* update AP or IBSS probe responses */
7986 FOREACH_BSS(wlc, idx, bsscfg) {
7987 if (bsscfg->up && (BSSCFG_AP(bsscfg) || !bsscfg->BSS))
7988 wlc_bss_update_probe_resp(wlc, bsscfg, suspend);
7989 }
7990}
7991
7992void
7cc4a4c0 7993wlc_bss_update_probe_resp(wlc_info_t *wlc, wlc_bsscfg_t *cfg, bool suspend)
a9533e7e 7994{
7d4df48e 7995 u16 prb_resp[BCN_TMPL_LEN / 2];
a9533e7e
HP
7996 int len = BCN_TMPL_LEN;
7997
7998 /* write the probe response to hardware, or save in the config structure */
7999 if (!MBSS_PRB_ENAB(cfg)) {
8000
8001 /* create the probe response template */
8002 wlc_bcn_prb_template(wlc, FC_PROBE_RESP, 0, cfg, prb_resp,
8003 &len);
8004
8005 if (suspend)
8006 wlc_suspend_mac_and_wait(wlc);
8007
8008 /* write the probe response into the template region */
8009 wlc_bmac_write_template_ram(wlc->hw, T_PRS_TPL_BASE,
8010 (len + 3) & ~3, prb_resp);
8011
8012 /* write the length of the probe response frame (+PLCP/-FCS) */
7d4df48e 8013 wlc_write_shm(wlc, M_PRB_RESP_FRM_LEN, (u16) len);
a9533e7e
HP
8014
8015 /* write the SSID and SSID length */
8016 wlc_shm_ssid_upd(wlc, cfg);
8017
8018 /*
8019 * Write PLCP headers and durations for probe response frames at all rates.
8020 * Use the actual frame length covered by the PLCP header for the call to
8021 * wlc_mod_prb_rsp_rate_table() by subtracting the PLCP len and adding the FCS.
8022 */
8023 len += (-D11_PHY_HDR_LEN + DOT11_FCS_LEN);
7d4df48e 8024 wlc_mod_prb_rsp_rate_table(wlc, (u16) len);
a9533e7e
HP
8025
8026 if (suspend)
8027 wlc_enable_mac(wlc);
8028 } else { /* Generating probe resp in sw; update local template */
8029 ASSERT(0 && "No software probe response support without MBSS");
8030 }
8031}
8032
8033/* prepares pdu for transmission. returns BCM error codes */
7cc4a4c0 8034int wlc_prep_pdu(wlc_info_t *wlc, void *pdu, uint *fifop)
a9533e7e
HP
8035{
8036 osl_t *osh;
8037 uint fifo;
8038 d11txh_t *txh;
8039 struct dot11_header *h;
8040 struct scb *scb;
7d4df48e 8041 u16 fc;
a9533e7e
HP
8042
8043 osh = wlc->osh;
8044
8045 ASSERT(pdu);
8046 txh = (d11txh_t *) PKTDATA(pdu);
8047 ASSERT(txh);
41feb5ed 8048 h = (struct dot11_header *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
a9533e7e
HP
8049 ASSERT(h);
8050 fc = ltoh16(h->fc);
8051
8052 /* get the pkt queue info. This was put at wlc_sendctl or wlc_send for PDU */
8053 fifo = ltoh16(txh->TxFrameID) & TXFID_QUEUE_MASK;
8054
8055 scb = NULL;
8056
8057 *fifop = fifo;
8058
8059 /* return if insufficient dma resources */
8060 if (TXAVAIL(wlc, fifo) < MAX_DMA_SEGS) {
8061 /* Mark precedences related to this FIFO, unsendable */
8062 WLC_TX_FIFO_CLEAR(wlc, fifo);
8063 return BCME_BUSY;
8064 }
8065
8066 if (FC_TYPE(ltoh16(txh->MacFrameControl)) != FC_TYPE_DATA)
8067 WLCNTINCR(wlc->pub->_cnt->txctl);
8068
8069 return 0;
8070}
8071
8072/* init tx reported rate mechanism */
7cc4a4c0 8073void wlc_reprate_init(wlc_info_t *wlc)
a9533e7e
HP
8074{
8075 int i;
8076 wlc_bsscfg_t *bsscfg;
8077
8078 FOREACH_BSS(wlc, i, bsscfg) {
8079 wlc_bsscfg_reprate_init(bsscfg);
8080 }
8081}
8082
8083/* per bsscfg init tx reported rate mechanism */
7cc4a4c0 8084void wlc_bsscfg_reprate_init(wlc_bsscfg_t *bsscfg)
a9533e7e
HP
8085{
8086 bsscfg->txrspecidx = 0;
8087 bzero((char *)bsscfg->txrspec, sizeof(bsscfg->txrspec));
8088}
8089
8090/* Retrieve a consolidated set of revision information,
8091 * typically for the WLC_GET_REVINFO ioctl
8092 */
7cc4a4c0 8093int wlc_get_revision_info(wlc_info_t *wlc, void *buf, uint len)
a9533e7e
HP
8094{
8095 wlc_rev_info_t *rinfo = (wlc_rev_info_t *) buf;
8096
8097 if (len < WL_REV_INFO_LEGACY_LENGTH)
8098 return BCME_BUFTOOSHORT;
8099
8100 rinfo->vendorid = wlc->vendorid;
8101 rinfo->deviceid = wlc->deviceid;
8102 rinfo->radiorev = (wlc->band->radiorev << IDCODE_REV_SHIFT) |
8103 (wlc->band->radioid << IDCODE_ID_SHIFT);
8104 rinfo->chiprev = wlc->pub->sih->chiprev;
8105 rinfo->corerev = wlc->pub->corerev;
8106 rinfo->boardid = wlc->pub->sih->boardtype;
8107 rinfo->boardvendor = wlc->pub->sih->boardvendor;
8108 rinfo->boardrev = wlc->pub->boardrev;
8109 rinfo->ucoderev = wlc->ucode_rev;
8110 rinfo->driverrev = EPI_VERSION_NUM;
8111 rinfo->bus = wlc->pub->sih->bustype;
8112 rinfo->chipnum = wlc->pub->sih->chip;
8113
ce0f1b8c 8114 if (len >= (offsetof(wlc_rev_info_t, chippkg))) {
a9533e7e
HP
8115 rinfo->phytype = wlc->band->phytype;
8116 rinfo->phyrev = wlc->band->phyrev;
8117 rinfo->anarev = 0; /* obsolete stuff, suppress */
8118 }
8119
8120 if (len >= sizeof(*rinfo)) {
8121 rinfo->chippkg = wlc->pub->sih->chippkg;
8122 }
8123
8124 return BCME_OK;
8125}
8126
7cc4a4c0 8127void wlc_default_rateset(wlc_info_t *wlc, wlc_rateset_t *rs)
a9533e7e
HP
8128{
8129 wlc_rateset_default(rs, NULL, wlc->band->phytype, wlc->band->bandtype,
0965ae88 8130 false, RATE_MASK_FULL, (bool) N_ENAB(wlc->pub),
a9533e7e
HP
8131 CHSPEC_WLC_BW(wlc->default_bss->chanspec),
8132 wlc->stf->txstreams);
8133}
8134
0d2f0724 8135static void wlc_bss_default_init(wlc_info_t *wlc)
a2627bc0 8136{
a9533e7e
HP
8137 chanspec_t chanspec;
8138 wlcband_t *band;
8139 wlc_bss_info_t *bi = wlc->default_bss;
8140
8141 /* init default and target BSS with some sane initial values */
8142 bzero((char *)(bi), sizeof(wlc_bss_info_t));
8143 bi->beacon_period = ISSIM_ENAB(wlc->pub->sih) ? BEACON_INTERVAL_DEF_QT :
8144 BEACON_INTERVAL_DEFAULT;
8145 bi->dtim_period = ISSIM_ENAB(wlc->pub->sih) ? DTIM_INTERVAL_DEF_QT :
8146 DTIM_INTERVAL_DEFAULT;
8147
8148 /* fill the default channel as the first valid channel
8149 * starting from the 2G channels
8150 */
8151 chanspec = CH20MHZ_CHSPEC(1);
8152 ASSERT(chanspec != INVCHANSPEC);
8153
8154 wlc->home_chanspec = bi->chanspec = chanspec;
8155
8156 /* find the band of our default channel */
8157 band = wlc->band;
8158 if (NBANDS(wlc) > 1 && band->bandunit != CHSPEC_WLCBANDUNIT(chanspec))
8159 band = wlc->bandstate[OTHERBANDUNIT(wlc)];
8160
8161 /* init bss rates to the band specific default rate set */
8162 wlc_rateset_default(&bi->rateset, NULL, band->phytype, band->bandtype,
0965ae88 8163 false, RATE_MASK_FULL, (bool) N_ENAB(wlc->pub),
a9533e7e
HP
8164 CHSPEC_WLC_BW(chanspec), wlc->stf->txstreams);
8165
8166 if (N_ENAB(wlc->pub))
8167 bi->flags |= WLC_BSS_HT;
8168}
8169
8170/* Deferred event processing */
8171static void wlc_process_eventq(void *arg)
8172{
8173 wlc_info_t *wlc = (wlc_info_t *) arg;
8174 wlc_event_t *etmp;
8175
8176 while ((etmp = wlc_eventq_deq(wlc->eventq))) {
8177 /* Perform OS specific event processing */
8178 wl_event(wlc->wl, etmp->event.ifname, etmp);
8179 if (etmp->data) {
182acb3c 8180 kfree(etmp->data);
a9533e7e
HP
8181 etmp->data = NULL;
8182 }
8183 wlc_event_free(wlc->eventq, etmp);
8184 }
8185}
8186
8187void
66cbd3ab 8188wlc_uint64_sub(u32 *a_high, u32 *a_low, u32 b_high, u32 b_low)
a9533e7e
HP
8189{
8190 if (b_low > *a_low) {
8191 /* low half needs a carry */
8192 b_high += 1;
8193 }
8194 *a_low -= b_low;
8195 *a_high -= b_high;
8196}
8197
8198static ratespec_t
66cbd3ab 8199mac80211_wlc_set_nrate(wlc_info_t *wlc, wlcband_t *cur_band, u32 int_val)
a9533e7e 8200{
41feb5ed
GKH
8201 u8 stf = (int_val & NRATE_STF_MASK) >> NRATE_STF_SHIFT;
8202 u8 rate = int_val & NRATE_RATE_MASK;
a9533e7e
HP
8203 ratespec_t rspec;
8204 bool ismcs = ((int_val & NRATE_MCS_INUSE) == NRATE_MCS_INUSE);
8205 bool issgi = ((int_val & NRATE_SGI_MASK) >> NRATE_SGI_SHIFT);
8206 bool override_mcs_only = ((int_val & NRATE_OVERRIDE_MCS_ONLY)
8207 == NRATE_OVERRIDE_MCS_ONLY);
8208 int bcmerror = 0;
8209
8210 if (!ismcs) {
8211 return (ratespec_t) rate;
8212 }
8213
8214 /* validate the combination of rate/mcs/stf is allowed */
8215 if (N_ENAB(wlc->pub) && ismcs) {
8216 /* mcs only allowed when nmode */
8217 if (stf > PHY_TXC1_MODE_SDM) {
8218 WL_ERROR(("wl%d: %s: Invalid stf\n", WLCWLUNIT(wlc),
8219 __func__));
8220 bcmerror = BCME_RANGE;
8221 goto done;
8222 }
8223
8224 /* mcs 32 is a special case, DUP mode 40 only */
8225 if (rate == 32) {
8226 if (!CHSPEC_IS40(wlc->home_chanspec) ||
8227 ((stf != PHY_TXC1_MODE_SISO)
8228 && (stf != PHY_TXC1_MODE_CDD))) {
8229 WL_ERROR(("wl%d: %s: Invalid mcs 32\n",
8230 WLCWLUNIT(wlc), __func__));
8231 bcmerror = BCME_RANGE;
8232 goto done;
8233 }
8234 /* mcs > 7 must use stf SDM */
8235 } else if (rate > HIGHEST_SINGLE_STREAM_MCS) {
8236 /* mcs > 7 must use stf SDM */
8237 if (stf != PHY_TXC1_MODE_SDM) {
8238 WL_TRACE(("wl%d: %s: enabling SDM mode for mcs %d\n", WLCWLUNIT(wlc), __func__, rate));
8239 stf = PHY_TXC1_MODE_SDM;
8240 }
8241 } else {
8242 /* MCS 0-7 may use SISO, CDD, and for phy_rev >= 3 STBC */
8243 if ((stf > PHY_TXC1_MODE_STBC) ||
8244 (!WLC_STBC_CAP_PHY(wlc)
8245 && (stf == PHY_TXC1_MODE_STBC))) {
8246 WL_ERROR(("wl%d: %s: Invalid STBC\n",
8247 WLCWLUNIT(wlc), __func__));
8248 bcmerror = BCME_RANGE;
8249 goto done;
8250 }
8251 }
8252 } else if (IS_OFDM(rate)) {
8253 if ((stf != PHY_TXC1_MODE_CDD) && (stf != PHY_TXC1_MODE_SISO)) {
8254 WL_ERROR(("wl%d: %s: Invalid OFDM\n", WLCWLUNIT(wlc),
8255 __func__));
8256 bcmerror = BCME_RANGE;
8257 goto done;
8258 }
8259 } else if (IS_CCK(rate)) {
8260 if ((cur_band->bandtype != WLC_BAND_2G)
8261 || (stf != PHY_TXC1_MODE_SISO)) {
8262 WL_ERROR(("wl%d: %s: Invalid CCK\n", WLCWLUNIT(wlc),
8263 __func__));
8264 bcmerror = BCME_RANGE;
8265 goto done;
8266 }
8267 } else {
8268 WL_ERROR(("wl%d: %s: Unknown rate type\n", WLCWLUNIT(wlc),
8269 __func__));
8270 bcmerror = BCME_RANGE;
8271 goto done;
8272 }
8273 /* make sure multiple antennae are available for non-siso rates */
8274 if ((stf != PHY_TXC1_MODE_SISO) && (wlc->stf->txstreams == 1)) {
8275 WL_ERROR(("wl%d: %s: SISO antenna but !SISO request\n",
8276 WLCWLUNIT(wlc), __func__));
8277 bcmerror = BCME_RANGE;
8278 goto done;
8279 }
8280
8281 rspec = rate;
8282 if (ismcs) {
8283 rspec |= RSPEC_MIMORATE;
8284 /* For STBC populate the STC field of the ratespec */
8285 if (stf == PHY_TXC1_MODE_STBC) {
41feb5ed 8286 u8 stc;
a9533e7e
HP
8287 stc = 1; /* Nss for single stream is always 1 */
8288 rspec |= (stc << RSPEC_STC_SHIFT);
8289 }
8290 }
8291
8292 rspec |= (stf << RSPEC_STF_SHIFT);
8293
8294 if (override_mcs_only)
8295 rspec |= RSPEC_OVERRIDE_MCS_ONLY;
8296
8297 if (issgi)
8298 rspec |= RSPEC_SHORT_GI;
8299
8300 if ((rate != 0)
0f0881b0 8301 && !wlc_valid_rate(wlc, rspec, cur_band->bandtype, true)) {
a9533e7e
HP
8302 return rate;
8303 }
8304
8305 return rspec;
8306 done:
8307 WL_ERROR(("Hoark\n"));
8308 return rate;
8309}
8310
8311/* formula: IDLE_BUSY_RATIO_X_16 = (100-duty_cycle)/duty_cycle*16 */
8312static int
7cc4a4c0 8313wlc_duty_cycle_set(wlc_info_t *wlc, int duty_cycle, bool isOFDM,
a9533e7e
HP
8314 bool writeToShm)
8315{
8316 int idle_busy_ratio_x_16 = 0;
8317 uint offset =
8318 isOFDM ? M_TX_IDLE_BUSY_RATIO_X_16_OFDM :
8319 M_TX_IDLE_BUSY_RATIO_X_16_CCK;
8320 if (duty_cycle > 100 || duty_cycle < 0) {
8321 WL_ERROR(("wl%d: duty cycle value off limit\n",
8322 wlc->pub->unit));
8323 return BCME_RANGE;
8324 }
8325 if (duty_cycle)
8326 idle_busy_ratio_x_16 = (100 - duty_cycle) * 16 / duty_cycle;
8327 /* Only write to shared memory when wl is up */
8328 if (writeToShm)
7d4df48e 8329 wlc_write_shm(wlc, offset, (u16) idle_busy_ratio_x_16);
a9533e7e
HP
8330
8331 if (isOFDM)
7d4df48e 8332 wlc->tx_duty_cycle_ofdm = (u16) duty_cycle;
a9533e7e 8333 else
7d4df48e 8334 wlc->tx_duty_cycle_cck = (u16) duty_cycle;
a9533e7e
HP
8335
8336 return BCME_OK;
8337}
8338
7d4df48e 8339/* Read a single u16 from shared memory.
a9533e7e
HP
8340 * SHM 'offset' needs to be an even address
8341 */
7d4df48e 8342u16 wlc_read_shm(wlc_info_t *wlc, uint offset)
a9533e7e
HP
8343{
8344 return wlc_bmac_read_shm(wlc->hw, offset);
8345}
8346
7d4df48e 8347/* Write a single u16 to shared memory.
a9533e7e
HP
8348 * SHM 'offset' needs to be an even address
8349 */
7d4df48e 8350void wlc_write_shm(wlc_info_t *wlc, uint offset, u16 v)
a9533e7e
HP
8351{
8352 wlc_bmac_write_shm(wlc->hw, offset, v);
8353}
8354
8355/* Set a range of shared memory to a value.
8356 * SHM 'offset' needs to be an even address and
8357 * Range length 'len' must be an even number of bytes
8358 */
7d4df48e 8359void wlc_set_shm(wlc_info_t *wlc, uint offset, u16 v, int len)
a9533e7e
HP
8360{
8361 /* offset and len need to be even */
8362 ASSERT((offset & 1) == 0);
8363 ASSERT((len & 1) == 0);
8364
8365 if (len <= 0)
8366 return;
8367
8368 wlc_bmac_set_shm(wlc->hw, offset, v, len);
8369}
8370
8371/* Copy a buffer to shared memory.
8372 * SHM 'offset' needs to be an even address and
8373 * Buffer length 'len' must be an even number of bytes
8374 */
7cc4a4c0 8375void wlc_copyto_shm(wlc_info_t *wlc, uint offset, const void *buf, int len)
a9533e7e
HP
8376{
8377 /* offset and len need to be even */
8378 ASSERT((offset & 1) == 0);
8379 ASSERT((len & 1) == 0);
8380
8381 if (len <= 0)
8382 return;
8383 wlc_bmac_copyto_objmem(wlc->hw, offset, buf, len, OBJADDR_SHM_SEL);
8384
8385}
8386
8387/* Copy from shared memory to a buffer.
8388 * SHM 'offset' needs to be an even address and
8389 * Buffer length 'len' must be an even number of bytes
8390 */
7cc4a4c0 8391void wlc_copyfrom_shm(wlc_info_t *wlc, uint offset, void *buf, int len)
a9533e7e
HP
8392{
8393 /* offset and len need to be even */
8394 ASSERT((offset & 1) == 0);
8395 ASSERT((len & 1) == 0);
8396
8397 if (len <= 0)
8398 return;
8399
8400 wlc_bmac_copyfrom_objmem(wlc->hw, offset, buf, len, OBJADDR_SHM_SEL);
8401}
8402
8403/* wrapper BMAC functions to for HIGH driver access */
66cbd3ab 8404void wlc_mctrl(wlc_info_t *wlc, u32 mask, u32 val)
a9533e7e
HP
8405{
8406 wlc_bmac_mctrl(wlc->hw, mask, val);
8407}
8408
66cbd3ab 8409void wlc_corereset(wlc_info_t *wlc, u32 flags)
a9533e7e
HP
8410{
8411 wlc_bmac_corereset(wlc->hw, flags);
8412}
8413
7d4df48e 8414void wlc_mhf(wlc_info_t *wlc, u8 idx, u16 mask, u16 val, int bands)
a9533e7e
HP
8415{
8416 wlc_bmac_mhf(wlc->hw, idx, mask, val, bands);
8417}
8418
7d4df48e 8419u16 wlc_mhf_get(wlc_info_t *wlc, u8 idx, int bands)
a9533e7e
HP
8420{
8421 return wlc_bmac_mhf_get(wlc->hw, idx, bands);
8422}
8423
7cc4a4c0 8424int wlc_xmtfifo_sz_get(wlc_info_t *wlc, uint fifo, uint *blocks)
a9533e7e
HP
8425{
8426 return wlc_bmac_xmtfifo_sz_get(wlc->hw, fifo, blocks);
8427}
8428
7cc4a4c0 8429void wlc_write_template_ram(wlc_info_t *wlc, int offset, int len, void *buf)
a9533e7e
HP
8430{
8431 wlc_bmac_write_template_ram(wlc->hw, offset, len, buf);
8432}
8433
7cc4a4c0 8434void wlc_write_hw_bcntemplates(wlc_info_t *wlc, void *bcn, int len, bool both)
a9533e7e
HP
8435{
8436 wlc_bmac_write_hw_bcntemplates(wlc->hw, bcn, len, both);
8437}
8438
8439void
7cc4a4c0 8440wlc_set_addrmatch(wlc_info_t *wlc, int match_reg_offset,
a9533e7e
HP
8441 const struct ether_addr *addr)
8442{
8443 wlc_bmac_set_addrmatch(wlc->hw, match_reg_offset, addr);
8444}
8445
7cc4a4c0 8446void wlc_set_rcmta(wlc_info_t *wlc, int idx, const struct ether_addr *addr)
a9533e7e
HP
8447{
8448 wlc_bmac_set_rcmta(wlc->hw, idx, addr);
8449}
8450
66cbd3ab 8451void wlc_read_tsf(wlc_info_t *wlc, u32 *tsf_l_ptr, u32 *tsf_h_ptr)
a9533e7e
HP
8452{
8453 wlc_bmac_read_tsf(wlc->hw, tsf_l_ptr, tsf_h_ptr);
8454}
8455
7d4df48e 8456void wlc_set_cwmin(wlc_info_t *wlc, u16 newmin)
a9533e7e
HP
8457{
8458 wlc->band->CWmin = newmin;
8459 wlc_bmac_set_cwmin(wlc->hw, newmin);
8460}
8461
7d4df48e 8462void wlc_set_cwmax(wlc_info_t *wlc, u16 newmax)
a9533e7e
HP
8463{
8464 wlc->band->CWmax = newmax;
8465 wlc_bmac_set_cwmax(wlc->hw, newmax);
8466}
8467
7cc4a4c0 8468void wlc_fifoerrors(wlc_info_t *wlc)
a9533e7e
HP
8469{
8470
8471 wlc_bmac_fifoerrors(wlc->hw);
8472}
8473
8474/* Search mem rw utilities */
8475
7cc4a4c0 8476void wlc_pllreq(wlc_info_t *wlc, bool set, mbool req_bit)
a9533e7e
HP
8477{
8478 wlc_bmac_pllreq(wlc->hw, set, req_bit);
8479}
8480
7cc4a4c0 8481void wlc_reset_bmac_done(wlc_info_t *wlc)
a9533e7e
HP
8482{
8483#ifdef WLC_HIGH_ONLY
0965ae88 8484 wlc->reset_bmac_pending = false;
a9533e7e
HP
8485#endif
8486}
8487
41feb5ed 8488void wlc_ht_mimops_cap_update(wlc_info_t *wlc, u8 mimops_mode)
a9533e7e
HP
8489{
8490 wlc->ht_cap.cap &= ~HT_CAP_MIMO_PS_MASK;
8491 wlc->ht_cap.cap |= (mimops_mode << HT_CAP_MIMO_PS_SHIFT);
8492
8493 if (AP_ENAB(wlc->pub) && wlc->clk) {
8494 wlc_update_beacon(wlc);
0f0881b0 8495 wlc_update_probe_resp(wlc, true);
a9533e7e
HP
8496 }
8497}
8498
8499/* check for the particular priority flow control bit being set */
8500bool
7cc4a4c0 8501wlc_txflowcontrol_prio_isset(wlc_info_t *wlc, wlc_txq_info_t *q, int prio)
a9533e7e
HP
8502{
8503 uint prio_mask;
8504
8505 if (prio == ALLPRIO) {
8506 prio_mask = TXQ_STOP_FOR_PRIOFC_MASK;
8507 } else {
8508 ASSERT(prio >= 0 && prio <= MAXPRIO);
8509 prio_mask = NBITVAL(prio);
8510 }
8511
8512 return (q->stopped & prio_mask) == prio_mask;
8513}
8514
8515/* propogate the flow control to all interfaces using the given tx queue */
7cc4a4c0 8516void wlc_txflowcontrol(wlc_info_t *wlc, wlc_txq_info_t *qi, bool on, int prio)
a9533e7e
HP
8517{
8518 uint prio_bits;
8519 uint cur_bits;
8520
8521 WL_ERROR(("%s: flow contro kicks in\n", __func__));
8522
8523 if (prio == ALLPRIO) {
8524 prio_bits = TXQ_STOP_FOR_PRIOFC_MASK;
8525 } else {
8526 ASSERT(prio >= 0 && prio <= MAXPRIO);
8527 prio_bits = NBITVAL(prio);
8528 }
8529
8530 cur_bits = qi->stopped & prio_bits;
8531
8532 /* Check for the case of no change and return early
8533 * Otherwise update the bit and continue
8534 */
8535 if (on) {
8536 if (cur_bits == prio_bits) {
8537 return;
8538 }
8539 mboolset(qi->stopped, prio_bits);
8540 } else {
8541 if (cur_bits == 0) {
8542 return;
8543 }
8544 mboolclr(qi->stopped, prio_bits);
8545 }
8546
8547 /* If there is a flow control override we will not change the external
8548 * flow control state.
8549 */
8550 if (qi->stopped & ~TXQ_STOP_FOR_PRIOFC_MASK) {
8551 return;
8552 }
8553
8554 wlc_txflowcontrol_signal(wlc, qi, on, prio);
8555}
8556
8557void
7cc4a4c0 8558wlc_txflowcontrol_override(wlc_info_t *wlc, wlc_txq_info_t *qi, bool on,
a9533e7e
HP
8559 uint override)
8560{
8561 uint prev_override;
8562
8563 ASSERT(override != 0);
8564 ASSERT((override & TXQ_STOP_FOR_PRIOFC_MASK) == 0);
8565
8566 prev_override = (qi->stopped & ~TXQ_STOP_FOR_PRIOFC_MASK);
8567
8568 /* Update the flow control bits and do an early return if there is
8569 * no change in the external flow control state.
8570 */
8571 if (on) {
8572 mboolset(qi->stopped, override);
8573 /* if there was a previous override bit on, then setting this
8574 * makes no difference.
8575 */
8576 if (prev_override) {
8577 return;
8578 }
8579
8580 wlc_txflowcontrol_signal(wlc, qi, ON, ALLPRIO);
8581 } else {
8582 mboolclr(qi->stopped, override);
8583 /* clearing an override bit will only make a difference for
8584 * flow control if it was the only bit set. For any other
8585 * override setting, just return
8586 */
8587 if (prev_override != override) {
8588 return;
8589 }
8590
8591 if (qi->stopped == 0) {
8592 wlc_txflowcontrol_signal(wlc, qi, OFF, ALLPRIO);
8593 } else {
8594 int prio;
8595
8596 for (prio = MAXPRIO; prio >= 0; prio--) {
8597 if (!mboolisset(qi->stopped, NBITVAL(prio)))
8598 wlc_txflowcontrol_signal(wlc, qi, OFF,
8599 prio);
8600 }
8601 }
8602 }
8603}
8604
7cc4a4c0 8605static void wlc_txflowcontrol_reset(wlc_info_t *wlc)
a9533e7e
HP
8606{
8607 wlc_txq_info_t *qi;
8608
8609 for (qi = wlc->tx_queues; qi != NULL; qi = qi->next) {
8610 if (qi->stopped) {
8611 wlc_txflowcontrol_signal(wlc, qi, OFF, ALLPRIO);
8612 qi->stopped = 0;
8613 }
8614 }
8615}
8616
8617static void
7cc4a4c0 8618wlc_txflowcontrol_signal(wlc_info_t *wlc, wlc_txq_info_t *qi, bool on,
a9533e7e
HP
8619 int prio)
8620{
8621 wlc_if_t *wlcif;
8622
8623 for (wlcif = wlc->wlcif_list; wlcif != NULL; wlcif = wlcif->next) {
8624 if (wlcif->qi == qi && wlcif->flags & WLC_IF_LINKED)
8625 wl_txflowcontrol(wlc->wl, wlcif->wlif, on, prio);
8626 }
8627}
8628
7cc4a4c0 8629static wlc_txq_info_t *wlc_txq_alloc(wlc_info_t *wlc, osl_t *osh)
a9533e7e
HP
8630{
8631 wlc_txq_info_t *qi, *p;
8632
8633 qi = (wlc_txq_info_t *) wlc_calloc(osh, wlc->pub->unit,
8634 sizeof(wlc_txq_info_t));
8635 if (qi == NULL) {
8636 return NULL;
8637 }
8638
8639 /* Have enough room for control packets along with HI watermark */
8640 /* Also, add room to txq for total psq packets if all the SCBs leave PS mode */
8641 /* The watermark for flowcontrol to OS packets will remain the same */
8642 pktq_init(&qi->q, WLC_PREC_COUNT,
8643 (2 * wlc->pub->tunables->datahiwat) + PKTQ_LEN_DEFAULT +
8644 wlc->pub->psq_pkts_total);
8645
8646 /* add this queue to the the global list */
8647 p = wlc->tx_queues;
8648 if (p == NULL) {
8649 wlc->tx_queues = qi;
8650 } else {
8651 while (p->next != NULL)
8652 p = p->next;
8653 p->next = qi;
8654 }
8655
8656 return qi;
8657}
8658
7cc4a4c0 8659static void wlc_txq_free(wlc_info_t *wlc, osl_t *osh, wlc_txq_info_t *qi)
a9533e7e
HP
8660{
8661 wlc_txq_info_t *p;
8662
8663 if (qi == NULL)
8664 return;
8665
8666 /* remove the queue from the linked list */
8667 p = wlc->tx_queues;
8668 if (p == qi)
8669 wlc->tx_queues = p->next;
8670 else {
8671 while (p != NULL && p->next != qi)
8672 p = p->next;
8673 ASSERT(p->next == qi);
8674 if (p != NULL)
8675 p->next = p->next->next;
8676 }
8677
182acb3c 8678 kfree(qi);
a9533e7e 8679}
This page took 0.421641 seconds and 5 git commands to generate.