15b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
25b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Copyright (c) 2010 Broadcom Corporation
35b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
45b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Permission to use, copy, modify, and/or distribute this software for any
55b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * purpose with or without fee is hereby granted, provided that the above
65b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * copyright notice and this permission notice appear in all copies.
75b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
85b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
95b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define __UNDEF_NO_VERSION__
1802f77195db6ce252d5488b6d48d8edc1c5e2aa30Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/etherdevice.h>
215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/sched.h>
225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/firmware.h>
235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/interrupt.h>
24452962366c11a9126fabac8cb28af49c27464408Paul Gortmaker#include <linux/module.h>
252e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel#include <linux/bcma/bcma.h>
265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <net/mac80211.h>
275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <defs.h>
285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "nicpci.h"
295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "phy/phy_int.h"
305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "d11.h"
315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "channel.h"
325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "scb.h"
335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "pub.h"
345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "ucode_loader.h"
355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "mac80211_if.h"
365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "main.h"
375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define N_TX_QUEUES	4 /* #tx queues on mac80211<->driver interface */
395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Flags we support */
415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define MAC_FILTERS (FIF_PROMISC_IN_BSS | \
425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	FIF_ALLMULTI | \
435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	FIF_FCSFAIL | \
445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	FIF_CONTROL | \
455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	FIF_OTHER_BSS | \
46be667669ec01d514b3820f8c74d9336115be6aa7Alwin Beukers	FIF_BCN_PRBRESP_PROMISC | \
47be667669ec01d514b3820f8c74d9336115be6aa7Alwin Beukers	FIF_PSPOLL)
485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CHAN2GHZ(channel, freqency, chflags)  { \
505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.band = IEEE80211_BAND_2GHZ, \
515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.center_freq = (freqency), \
525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.hw_value = (channel), \
535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.flags = chflags, \
545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.max_antenna_gain = 0, \
555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.max_power = 19, \
565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CHAN5GHZ(channel, chflags)  { \
595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.band = IEEE80211_BAND_5GHZ, \
605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.center_freq = 5000 + 5*(channel), \
615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.hw_value = (channel), \
625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.flags = chflags, \
635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.max_antenna_gain = 0, \
645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.max_power = 21, \
655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define RATE(rate100m, _flags) { \
685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.bitrate = (rate100m), \
695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.flags = (_flags), \
705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.hw_value = (rate100m / 5), \
715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
735b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct firmware_hdr {
745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 offset;
755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 len;
765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 idx;
775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
795b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic const char * const brcms_firmwares[MAX_FW_IMAGES] = {
805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	"brcm/bcm43xx",
815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	NULL
825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
845b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int n_adapters_found;
855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
865b435de0d786869c95d1962121af0d7df2542009Arend van SprielMODULE_AUTHOR("Broadcom Corporation");
875b435de0d786869c95d1962121af0d7df2542009Arend van SprielMODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver.");
885b435de0d786869c95d1962121af0d7df2542009Arend van SprielMODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN cards");
895b435de0d786869c95d1962121af0d7df2542009Arend van SprielMODULE_LICENSE("Dual BSD/GPL");
905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
922e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel/* recognized BCMA Core IDs */
932e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Sprielstatic struct bcma_device_id brcms_coreid_table[] = {
94eb032f03cbf7ac7baf78ae89a832100561bf0e29Arend van Spriel	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 23, BCMA_ANY_CLASS),
95eb032f03cbf7ac7baf78ae89a832100561bf0e29Arend van Spriel	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 24, BCMA_ANY_CLASS),
962e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel	BCMA_CORETABLE_END
972e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel};
982e756560a8a47ce754b852d0bc1ff7549433d0ebArend van SprielMODULE_DEVICE_TABLE(bcma, brcms_coreid_table);
995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1008ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
1015b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int msglevel = 0xdeadbeef;
1025b435de0d786869c95d1962121af0d7df2542009Arend van Sprielmodule_param(msglevel, int, 0);
1038ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#endif				/* DEBUG */
1045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1055b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic struct ieee80211_channel brcms_2ghz_chantable[] = {
1065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN2GHZ(1, 2412, IEEE80211_CHAN_NO_HT40MINUS),
1075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN2GHZ(2, 2417, IEEE80211_CHAN_NO_HT40MINUS),
1085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN2GHZ(3, 2422, IEEE80211_CHAN_NO_HT40MINUS),
1095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN2GHZ(4, 2427, IEEE80211_CHAN_NO_HT40MINUS),
1105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN2GHZ(5, 2432, 0),
1115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN2GHZ(6, 2437, 0),
1125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN2GHZ(7, 2442, 0),
1135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN2GHZ(8, 2447, IEEE80211_CHAN_NO_HT40PLUS),
1145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN2GHZ(9, 2452, IEEE80211_CHAN_NO_HT40PLUS),
1155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN2GHZ(10, 2457, IEEE80211_CHAN_NO_HT40PLUS),
1165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN2GHZ(11, 2462, IEEE80211_CHAN_NO_HT40PLUS),
1175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN2GHZ(12, 2467,
1185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
1195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_NO_HT40PLUS),
1205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN2GHZ(13, 2472,
1215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
1225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_NO_HT40PLUS),
1235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN2GHZ(14, 2484,
1245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
1255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
1265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
1275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1285b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic struct ieee80211_channel brcms_5ghz_nphy_chantable[] = {
1295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* UNII-1 */
1305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(36, IEEE80211_CHAN_NO_HT40MINUS),
1315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(40, IEEE80211_CHAN_NO_HT40PLUS),
1325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(44, IEEE80211_CHAN_NO_HT40MINUS),
1335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(48, IEEE80211_CHAN_NO_HT40PLUS),
1345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* UNII-2 */
1355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(52,
1365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
1385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(56,
1395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
1415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(60,
1425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
1445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(64,
1455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
1475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* MID */
1485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(100,
1495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
1515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(104,
1525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
1545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(108,
1555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
1575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(112,
1585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
1605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(116,
1615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
1635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(120,
1645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
1665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(124,
1675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
1695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(128,
1705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
1725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(132,
1735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
1755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(136,
1765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
1785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(140,
1795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
1805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS |
1815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 IEEE80211_CHAN_NO_HT40MINUS),
1825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* UNII-3 */
1835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(149, IEEE80211_CHAN_NO_HT40MINUS),
1845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(153, IEEE80211_CHAN_NO_HT40PLUS),
1855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(157, IEEE80211_CHAN_NO_HT40MINUS),
1865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(161, IEEE80211_CHAN_NO_HT40PLUS),
1875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	CHAN5GHZ(165, IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
1885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
1895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
1915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * The rate table is used for both 2.4G and 5G rates. The
1925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * latter being a subset as it does not support CCK rates.
1935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
1945b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic struct ieee80211_rate legacy_ratetable[] = {
1955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	RATE(10, 0),
1965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	RATE(20, IEEE80211_RATE_SHORT_PREAMBLE),
1975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	RATE(55, IEEE80211_RATE_SHORT_PREAMBLE),
1985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	RATE(110, IEEE80211_RATE_SHORT_PREAMBLE),
1995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	RATE(60, 0),
2005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	RATE(90, 0),
2015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	RATE(120, 0),
2025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	RATE(180, 0),
2035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	RATE(240, 0),
2045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	RATE(360, 0),
2055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	RATE(480, 0),
2065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	RATE(540, 0),
2075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
2085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2095b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic const struct ieee80211_supported_band brcms_band_2GHz_nphy_template = {
2105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.band = IEEE80211_BAND_2GHZ,
2115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.channels = brcms_2ghz_chantable,
2125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.n_channels = ARRAY_SIZE(brcms_2ghz_chantable),
2135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.bitrates = legacy_ratetable,
2145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.n_bitrates = ARRAY_SIZE(legacy_ratetable),
2155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.ht_cap = {
2165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		   /* from include/linux/ieee80211.h */
2175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		   .cap = IEEE80211_HT_CAP_GRN_FLD |
2186b1a89afbf97f40797255b9543d441ce361dbb52Arend van Spriel			  IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40,
2195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		   .ht_supported = true,
2205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		   .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
2215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		   .ampdu_density = AMPDU_DEF_MPDU_DENSITY,
2225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		   .mcs = {
2235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			   /* placeholders for now */
2245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			   .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
2255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			   .rx_highest = cpu_to_le16(500),
2265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			   .tx_params = IEEE80211_HT_MCS_TX_DEFINED}
2275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		   }
2285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
2295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2305b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic const struct ieee80211_supported_band brcms_band_5GHz_nphy_template = {
2315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.band = IEEE80211_BAND_5GHZ,
2325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.channels = brcms_5ghz_nphy_chantable,
2335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.n_channels = ARRAY_SIZE(brcms_5ghz_nphy_chantable),
2345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.bitrates = legacy_ratetable + BRCMS_LEGACY_5G_RATE_OFFSET,
2355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.n_bitrates = ARRAY_SIZE(legacy_ratetable) -
2365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			BRCMS_LEGACY_5G_RATE_OFFSET,
2375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.ht_cap = {
2385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		   .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 |
2396b1a89afbf97f40797255b9543d441ce361dbb52Arend van Spriel			  IEEE80211_HT_CAP_SGI_40,
2405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		   .ht_supported = true,
2415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		   .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
2425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		   .ampdu_density = AMPDU_DEF_MPDU_DENSITY,
2435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		   .mcs = {
2445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			   /* placeholders for now */
2455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			   .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
2465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			   .rx_highest = cpu_to_le16(500),
2475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			   .tx_params = IEEE80211_HT_MCS_TX_DEFINED}
2485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		   }
2495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
2505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* flags the given rate in rateset as requested */
2525b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void brcms_set_basic_rate(struct brcm_rateset *rs, u16 rate, bool is_br)
2535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
2545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 i;
2555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	for (i = 0; i < rs->count; i++) {
2575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (rate != (rs->rates[i] & 0x7f))
2585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			continue;
2595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (is_br)
2615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			rs->rates[i] |= BRCMS_RATE_FLAG;
2625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		else
2635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			rs->rates[i] &= BRCMS_RATE_MASK;
2645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return;
2655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
2665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
2675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2685b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void brcms_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
2695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
2705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_info *wl = hw->priv;
2715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_bh(&wl->lock);
2735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!wl->pub->up) {
2745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_err(wl->wiphy, "ops->tx called while down\n");
2755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		kfree_skb(skb);
2765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
2775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
2785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcms_c_sendpkt_mac80211(wl->wlc, skb, hw);
2795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel done:
2805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock_bh(&wl->lock);
2815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
2825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2835b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int brcms_ops_start(struct ieee80211_hw *hw)
2845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
2855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_info *wl = hw->priv;
2865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool blocked;
2872646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	int err;
2885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	ieee80211_wake_queues(hw);
2905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_bh(&wl->lock);
2915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	blocked = brcms_rfkill_set_hw_state(wl);
2925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock_bh(&wl->lock);
2935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!blocked)
2945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
2955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2962646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	spin_lock_bh(&wl->lock);
297dc460127898cab9014fb06281e0bad37b198bd83Roland Vossen	/* avoid acknowledging frames before a non-monitor device is added */
298dc460127898cab9014fb06281e0bad37b198bd83Roland Vossen	wl->mute_tx = true;
299dc460127898cab9014fb06281e0bad37b198bd83Roland Vossen
3002646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	if (!wl->pub->up)
3012646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen		err = brcms_up(wl);
3022646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	else
3032646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen		err = -ENODEV;
3042646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	spin_unlock_bh(&wl->lock);
3052646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen
3062646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	if (err != 0)
3072646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen		wiphy_err(hw->wiphy, "%s: brcms_up() returned %d\n", __func__,
3082646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen			  err);
3092646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	return err;
3105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
3115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3125b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void brcms_ops_stop(struct ieee80211_hw *hw)
3135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
3142646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	struct brcms_info *wl = hw->priv;
3152646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	int status;
3162646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen
3175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	ieee80211_stop_queues(hw);
3182646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen
3192646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	if (wl->wlc == NULL)
3202646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen		return;
3212646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen
3222646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	spin_lock_bh(&wl->lock);
3232646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	status = brcms_c_chipmatch(wl->wlc->hw->vendorid,
3242646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen				   wl->wlc->hw->deviceid);
3252646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	spin_unlock_bh(&wl->lock);
3262646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	if (!status) {
3272646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen		wiphy_err(wl->wiphy,
3282646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen			  "wl: brcms_ops_stop: chipmatch failed\n");
3292646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen		return;
3302646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	}
3312646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen
3322646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	/* put driver in down state */
3332646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	spin_lock_bh(&wl->lock);
3342646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	brcms_down(wl);
3352646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	spin_unlock_bh(&wl->lock);
3365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
3375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3385b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int
3395b435de0d786869c95d1962121af0d7df2542009Arend van Sprielbrcms_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
3405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
341dc460127898cab9014fb06281e0bad37b198bd83Roland Vossen	struct brcms_info *wl = hw->priv;
342dc460127898cab9014fb06281e0bad37b198bd83Roland Vossen
3435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Just STA for now */
3441525662ac280e61feb1af7778881241b542dc075Roland Vossen	if (vif->type != NL80211_IFTYPE_STATION) {
3455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_err(hw->wiphy, "%s: Attempt to add type %d, only"
3465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  " STA for now\n", __func__, vif->type);
3475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return -EOPNOTSUPP;
3485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
3495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
350dc460127898cab9014fb06281e0bad37b198bd83Roland Vossen	wl->mute_tx = false;
351dc460127898cab9014fb06281e0bad37b198bd83Roland Vossen	brcms_c_mute(wl->wlc, false);
352dc460127898cab9014fb06281e0bad37b198bd83Roland Vossen
3532646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	return 0;
3545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
3555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3565b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void
3575b435de0d786869c95d1962121af0d7df2542009Arend van Sprielbrcms_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
3585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
3595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
3605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3615b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int brcms_ops_config(struct ieee80211_hw *hw, u32 changed)
3625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
3635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct ieee80211_conf *conf = &hw->conf;
3645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_info *wl = hw->priv;
3655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int err = 0;
3665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int new_int;
3675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct wiphy *wiphy = hw->wiphy;
3685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_bh(&wl->lock);
3705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) {
3715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcms_c_set_beacon_listen_interval(wl->wlc,
3725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						   conf->listen_interval);
3735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
3745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed & IEEE80211_CONF_CHANGE_MONITOR)
375be667669ec01d514b3820f8c74d9336115be6aa7Alwin Beukers		wiphy_dbg(wiphy, "%s: change monitor mode: %s\n",
3765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  __func__, conf->flags & IEEE80211_CONF_MONITOR ?
3775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  "true" : "false");
3785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed & IEEE80211_CONF_CHANGE_PS)
3795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_err(wiphy, "%s: change power-save mode: %s (implement)\n",
3805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  __func__, conf->flags & IEEE80211_CONF_PS ?
3815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  "true" : "false");
3825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed & IEEE80211_CONF_CHANGE_POWER) {
3845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		err = brcms_c_set_tx_power(wl->wlc, conf->power_level);
3855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (err < 0) {
3865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			wiphy_err(wiphy, "%s: Error setting power_level\n",
3875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  __func__);
3885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			goto config_out;
3895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
3905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		new_int = brcms_c_get_tx_power(wl->wlc);
3915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (new_int != conf->power_level)
3925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			wiphy_err(wiphy, "%s: Power level req != actual, %d %d"
3935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  "\n", __func__, conf->power_level,
3945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  new_int);
3955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
3965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
3975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (conf->channel_type == NL80211_CHAN_HT20 ||
3985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		    conf->channel_type == NL80211_CHAN_NO_HT)
3995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			err = brcms_c_set_channel(wl->wlc,
4005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						  conf->channel->hw_value);
4015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		else
4025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			err = -ENOTSUPP;
4035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
4045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
4055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		err = brcms_c_set_rate_limit(wl->wlc,
4065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					     conf->short_frame_max_tx_count,
4075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					     conf->long_frame_max_tx_count);
4085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel config_out:
4105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock_bh(&wl->lock);
4115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return err;
4125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
4135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4145b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void
4155b435de0d786869c95d1962121af0d7df2542009Arend van Sprielbrcms_ops_bss_info_changed(struct ieee80211_hw *hw,
4165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			struct ieee80211_vif *vif,
4175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			struct ieee80211_bss_conf *info, u32 changed)
4185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
4195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_info *wl = hw->priv;
4205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct wiphy *wiphy = hw->wiphy;
4215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed & BSS_CHANGED_ASSOC) {
4235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* association status changed (associated/disassociated)
4245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 * also implies a change in the AID.
4255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 */
4265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_err(wiphy, "%s: %s: %sassociated\n", KBUILD_MODNAME,
4275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  __func__, info->assoc ? "" : "dis");
4285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_lock_bh(&wl->lock);
4295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcms_c_associate_upd(wl->wlc, info->assoc);
4305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_unlock_bh(&wl->lock);
4315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
4325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed & BSS_CHANGED_ERP_SLOT) {
4335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		s8 val;
4345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* slot timing changed */
4365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (info->use_short_slot)
4375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			val = 1;
4385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		else
4395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			val = 0;
4405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_lock_bh(&wl->lock);
4415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcms_c_set_shortslot_override(wl->wlc, val);
4425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_unlock_bh(&wl->lock);
4435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
4445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed & BSS_CHANGED_HT) {
4465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* 802.11n parameters changed */
4475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		u16 mode = info->ht_operation_mode;
4485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_lock_bh(&wl->lock);
4505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_CFG,
4515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			mode & IEEE80211_HT_OP_MODE_PROTECTION);
4525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_NONGF,
4535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
4545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_OBSS,
4555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			mode & IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT);
4565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_unlock_bh(&wl->lock);
4575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
4585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed & BSS_CHANGED_BASIC_RATES) {
4595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		struct ieee80211_supported_band *bi;
4605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		u32 br_mask, i;
4615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		u16 rate;
4625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		struct brcm_rateset rs;
4635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		int error;
4645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* retrieve the current rates */
4665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_lock_bh(&wl->lock);
4675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcms_c_get_current_rateset(wl->wlc, &rs);
4685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_unlock_bh(&wl->lock);
4695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		br_mask = info->basic_rates;
4715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bi = hw->wiphy->bands[brcms_c_get_curband(wl->wlc)];
4725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		for (i = 0; i < bi->n_bitrates; i++) {
4735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* convert to internal rate value */
4745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			rate = (bi->bitrates[i].bitrate << 1) / 10;
4755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* set/clear basic rate flag */
4775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcms_set_basic_rate(&rs, rate, br_mask & 1);
4785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			br_mask >>= 1;
4795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
4805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* update the rate set */
4825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_lock_bh(&wl->lock);
4835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		error = brcms_c_set_rateset(wl->wlc, &rs);
4845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_unlock_bh(&wl->lock);
4855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (error)
4865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			wiphy_err(wiphy, "changing basic rates failed: %d\n",
4875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  error);
4885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
4895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed & BSS_CHANGED_BEACON_INT) {
4905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Beacon interval changed */
4915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_lock_bh(&wl->lock);
4925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcms_c_set_beacon_period(wl->wlc, info->beacon_int);
4935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_unlock_bh(&wl->lock);
4945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
4955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed & BSS_CHANGED_BSSID) {
4965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* BSSID changed, for whatever reason (IBSS and managed mode) */
4975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_lock_bh(&wl->lock);
4985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcms_c_set_addrmatch(wl->wlc, RCM_BSSID_OFFSET, info->bssid);
4995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_unlock_bh(&wl->lock);
5005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
5015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed & BSS_CHANGED_BEACON)
5025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Beacon data changed, retrieve new beacon (beaconing modes) */
5035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_err(wiphy, "%s: beacon changed\n", __func__);
5045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed & BSS_CHANGED_BEACON_ENABLED) {
5065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Beaconing should be enabled/disabled (beaconing modes) */
5075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_err(wiphy, "%s: Beacon enabled: %s\n", __func__,
5085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  info->enable_beacon ? "true" : "false");
5095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
5105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed & BSS_CHANGED_CQM) {
5125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Connection quality monitor config changed */
5135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_err(wiphy, "%s: cqm change: threshold %d, hys %d "
5145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  " (implement)\n", __func__, info->cqm_rssi_thold,
5155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  info->cqm_rssi_hyst);
5165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
5175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed & BSS_CHANGED_IBSS) {
5195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* IBSS join status changed */
5205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_err(wiphy, "%s: IBSS joined: %s (implement)\n", __func__,
5215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  info->ibss_joined ? "true" : "false");
5225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
5235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed & BSS_CHANGED_ARP_FILTER) {
5255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Hardware ARP filter address list or state changed */
5265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_err(wiphy, "%s: arp filtering: enabled %s, count %d"
5275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  " (implement)\n", __func__, info->arp_filter_enabled ?
5285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  "true" : "false", info->arp_addr_cnt);
5295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
5305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed & BSS_CHANGED_QOS) {
5325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/*
5335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 * QoS for this association was enabled/disabled.
5345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 * Note that it is only ever disabled for station mode.
5355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 */
5365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_err(wiphy, "%s: qos enabled: %s (implement)\n", __func__,
5375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  info->qos ? "true" : "false");
5385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
5395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return;
5405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
5415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5425b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void
5435b435de0d786869c95d1962121af0d7df2542009Arend van Sprielbrcms_ops_configure_filter(struct ieee80211_hw *hw,
5445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			unsigned int changed_flags,
5455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			unsigned int *total_flags, u64 multicast)
5465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
5475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_info *wl = hw->priv;
5485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct wiphy *wiphy = hw->wiphy;
5495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	changed_flags &= MAC_FILTERS;
5515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	*total_flags &= MAC_FILTERS;
552be667669ec01d514b3820f8c74d9336115be6aa7Alwin Beukers
5535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed_flags & FIF_PROMISC_IN_BSS)
554be667669ec01d514b3820f8c74d9336115be6aa7Alwin Beukers		wiphy_dbg(wiphy, "FIF_PROMISC_IN_BSS\n");
5555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed_flags & FIF_ALLMULTI)
556be667669ec01d514b3820f8c74d9336115be6aa7Alwin Beukers		wiphy_dbg(wiphy, "FIF_ALLMULTI\n");
5575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed_flags & FIF_FCSFAIL)
558be667669ec01d514b3820f8c74d9336115be6aa7Alwin Beukers		wiphy_dbg(wiphy, "FIF_FCSFAIL\n");
5595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed_flags & FIF_CONTROL)
560be667669ec01d514b3820f8c74d9336115be6aa7Alwin Beukers		wiphy_dbg(wiphy, "FIF_CONTROL\n");
5615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (changed_flags & FIF_OTHER_BSS)
562be667669ec01d514b3820f8c74d9336115be6aa7Alwin Beukers		wiphy_dbg(wiphy, "FIF_OTHER_BSS\n");
563be667669ec01d514b3820f8c74d9336115be6aa7Alwin Beukers	if (changed_flags & FIF_PSPOLL)
564be667669ec01d514b3820f8c74d9336115be6aa7Alwin Beukers		wiphy_dbg(wiphy, "FIF_PSPOLL\n");
565be667669ec01d514b3820f8c74d9336115be6aa7Alwin Beukers	if (changed_flags & FIF_BCN_PRBRESP_PROMISC)
566be667669ec01d514b3820f8c74d9336115be6aa7Alwin Beukers		wiphy_dbg(wiphy, "FIF_BCN_PRBRESP_PROMISC\n");
567be667669ec01d514b3820f8c74d9336115be6aa7Alwin Beukers
568be667669ec01d514b3820f8c74d9336115be6aa7Alwin Beukers	spin_lock_bh(&wl->lock);
569be667669ec01d514b3820f8c74d9336115be6aa7Alwin Beukers	brcms_c_mac_promisc(wl->wlc, *total_flags);
570be667669ec01d514b3820f8c74d9336115be6aa7Alwin Beukers	spin_unlock_bh(&wl->lock);
5715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return;
5725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
5735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5745b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void brcms_ops_sw_scan_start(struct ieee80211_hw *hw)
5755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
5765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_info *wl = hw->priv;
5775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_bh(&wl->lock);
5785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcms_c_scan_start(wl->wlc);
5795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock_bh(&wl->lock);
5805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return;
5815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
5825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5835b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw)
5845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
5855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_info *wl = hw->priv;
5865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_bh(&wl->lock);
5875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcms_c_scan_stop(wl->wlc);
5885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock_bh(&wl->lock);
5895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return;
5905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
5915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5925b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int
5935b435de0d786869c95d1962121af0d7df2542009Arend van Sprielbrcms_ops_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue,
5945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		  const struct ieee80211_tx_queue_params *params)
5955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
5965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_info *wl = hw->priv;
5975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_bh(&wl->lock);
5995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcms_c_wme_setparams(wl->wlc, queue, params, true);
6005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock_bh(&wl->lock);
6015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
6035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
6045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6055b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int
6065b435de0d786869c95d1962121af0d7df2542009Arend van Sprielbrcms_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
6075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	       struct ieee80211_sta *sta)
6085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
6095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_info *wl = hw->priv;
6105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct scb *scb = &wl->wlc->pri_scb;
6115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcms_c_init_scb(scb);
6135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wl->pub->global_ampdu = &(scb->scb_ampdu);
6155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wl->pub->global_ampdu->scb = scb;
6165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wl->pub->global_ampdu->max_pdu = 16;
6175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/*
6195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * minstrel_ht initiates addBA on our behalf by calling
6205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * ieee80211_start_tx_ba_session()
6215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 */
6225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
6235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
6245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6255b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int
6265b435de0d786869c95d1962121af0d7df2542009Arend van Sprielbrcms_ops_ampdu_action(struct ieee80211_hw *hw,
6275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		    struct ieee80211_vif *vif,
6285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		    enum ieee80211_ampdu_mlme_action action,
6295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		    struct ieee80211_sta *sta, u16 tid, u16 *ssn,
6305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		    u8 buf_size)
6315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
6325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_info *wl = hw->priv;
6335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct scb *scb = &wl->wlc->pri_scb;
6345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int status;
6355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (WARN_ON(scb->magic != SCB_MAGIC))
6375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return -EIDRM;
6385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	switch (action) {
6395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	case IEEE80211_AMPDU_RX_START:
6405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		break;
6415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	case IEEE80211_AMPDU_RX_STOP:
6425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		break;
6435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	case IEEE80211_AMPDU_TX_START:
6445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_lock_bh(&wl->lock);
6455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		status = brcms_c_aggregatable(wl->wlc, tid);
6465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_unlock_bh(&wl->lock);
6475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (!status) {
6485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			wiphy_err(wl->wiphy, "START: tid %d is not agg\'able\n",
6495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  tid);
6505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return -EINVAL;
6515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
6525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
6535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		break;
6545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	case IEEE80211_AMPDU_TX_STOP:
6565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_lock_bh(&wl->lock);
6575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcms_c_ampdu_flush(wl->wlc, sta, tid);
6585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_unlock_bh(&wl->lock);
6595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
6605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		break;
6615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	case IEEE80211_AMPDU_TX_OPERATIONAL:
6625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/*
6635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 * BA window size from ADDBA response ('buf_size') defines how
6645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 * many outstanding MPDUs are allowed for the BA stream by
6655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 * recipient and traffic class. 'ampdu_factor' gives maximum
6665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 * AMPDU size.
6675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 */
6685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_lock_bh(&wl->lock);
6695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcms_c_ampdu_tx_operational(wl->wlc, tid, buf_size,
6705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			(1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
6715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 sta->ht_cap.ampdu_factor)) - 1);
6725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_unlock_bh(&wl->lock);
6735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Power save wakeup */
6745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		break;
6755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	default:
6765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_err(wl->wiphy, "%s: Invalid command, ignoring\n",
6775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  __func__);
6785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
6795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
6815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
6825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6835b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void brcms_ops_rfkill_poll(struct ieee80211_hw *hw)
6845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
6855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_info *wl = hw->priv;
6865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool blocked;
6875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_bh(&wl->lock);
6895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	blocked = brcms_c_check_radio_disabled(wl->wlc);
6905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock_bh(&wl->lock);
6915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked);
6935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
6945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6955b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void brcms_ops_flush(struct ieee80211_hw *hw, bool drop)
6965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
6975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_info *wl = hw->priv;
6985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	no_printk("%s: drop = %s\n", __func__, drop ? "true" : "false");
7005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* wait for packet queue and dma fifos to run empty */
7025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_bh(&wl->lock);
7035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcms_c_wait_for_tx_completion(wl->wlc, drop);
7045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock_bh(&wl->lock);
7055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
7065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7075b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic const struct ieee80211_ops brcms_ops = {
7085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.tx = brcms_ops_tx,
7095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.start = brcms_ops_start,
7105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.stop = brcms_ops_stop,
7115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.add_interface = brcms_ops_add_interface,
7125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.remove_interface = brcms_ops_remove_interface,
7135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.config = brcms_ops_config,
7145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.bss_info_changed = brcms_ops_bss_info_changed,
7155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.configure_filter = brcms_ops_configure_filter,
7165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.sw_scan_start = brcms_ops_sw_scan_start,
7175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.sw_scan_complete = brcms_ops_sw_scan_complete,
7185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.conf_tx = brcms_ops_conf_tx,
7195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.sta_add = brcms_ops_sta_add,
7205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.ampdu_action = brcms_ops_ampdu_action,
7215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.rfkill_poll = brcms_ops_rfkill_poll,
7225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.flush = brcms_ops_flush,
7235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
7245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
7262e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel * is called in brcms_bcma_probe() context, therefore no locking required.
7275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
7285b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int brcms_set_hint(struct brcms_info *wl, char *abbrev)
7295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
7305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return regulatory_hint(wl->pub->ieee_hw->wiphy, abbrev);
7315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
7325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7335b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcms_dpc(unsigned long data)
7345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
7355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_info *wl;
7365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wl = (struct brcms_info *) data;
7385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_bh(&wl->lock);
7405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* call the common second level interrupt handler */
7425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (wl->pub->up) {
7435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (wl->resched) {
7445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			unsigned long flags;
7455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			spin_lock_irqsave(&wl->isr_lock, flags);
7475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcms_c_intrsupd(wl->wlc);
7485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			spin_unlock_irqrestore(&wl->isr_lock, flags);
7495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
7505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wl->resched = brcms_c_dpc(wl->wlc, true);
7525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
7535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* brcms_c_dpc() may bring the driver down */
7555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!wl->pub->up)
7565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
7575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* re-schedule dpc */
7595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (wl->resched)
7605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		tasklet_schedule(&wl->tasklet);
7615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	else
7625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* re-enable interrupts */
7635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcms_intrson(wl);
7645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel done:
7665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock_bh(&wl->lock);
7675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
7685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
7705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Precondition: Since this function is called in brcms_pci_probe() context,
7715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * no locking is required.
7725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
7735b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int brcms_request_fw(struct brcms_info *wl, struct pci_dev *pdev)
7745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
7755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int status;
7765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct device *device = &pdev->dev;
7775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	char fw_name[100];
7785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int i;
7795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	memset(&wl->fw, 0, sizeof(struct brcms_firmware));
7815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	for (i = 0; i < MAX_FW_IMAGES; i++) {
7825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (brcms_firmwares[i] == NULL)
7835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			break;
7845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		sprintf(fw_name, "%s-%d.fw", brcms_firmwares[i],
7855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			UCODE_LOADER_API_VER);
7865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		status = request_firmware(&wl->fw.fw_bin[i], fw_name, device);
7875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (status) {
7885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n",
7895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  KBUILD_MODNAME, fw_name);
7905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return status;
7915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
7925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		sprintf(fw_name, "%s_hdr-%d.fw", brcms_firmwares[i],
7935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			UCODE_LOADER_API_VER);
7945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		status = request_firmware(&wl->fw.fw_hdr[i], fw_name, device);
7955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (status) {
7965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n",
7975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  KBUILD_MODNAME, fw_name);
7985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return status;
7995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
8005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wl->fw.hdr_num_entries[i] =
8015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		    wl->fw.fw_hdr[i]->size / (sizeof(struct firmware_hdr));
8025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
8035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wl->fw.fw_cnt = i;
8045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return brcms_ucode_data_init(wl, &wl->ucode);
8055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
8065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
8085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Precondition: Since this function is called in brcms_pci_probe() context,
8095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * no locking is required.
8105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
8115b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void brcms_release_fw(struct brcms_info *wl)
8125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
8135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int i;
8145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	for (i = 0; i < MAX_FW_IMAGES; i++) {
8155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		release_firmware(wl->fw.fw_bin[i]);
8165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		release_firmware(wl->fw.fw_hdr[i]);
8175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
8185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
8195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/**
8215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * This function frees the WL per-device resources.
8225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
8235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * This function frees resources owned by the WL device pointed to
8245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * by the wl parameter.
8255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
8265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * precondition: can both be called locked and unlocked
8275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
8285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
8295b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void brcms_free(struct brcms_info *wl)
8305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
8315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_timer *t, *next;
8325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* free ucode data */
8345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (wl->fw.fw_cnt)
8355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcms_ucode_data_free(&wl->ucode);
8365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (wl->irq)
8375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		free_irq(wl->irq, wl);
8385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* kill dpc */
8405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	tasklet_kill(&wl->tasklet);
8415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (wl->pub)
8435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcms_c_module_unregister(wl->pub, "linux", wl);
8445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* free common resources */
8465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (wl->wlc) {
8475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcms_c_detach(wl->wlc);
8485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wl->wlc = NULL;
8495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wl->pub = NULL;
8505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
8515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* virtual interface deletion is deferred so we cannot spinwait */
8535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* wait for all pending callbacks to complete */
8555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	while (atomic_read(&wl->callbacks) > 0)
8565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		schedule();
8575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* free timers */
8595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	for (t = wl->timers; t; t = next) {
8605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		next = t->next;
8618ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
8625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		kfree(t->name);
8635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#endif
8645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		kfree(t);
8655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
8665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
8675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
8692646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen* called from both kernel as from this kernel module (error flow on attach)
8705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel* precondition: perimeter lock is not acquired.
8715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel*/
8722e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Sprielstatic void brcms_remove(struct bcma_device *pdev)
8735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
8742e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel	struct ieee80211_hw *hw = bcma_get_drvdata(pdev);
8752646c46d56792bdb370784d1cd6e696a7b3bbf67Roland Vossen	struct brcms_info *wl = hw->priv;
8765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (wl->wlc) {
8785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, false);
8795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
8805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ieee80211_unregister_hw(hw);
8815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
8825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcms_free(wl);
8845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8852e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel	bcma_set_drvdata(pdev, NULL);
8865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	ieee80211_free_hw(hw);
8875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
8885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8895b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic irqreturn_t brcms_isr(int irq, void *dev_id)
8905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
8915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_info *wl;
8925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool ours, wantdpc;
8935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wl = (struct brcms_info *) dev_id;
8955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock(&wl->isr_lock);
8975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* call common first level interrupt handler */
8995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	ours = brcms_c_isr(wl->wlc, &wantdpc);
9005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ours) {
9015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* if more to do... */
9025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (wantdpc) {
9035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* ...and call the second level interrupt handler */
9055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* schedule dpc */
9065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			tasklet_schedule(&wl->tasklet);
9075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
9085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
9095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock(&wl->isr_lock);
9115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return IRQ_RETVAL(ours);
9135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
9145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
9165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * is called in brcms_pci_probe() context, therefore no locking required.
9175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
9185b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int ieee_hw_rate_init(struct ieee80211_hw *hw)
9195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
9205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_info *wl = hw->priv;
9215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_c_info *wlc = wl->wlc;
9225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct ieee80211_supported_band *band;
9235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int has_5g = 0;
9245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 phy_type;
9255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
9275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
9285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	phy_type = brcms_c_get_phy_type(wl->wlc, 0);
9305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (phy_type == PHY_TYPE_N || phy_type == PHY_TYPE_LCN) {
9315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		band = &wlc->bandstate[BAND_2G_INDEX]->band;
9325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		*band = brcms_band_2GHz_nphy_template;
9335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (phy_type == PHY_TYPE_LCN) {
9345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Single stream */
9355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			band->ht_cap.mcs.rx_mask[1] = 0;
936df4492f89499fa5ba45f8fdfe1f100b4112f5368Arend van Spriel			band->ht_cap.mcs.rx_highest = cpu_to_le16(72);
9375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
9385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		hw->wiphy->bands[IEEE80211_BAND_2GHZ] = band;
9395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} else {
9405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return -EPERM;
9415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
9425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Assume all bands use the same phy.  True for 11n devices. */
9445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (wl->pub->_nbands > 1) {
9455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		has_5g++;
9465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (phy_type == PHY_TYPE_N || phy_type == PHY_TYPE_LCN) {
9475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			band = &wlc->bandstate[BAND_5G_INDEX]->band;
9485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			*band = brcms_band_5GHz_nphy_template;
9495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			hw->wiphy->bands[IEEE80211_BAND_5GHZ] = band;
9505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else {
9515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return -EPERM;
9525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
9535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
9545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
9555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
9565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
9585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * is called in brcms_pci_probe() context, therefore no locking required.
9595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
9605b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int ieee_hw_init(struct ieee80211_hw *hw)
9615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
9625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	hw->flags = IEEE80211_HW_SIGNAL_DBM
9635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	    /* | IEEE80211_HW_CONNECTION_MONITOR  What is this? */
9645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	    | IEEE80211_HW_REPORTS_TX_ACK_STATUS
9655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	    | IEEE80211_HW_AMPDU_AGGREGATION;
9665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	hw->extra_tx_headroom = brcms_c_get_header_len();
9685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	hw->queues = N_TX_QUEUES;
9695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	hw->max_rates = 2;	/* Primary rate and 1 fallback rate */
9705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* channel change time is dependent on chip and band  */
9725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	hw->channel_change_time = 7 * 1000;
9735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
9745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	hw->rate_control_algorithm = "minstrel_ht";
9765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	hw->sta_data_size = 0;
9785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return ieee_hw_rate_init(hw);
9795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
9805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/**
9825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * attach to the WL device.
9835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
9845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Attach to the WL device identified by vendor and device parameters.
9855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * regs is a host accessible memory address pointing to WL device registers.
9865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
9875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * brcms_attach is not defined as static because in the case where no bus
9885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * is defined, wl_attach will never be called, and thus, gcc will issue
9895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * a warning that this function is defined but not used if we declare
9905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * it as static.
9915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
9925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
9932e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel * is called in brcms_bcma_probe() context, therefore no locking required.
9945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
9952e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Sprielstatic struct brcms_info *brcms_attach(struct bcma_device *pdev)
9965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
9975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_info *wl = NULL;
9985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int unit, err;
9995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct ieee80211_hw *hw;
10005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 perm[ETH_ALEN];
10015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	unit = n_adapters_found;
10035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	err = 0;
10045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (unit < 0)
10065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return NULL;
10075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* allocate private info */
10092e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel	hw = bcma_get_drvdata(pdev);
10105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (hw != NULL)
10115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wl = hw->priv;
10125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (WARN_ON(hw == NULL) || WARN_ON(wl == NULL))
10135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return NULL;
10145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wl->wiphy = hw->wiphy;
10155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	atomic_set(&wl->callbacks, 0);
10175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* setup the bottom half handler */
10195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	tasklet_init(&wl->tasklet, brcms_dpc, (unsigned long) wl);
10205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_init(&wl->lock);
10225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_init(&wl->isr_lock);
10235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* prepare ucode */
10252e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel	if (brcms_request_fw(wl, pdev->bus->host_pci) < 0) {
10265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_err(wl->wiphy, "%s: Failed to find firmware usually in "
10275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  "%s\n", KBUILD_MODNAME, "/lib/firmware/brcm");
10285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcms_release_fw(wl);
10292e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel		brcms_remove(pdev);
10305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return NULL;
10315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
10325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* common load-time initialization */
1034b63337a0344d7ebf3c8d710b1327d0b61c0f6f03Arend van Spriel	wl->wlc = brcms_c_attach((void *)wl, pdev, unit, false, &err);
10355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcms_release_fw(wl);
10365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!wl->wlc) {
10375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_err(wl->wiphy, "%s: attach() failed with code %d\n",
10385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  KBUILD_MODNAME, err);
10395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
10405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
10415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wl->pub = brcms_c_pub(wl->wlc);
10425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wl->pub->ieee_hw = hw;
10445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* register our interrupt handler */
10462e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel	if (request_irq(pdev->bus->host_pci->irq, brcms_isr,
10472e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel			IRQF_SHARED, KBUILD_MODNAME, wl)) {
10485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_err(wl->wiphy, "wl%d: request_irq() failed\n", unit);
10495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
10505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
10512e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel	wl->irq = pdev->bus->host_pci->irq;
10525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* register module */
10545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcms_c_module_register(wl->pub, "linux", wl, NULL);
10555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ieee_hw_init(hw)) {
10575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_err(wl->wiphy, "wl%d: %s: ieee_hw_init failed!\n", unit,
10585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  __func__);
10595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
10605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
10615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	memcpy(perm, &wl->pub->cur_etheraddr, ETH_ALEN);
10635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (WARN_ON(!is_valid_ether_addr(perm)))
10645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
10655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	SET_IEEE80211_PERM_ADDR(hw, perm);
10665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	err = ieee80211_register_hw(hw);
10685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (err)
10695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_err(wl->wiphy, "%s: ieee80211_register_hw failed, status"
10705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  "%d\n", __func__, err);
10715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (wl->pub->srom_ccode[0])
10735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		err = brcms_set_hint(wl, wl->pub->srom_ccode);
10745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	else
10755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		err = brcms_set_hint(wl, "US");
10765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (err)
10775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_err(wl->wiphy, "%s: regulatory_hint failed, status %d\n",
10785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  __func__, err);
10795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	n_adapters_found++;
10815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return wl;
10825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10835b435de0d786869c95d1962121af0d7df2542009Arend van Sprielfail:
10845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcms_free(wl);
10855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return NULL;
10865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
10875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/**
10915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * determines if a device is a WL device, and if so, attaches it.
10925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
10935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * This function determines if a device pointed to by pdev is a WL device,
10945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * and if so, performs a brcms_attach() on it.
10955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
10965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Perimeter lock is initialized in the course of this function.
10975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
10982e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Sprielstatic int __devinit brcms_bcma_probe(struct bcma_device *pdev)
10995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
11005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_info *wl;
11015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct ieee80211_hw *hw;
11025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11032e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel	dev_info(&pdev->dev, "mfg %x core %x rev %d class %d irq %d\n",
11042e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel		 pdev->id.manuf, pdev->id.id, pdev->id.rev, pdev->id.class,
11052e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel		 pdev->bus->host_pci->irq);
11065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11072e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel	if ((pdev->id.manuf != BCMA_MANUF_BCM) ||
11082e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel	    (pdev->id.id != BCMA_CORE_80211))
11095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return -ENODEV;
11105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	hw = ieee80211_alloc_hw(sizeof(struct brcms_info), &brcms_ops);
11125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!hw) {
11135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		pr_err("%s: ieee80211_alloc_hw failed\n", __func__);
11145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return -ENOMEM;
11155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
11165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	SET_IEEE80211_DEV(hw, &pdev->dev);
11185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11192e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel	bcma_set_drvdata(pdev, hw);
11205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	memset(hw->priv, 0, sizeof(*wl));
11225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11232e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel	wl = brcms_attach(pdev);
11245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!wl) {
112502f77195db6ce252d5488b6d48d8edc1c5e2aa30Joe Perches		pr_err("%s: brcms_attach failed!\n", __func__);
11265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return -ENODEV;
11275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
11285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
11295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
11305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11317d5869e78f4c9d32f834dadefbb7dcb3c9d4d85fLinus Torvaldsstatic int brcms_suspend(struct bcma_device *pdev)
11325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
11335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_info *wl;
11345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct ieee80211_hw *hw;
11355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11362e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel	hw = bcma_get_drvdata(pdev);
11375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wl = hw->priv;
11385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!wl) {
1139137dabed34a13f90e068327b98329331367c9b46Arend van Spriel		pr_err("%s: %s: no driver private struct!\n", KBUILD_MODNAME,
1140137dabed34a13f90e068327b98329331367c9b46Arend van Spriel		       __func__);
11415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return -ENODEV;
11425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
11435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* only need to flag hw is down for proper resume */
11455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_bh(&wl->lock);
11465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wl->pub->hw_up = false;
11475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock_bh(&wl->lock);
11485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11497e9e7fa414e47fccc6127f23bc866cbcc7e27dcbLinus Torvalds	pr_debug("brcms_suspend ok\n");
11505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11512e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel	return 0;
11522e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel}
11532e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel
11542e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Sprielstatic int brcms_resume(struct bcma_device *pdev)
11552e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel{
11567e9e7fa414e47fccc6127f23bc866cbcc7e27dcbLinus Torvalds	pr_debug("brcms_resume ok\n");
11577e9e7fa414e47fccc6127f23bc866cbcc7e27dcbLinus Torvalds	return 0;
11585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
11595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11602e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Sprielstatic struct bcma_driver brcms_bcma_driver = {
11615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.name     = KBUILD_MODNAME,
11622e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel	.probe    = brcms_bcma_probe,
11635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.suspend  = brcms_suspend,
11645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.resume   = brcms_resume,
11655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	.remove   = __devexit_p(brcms_remove),
11662e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel	.id_table = brcms_coreid_table,
11675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
11685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/**
11702e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel * This is the main entry point for the brcmsmac driver.
11715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
1172b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Spriel * This function is scheduled upon module initialization and
1173b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Spriel * does the driver registration, which result in brcms_bcma_probe()
1174b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Spriel * call resulting in the driver bringup.
11755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
1176b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Sprielstatic void brcms_driver_init(struct work_struct *work)
11775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
1178b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Spriel	int error;
1179b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Spriel
1180b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Spriel	error = bcma_driver_register(&brcms_bcma_driver);
1181b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Spriel	if (error)
1182b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Spriel		pr_err("%s: register returned %d\n", __func__, error);
1183b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Spriel}
1184b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Spriel
1185b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Sprielstatic DECLARE_WORK(brcms_driver_work, brcms_driver_init);
11865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1187b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Sprielstatic int __init brcms_module_init(void)
1188b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Spriel{
11898ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
11905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (msglevel != 0xdeadbeef)
11915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcm_msg_level = msglevel;
1192b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Spriel#endif
1193b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Spriel	if (!schedule_work(&brcms_driver_work))
1194b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Spriel		return -EBUSY;
11955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1196b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Spriel	return 0;
11975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
11985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/**
12002e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel * This function unloads the brcmsmac driver from the system.
12015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
12022e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel * This function unconditionally unloads the brcmsmac driver module from the
12035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * system.
12045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
12055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
12065b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void __exit brcms_module_exit(void)
12075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
1208b0c359b2f68e982ac9334a5f2c04c3a67dee4d50Arend van Spriel	cancel_work_sync(&brcms_driver_work);
12092e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel	bcma_driver_unregister(&brcms_bcma_driver);
12105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
12115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12125b435de0d786869c95d1962121af0d7df2542009Arend van Sprielmodule_init(brcms_module_init);
12135b435de0d786869c95d1962121af0d7df2542009Arend van Sprielmodule_exit(brcms_module_exit);
12145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
12165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * precondition: perimeter lock has been acquired
12175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
12185b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif,
12195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 bool state, int prio)
12205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
12215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wiphy_err(wl->wiphy, "Shouldn't be here %s\n", __func__);
12225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
12235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
12255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * precondition: perimeter lock has been acquired
12265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
12275b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcms_init(struct brcms_info *wl)
12285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
12295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	BCMMSG(wl->pub->ieee_hw->wiphy, "wl%d\n", wl->pub->unit);
12305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcms_reset(wl);
1231dc460127898cab9014fb06281e0bad37b198bd83Roland Vossen	brcms_c_init(wl->wlc, wl->mute_tx);
12325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
12335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
12355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * precondition: perimeter lock has been acquired
12365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
12375b435de0d786869c95d1962121af0d7df2542009Arend van Sprieluint brcms_reset(struct brcms_info *wl)
12385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
12395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	BCMMSG(wl->pub->ieee_hw->wiphy, "wl%d\n", wl->pub->unit);
12405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcms_c_reset(wl->wlc);
12415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* dpc will not be rescheduled */
12433db1cd5c05f35fb43eb134df6f321de4e63141f2Rusty Russell	wl->resched = false;
12445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
12465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
12475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1248c261bdf8acad56717cae233709808d8d9291ce36Roland Vossenvoid brcms_fatal_error(struct brcms_info *wl)
1249c261bdf8acad56717cae233709808d8d9291ce36Roland Vossen{
1250c261bdf8acad56717cae233709808d8d9291ce36Roland Vossen	wiphy_err(wl->wlc->wiphy, "wl%d: fatal error, reinitializing\n",
1251c261bdf8acad56717cae233709808d8d9291ce36Roland Vossen		  wl->wlc->pub->unit);
1252c261bdf8acad56717cae233709808d8d9291ce36Roland Vossen	brcms_reset(wl);
1253c261bdf8acad56717cae233709808d8d9291ce36Roland Vossen	ieee80211_restart_hw(wl->pub->ieee_hw);
1254c261bdf8acad56717cae233709808d8d9291ce36Roland Vossen}
1255c261bdf8acad56717cae233709808d8d9291ce36Roland Vossen
12565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
12575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * These are interrupt on/off entry points. Disable interrupts
12585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * during interrupt state transition.
12595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
12605b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcms_intrson(struct brcms_info *wl)
12615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
12625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	unsigned long flags;
12635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_irqsave(&wl->isr_lock, flags);
12655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcms_c_intrson(wl->wlc);
12665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock_irqrestore(&wl->isr_lock, flags);
12675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
12685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12695b435de0d786869c95d1962121af0d7df2542009Arend van Sprielu32 brcms_intrsoff(struct brcms_info *wl)
12705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
12715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	unsigned long flags;
12725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 status;
12735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_irqsave(&wl->isr_lock, flags);
12755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	status = brcms_c_intrsoff(wl->wlc);
12765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock_irqrestore(&wl->isr_lock, flags);
12775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return status;
12785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
12795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12805b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcms_intrsrestore(struct brcms_info *wl, u32 macintmask)
12815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
12825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	unsigned long flags;
12835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_irqsave(&wl->isr_lock, flags);
12855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcms_c_intrsrestore(wl->wlc, macintmask);
12865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock_irqrestore(&wl->isr_lock, flags);
12875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
12885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
12905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * precondition: perimeter lock has been acquired
12915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
12925b435de0d786869c95d1962121af0d7df2542009Arend van Sprielint brcms_up(struct brcms_info *wl)
12935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
12945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int error = 0;
12955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (wl->pub->up)
12975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return 0;
12985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	error = brcms_c_up(wl->wlc);
13005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return error;
13025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
13035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
13055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * precondition: perimeter lock has been acquired
13065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
13075b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcms_down(struct brcms_info *wl)
13085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
13095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint callbacks, ret_val = 0;
13105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* call common down function */
13125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	ret_val = brcms_c_down(wl->wlc);
13135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	callbacks = atomic_read(&wl->callbacks) - ret_val;
13145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* wait for down callbacks to complete */
13165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock_bh(&wl->lock);
13175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* For HIGH_only driver, it's important to actually schedule other work,
13195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * not just spin wait since everything runs at schedule level
13205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 */
13215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	SPINWAIT((atomic_read(&wl->callbacks) > callbacks), 100 * 1000);
13225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_bh(&wl->lock);
13245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
13255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
13275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel* precondition: perimeter lock is not acquired
13285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
13292a7fc5b1c17a6055fe2753ebacaf43b5780bcf99Roland Vossenstatic void _brcms_timer(struct work_struct *work)
13305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
13312a7fc5b1c17a6055fe2753ebacaf43b5780bcf99Roland Vossen	struct brcms_timer *t = container_of(work, struct brcms_timer,
13322a7fc5b1c17a6055fe2753ebacaf43b5780bcf99Roland Vossen					     dly_wrk.work);
13332a7fc5b1c17a6055fe2753ebacaf43b5780bcf99Roland Vossen
13345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_bh(&t->wl->lock);
13355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (t->set) {
13375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (t->periodic) {
13385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			atomic_inc(&t->wl->callbacks);
13392a7fc5b1c17a6055fe2753ebacaf43b5780bcf99Roland Vossen			ieee80211_queue_delayed_work(t->wl->pub->ieee_hw,
13402a7fc5b1c17a6055fe2753ebacaf43b5780bcf99Roland Vossen						     &t->dly_wrk,
13412a7fc5b1c17a6055fe2753ebacaf43b5780bcf99Roland Vossen						     msecs_to_jiffies(t->ms));
13422a7fc5b1c17a6055fe2753ebacaf43b5780bcf99Roland Vossen		} else {
13435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			t->set = false;
13442a7fc5b1c17a6055fe2753ebacaf43b5780bcf99Roland Vossen		}
13455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		t->fn(t->arg);
13475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
13485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	atomic_dec(&t->wl->callbacks);
13505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock_bh(&t->wl->lock);
13525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
13535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
13555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Adds a timer to the list. Caller supplies a timer function.
13565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Is called from wlc.
13575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
13585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * precondition: perimeter lock has been acquired
13595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
13605b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct brcms_timer *brcms_init_timer(struct brcms_info *wl,
13615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				     void (*fn) (void *arg),
13625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				     void *arg, const char *name)
13635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
13645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_timer *t;
13655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	t = kzalloc(sizeof(struct brcms_timer), GFP_ATOMIC);
13675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!t)
13685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return NULL;
13695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13702a7fc5b1c17a6055fe2753ebacaf43b5780bcf99Roland Vossen	INIT_DELAYED_WORK(&t->dly_wrk, _brcms_timer);
13715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	t->wl = wl;
13725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	t->fn = fn;
13735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	t->arg = arg;
13745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	t->next = wl->timers;
13755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wl->timers = t;
13765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13778ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
13785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	t->name = kmalloc(strlen(name) + 1, GFP_ATOMIC);
13795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (t->name)
13805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		strcpy(t->name, name);
13815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#endif
13825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return t;
13845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
13855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
13875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * adds only the kernel timer since it's going to be more accurate
13885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * as well as it's easier to make it periodic
13895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
13905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * precondition: perimeter lock has been acquired
13915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
13922a7fc5b1c17a6055fe2753ebacaf43b5780bcf99Roland Vossenvoid brcms_add_timer(struct brcms_timer *t, uint ms, int periodic)
13935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
13942a7fc5b1c17a6055fe2753ebacaf43b5780bcf99Roland Vossen	struct ieee80211_hw *hw = t->wl->pub->ieee_hw;
13952a7fc5b1c17a6055fe2753ebacaf43b5780bcf99Roland Vossen
13968ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
13975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (t->set)
13982a7fc5b1c17a6055fe2753ebacaf43b5780bcf99Roland Vossen		wiphy_err(hw->wiphy, "%s: Already set. Name: %s, per %d\n",
13995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  __func__, t->name, periodic);
14005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#endif
14015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	t->ms = ms;
14025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	t->periodic = (bool) periodic;
14035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	t->set = true;
14045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1405be69c4ef462a476523f89c74e7db29f6ad207a1aRoland Vossen	atomic_inc(&t->wl->callbacks);
14062a7fc5b1c17a6055fe2753ebacaf43b5780bcf99Roland Vossen
14072a7fc5b1c17a6055fe2753ebacaf43b5780bcf99Roland Vossen	ieee80211_queue_delayed_work(hw, &t->dly_wrk, msecs_to_jiffies(ms));
14085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
14095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
14115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * return true if timer successfully deleted, false if still pending
14125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
14135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * precondition: perimeter lock has been acquired
14145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
1415be69c4ef462a476523f89c74e7db29f6ad207a1aRoland Vossenbool brcms_del_timer(struct brcms_timer *t)
14165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
14175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (t->set) {
14185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		t->set = false;
14192a7fc5b1c17a6055fe2753ebacaf43b5780bcf99Roland Vossen		if (!cancel_delayed_work(&t->dly_wrk))
14205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return false;
14215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1422be69c4ef462a476523f89c74e7db29f6ad207a1aRoland Vossen		atomic_dec(&t->wl->callbacks);
14235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
14245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return true;
14265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
14275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
14295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * precondition: perimeter lock has been acquired
14305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
1431be69c4ef462a476523f89c74e7db29f6ad207a1aRoland Vossenvoid brcms_free_timer(struct brcms_timer *t)
14325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
1433be69c4ef462a476523f89c74e7db29f6ad207a1aRoland Vossen	struct brcms_info *wl = t->wl;
14345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcms_timer *tmp;
14355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* delete the timer in case it is active */
1437be69c4ef462a476523f89c74e7db29f6ad207a1aRoland Vossen	brcms_del_timer(t);
14385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (wl->timers == t) {
14405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wl->timers = wl->timers->next;
14418ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
14425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		kfree(t->name);
14435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#endif
14445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		kfree(t);
14455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return;
14465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
14485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	tmp = wl->timers;
14505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	while (tmp) {
14515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (tmp->next == t) {
14525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			tmp->next = t->next;
14538ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
14545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			kfree(t->name);
14555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#endif
14565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			kfree(t);
14575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return;
14585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
14595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		tmp = tmp->next;
14605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
14615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
14635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
14655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * precondition: perimeter lock has been acquired
14665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
14675b435de0d786869c95d1962121af0d7df2542009Arend van Sprielint brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, u32 idx)
14685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
14695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int i, entry;
14705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	const u8 *pdata;
14715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct firmware_hdr *hdr;
14725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	for (i = 0; i < wl->fw.fw_cnt; i++) {
14735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		hdr = (struct firmware_hdr *)wl->fw.fw_hdr[i]->data;
14745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		for (entry = 0; entry < wl->fw.hdr_num_entries[i];
14755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		     entry++, hdr++) {
14765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			u32 len = le32_to_cpu(hdr->len);
14775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (le32_to_cpu(hdr->idx) == idx) {
14785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				pdata = wl->fw.fw_bin[i]->data +
14795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					le32_to_cpu(hdr->offset);
14801f1d528977162a1a04aaecdc7f08a5b715a58810Thomas Meyer				*pbuf = kmemdup(pdata, len, GFP_ATOMIC);
14815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				if (*pbuf == NULL)
14825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					goto fail;
14835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				return 0;
14855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
14865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
14875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
14885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wiphy_err(wl->wiphy, "ERROR: ucode buf tag:%d can not be found!\n",
14895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		  idx);
14905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	*pbuf = NULL;
14915b435de0d786869c95d1962121af0d7df2542009Arend van Sprielfail:
14925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return -ENODATA;
14935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
14945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
14962e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel * Precondition: Since this function is called in brcms_bcma_probe() context,
14975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * no locking is required.
14985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
14995b435de0d786869c95d1962121af0d7df2542009Arend van Sprielint brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes, u32 idx)
15005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
15015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int i, entry;
15025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	const u8 *pdata;
15035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct firmware_hdr *hdr;
15045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	for (i = 0; i < wl->fw.fw_cnt; i++) {
15055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		hdr = (struct firmware_hdr *)wl->fw.fw_hdr[i]->data;
15065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		for (entry = 0; entry < wl->fw.hdr_num_entries[i];
15075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		     entry++, hdr++) {
15085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (le32_to_cpu(hdr->idx) == idx) {
15095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				pdata = wl->fw.fw_bin[i]->data +
15105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					le32_to_cpu(hdr->offset);
15115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				if (le32_to_cpu(hdr->len) != 4) {
15125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					wiphy_err(wl->wiphy,
15135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						  "ERROR: fw hdr len\n");
15145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					return -ENOMSG;
15155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				}
15165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				*n_bytes = le32_to_cpu(*((__le32 *) pdata));
15175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				return 0;
15185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
15195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
15205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
15215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wiphy_err(wl->wiphy, "ERROR: ucode tag:%d can not be found!\n", idx);
15225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return -ENOMSG;
15235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
15245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
15265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * precondition: can both be called locked and unlocked
15275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
15285b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcms_ucode_free_buf(void *p)
15295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
15305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	kfree(p);
15315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
15325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
15345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * checks validity of all firmware images loaded from user space
15355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
15362e756560a8a47ce754b852d0bc1ff7549433d0ebArend van Spriel * Precondition: Since this function is called in brcms_bcma_probe() context,
15375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * no locking is required.
15385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
15395b435de0d786869c95d1962121af0d7df2542009Arend van Sprielint brcms_check_firmwares(struct brcms_info *wl)
15405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
15415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int i;
15425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int entry;
15435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int rc = 0;
15445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	const struct firmware *fw;
15455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	const struct firmware *fw_hdr;
15465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct firmware_hdr *ucode_hdr;
15475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	for (i = 0; i < MAX_FW_IMAGES && rc == 0; i++) {
15485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		fw =  wl->fw.fw_bin[i];
15495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		fw_hdr = wl->fw.fw_hdr[i];
15505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (fw == NULL && fw_hdr == NULL) {
15515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			break;
15525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else if (fw == NULL || fw_hdr == NULL) {
15535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			wiphy_err(wl->wiphy, "%s: invalid bin/hdr fw\n",
15545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  __func__);
15555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			rc = -EBADF;
15565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else if (fw_hdr->size % sizeof(struct firmware_hdr)) {
15575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			wiphy_err(wl->wiphy, "%s: non integral fw hdr file "
15585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				"size %zu/%zu\n", __func__, fw_hdr->size,
15595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				sizeof(struct firmware_hdr));
15605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			rc = -EBADF;
15615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else if (fw->size < MIN_FW_SIZE || fw->size > MAX_FW_SIZE) {
15625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			wiphy_err(wl->wiphy, "%s: out of bounds fw file size "
15635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  "%zu\n", __func__, fw->size);
15645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			rc = -EBADF;
15655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else {
15665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* check if ucode section overruns firmware image */
15675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			ucode_hdr = (struct firmware_hdr *)fw_hdr->data;
15685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			for (entry = 0; entry < wl->fw.hdr_num_entries[i] &&
15695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			     !rc; entry++, ucode_hdr++) {
15705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				if (le32_to_cpu(ucode_hdr->offset) +
15715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				    le32_to_cpu(ucode_hdr->len) >
15725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				    fw->size) {
15735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					wiphy_err(wl->wiphy,
15745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						  "%s: conflicting bin/hdr\n",
15755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						  __func__);
15765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					rc = -EBADF;
15775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				}
15785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
15795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
15805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
15815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (rc == 0 && wl->fw.fw_cnt != i) {
15825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_err(wl->wiphy, "%s: invalid fw_cnt=%d\n", __func__,
15835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			wl->fw.fw_cnt);
15845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		rc = -EBADF;
15855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
15865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return rc;
15875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
15885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
15905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * precondition: perimeter lock has been acquired
15915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
15925b435de0d786869c95d1962121af0d7df2542009Arend van Sprielbool brcms_rfkill_set_hw_state(struct brcms_info *wl)
15935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
15945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool blocked = brcms_c_check_radio_disabled(wl->wlc);
15955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock_bh(&wl->lock);
15975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked);
15985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (blocked)
15995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wiphy_rfkill_start_polling(wl->pub->ieee_hw->wiphy);
16005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_bh(&wl->lock);
16015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return blocked;
16025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
16035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
16045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
16055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * precondition: perimeter lock has been acquired
16065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
16075b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcms_msleep(struct brcms_info *wl, uint ms)
16085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
16095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock_bh(&wl->lock);
16105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	msleep(ms);
16115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_bh(&wl->lock);
16125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
1613