提交 9ebea382 编写于 作者: J John W. Linville

Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless

Conflicts:
	drivers/net/wireless/ath/ath9k/main.c
	drivers/net/wireless/iwlwifi/dvm/tx.c
......@@ -1353,6 +1353,14 @@ W: http://wireless.kernel.org/en/users/Drivers/ath9k
S: Supported
F: drivers/net/wireless/ath/ath9k/
WILOCITY WIL6210 WIRELESS DRIVER
M: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
L: linux-wireless@vger.kernel.org
L: wil6210@qca.qualcomm.com
S: Supported
W: http://wireless.kernel.org/en/users/Drivers/wil6210
F: drivers/net/wireless/ath/wil6210/
CARL9170 LINUX COMMUNITY WIRELESS DRIVER
M: Christian Lamparter <chunkeey@googlemail.com>
L: linux-wireless@vger.kernel.org
......
......@@ -67,8 +67,7 @@ config BCMA_DRIVER_GMAC_CMN
config BCMA_DRIVER_GPIO
bool "BCMA GPIO driver"
depends on BCMA
select GPIOLIB
depends on BCMA && GPIOLIB
help
Driver to provide access to the GPIO pins of the bcma bus.
......
......@@ -35,7 +35,7 @@ static struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = {
{ "M25P40", 0x12, 0x10000, 8, },
{ "M25P16", 0x14, 0x10000, 32, },
{ "M25P32", 0x14, 0x10000, 64, },
{ "M25P32", 0x15, 0x10000, 64, },
{ "M25P64", 0x16, 0x10000, 128, },
{ "M25FL128", 0x17, 0x10000, 256, },
{ 0 },
......
......@@ -77,10 +77,15 @@ static struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x0CF3, 0x311D) },
{ USB_DEVICE(0x13d3, 0x3375) },
{ USB_DEVICE(0x04CA, 0x3005) },
{ USB_DEVICE(0x04CA, 0x3006) },
{ USB_DEVICE(0x04CA, 0x3008) },
{ USB_DEVICE(0x13d3, 0x3362) },
{ USB_DEVICE(0x0CF3, 0xE004) },
{ USB_DEVICE(0x0930, 0x0219) },
{ USB_DEVICE(0x0489, 0xe057) },
{ USB_DEVICE(0x13d3, 0x3393) },
{ USB_DEVICE(0x0489, 0xe04e) },
{ USB_DEVICE(0x0489, 0xe056) },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE02C) },
......@@ -104,10 +109,15 @@ static struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x0cf3, 0x311D), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU22 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 },
......
......@@ -135,10 +135,15 @@ static struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
......
......@@ -30,5 +30,6 @@ source "drivers/net/wireless/ath/ath9k/Kconfig"
source "drivers/net/wireless/ath/carl9170/Kconfig"
source "drivers/net/wireless/ath/ath6kl/Kconfig"
source "drivers/net/wireless/ath/ar5523/Kconfig"
source "drivers/net/wireless/ath/wil6210/Kconfig"
endif
......@@ -3,6 +3,7 @@ obj-$(CONFIG_ATH9K_HW) += ath9k/
obj-$(CONFIG_CARL9170) += carl9170/
obj-$(CONFIG_ATH6KL) += ath6kl/
obj-$(CONFIG_AR5523) += ar5523/
obj-$(CONFIG_WIL6210) += wil6210/
obj-$(CONFIG_ATH_COMMON) += ath.o
......
......@@ -2,6 +2,7 @@ config ATH9K_HW
tristate
config ATH9K_COMMON
tristate
select ATH_COMMON
config ATH9K_DFS_DEBUGFS
def_bool y
depends on ATH9K_DEBUGFS && ATH9K_DFS_CERTIFIED
......@@ -17,7 +18,6 @@ config ATH9K_BTCOEX_SUPPORT
config ATH9K
tristate "Atheros 802.11n wireless cards support"
depends on MAC80211
select ATH_COMMON
select ATH9K_HW
select MAC80211_LEDS
select LEDS_CLASS
......@@ -56,7 +56,8 @@ config ATH9K_AHB
config ATH9K_DEBUGFS
bool "Atheros ath9k debugging"
depends on ATH9K && DEBUG_FS
depends on ATH9K
select MAC80211_DEBUGFS
---help---
Say Y, if you need access to ath9k's statistics for
interrupts, rate control, etc.
......
......@@ -1023,6 +1023,8 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah,
AR_PHY_AGC_CONTROL_FLTR_CAL |
AR_PHY_AGC_CONTROL_PKDET_CAL;
ar9003_hw_set_chain_masks(ah, ah->caps.rx_chainmask, ah->caps.tx_chainmask);
if (rtt) {
if (!ar9003_hw_rtt_restore(ah, chan))
run_rtt_cal = true;
......
......@@ -575,7 +575,7 @@ static void ar9003_rx_gain_table_mode0(struct ath_hw *ah)
ar9340Common_rx_gain_table_1p0);
else if (AR_SREV_9485_11(ah))
INIT_INI_ARRAY(&ah->iniModesRxGain,
ar9485Common_wo_xlna_rx_gain_1_1);
ar9485_common_rx_gain_1_1);
else if (AR_SREV_9550(ah)) {
INIT_INI_ARRAY(&ah->iniModesRxGain,
ar955x_1p0_common_rx_gain_table);
......
......@@ -589,32 +589,19 @@ static void ar9003_hw_init_bb(struct ath_hw *ah,
ath9k_hw_synth_delay(ah, chan, synthDelay);
}
static void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx)
void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx)
{
switch (rx) {
case 0x5:
if (ah->caps.tx_chainmask == 5 || ah->caps.rx_chainmask == 5)
REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP,
AR_PHY_SWAP_ALT_CHAIN);
case 0x3:
case 0x1:
case 0x2:
case 0x7:
REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx);
REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx);
break;
default:
break;
}
REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx);
REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx);
if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && (tx == 0x7))
REG_WRITE(ah, AR_SELFGEN_MASK, 0x3);
else
REG_WRITE(ah, AR_SELFGEN_MASK, tx);
tx = 3;
if (tx == 0x5) {
REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP,
AR_PHY_SWAP_ALT_CHAIN);
}
REG_WRITE(ah, AR_SELFGEN_MASK, tx);
}
/*
......
......@@ -314,7 +314,6 @@ struct ath_rx {
u32 *rxlink;
u32 num_pkts;
unsigned int rxfilter;
spinlock_t rxbuflock;
struct list_head rxbuf;
struct ath_descdma rxdma;
struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX];
......@@ -324,7 +323,6 @@ struct ath_rx {
int ath_startrecv(struct ath_softc *sc);
bool ath_stoprecv(struct ath_softc *sc);
void ath_flushrecv(struct ath_softc *sc);
u32 ath_calcrxfilter(struct ath_softc *sc);
int ath_rx_init(struct ath_softc *sc, int nbufs);
void ath_rx_cleanup(struct ath_softc *sc);
......@@ -640,7 +638,6 @@ void ath_ant_comb_update(struct ath_softc *sc);
enum sc_op_flags {
SC_OP_INVALID,
SC_OP_BEACONS,
SC_OP_RXFLUSH,
SC_OP_ANI_RUN,
SC_OP_PRIM_STA_VIF,
SC_OP_HW_RESET,
......
......@@ -147,6 +147,7 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
skb->len, DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
bf->bf_buf_addr = 0;
bf->bf_mpdu = NULL;
}
skb = ieee80211_beacon_get(hw, vif);
......@@ -359,7 +360,6 @@ void ath9k_beacon_tasklet(unsigned long data)
return;
bf = ath9k_beacon_generate(sc->hw, vif);
WARN_ON(!bf);
if (sc->beacon.bmisscnt != 0) {
ath_dbg(common, BSTUCK, "resume beacon xmit after %u misses\n",
......
......@@ -862,7 +862,6 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf,
RXS_ERR("RX-LENGTH-ERR", rx_len_err);
RXS_ERR("RX-OOM-ERR", rx_oom_err);
RXS_ERR("RX-RATE-ERR", rx_rate_err);
RXS_ERR("RX-DROP-RXFLUSH", rx_drop_rxflush);
RXS_ERR("RX-TOO-MANY-FRAGS", rx_too_many_frags_err);
PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN);
......
......@@ -217,7 +217,6 @@ struct ath_tx_stats {
* @rx_oom_err: No. of frames dropped due to OOM issues.
* @rx_rate_err: No. of frames dropped due to rate errors.
* @rx_too_many_frags_err: Frames dropped due to too-many-frags received.
* @rx_drop_rxflush: No. of frames dropped due to RX-FLUSH.
* @rx_beacons: No. of beacons received.
* @rx_frags: No. of rx-fragements received.
*/
......@@ -236,7 +235,6 @@ struct ath_rx_stats {
u32 rx_oom_err;
u32 rx_rate_err;
u32 rx_too_many_frags_err;
u32 rx_drop_rxflush;
u32 rx_beacons;
u32 rx_frags;
};
......
......@@ -344,6 +344,8 @@ void ath9k_htc_txcompletion_cb(struct htc_target *htc_handle,
endpoint->ep_callbacks.tx(endpoint->ep_callbacks.priv,
skb, htc_hdr->endpoint_id,
txok);
} else {
kfree_skb(skb);
}
}
......
......@@ -1096,6 +1096,7 @@ void ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain);
int ar9003_paprd_init_table(struct ath_hw *ah);
bool ar9003_paprd_is_done(struct ath_hw *ah);
bool ar9003_is_paprd_enabled(struct ath_hw *ah);
void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx);
/* Hardware family op attach helpers */
int ar5008_hw_attach_phy_ops(struct ath_hw *ah);
......
......@@ -182,7 +182,7 @@ static void ath_restart_work(struct ath_softc *sc)
ath_start_ani(sc);
}
static bool ath_prepare_reset(struct ath_softc *sc, bool flush)
static bool ath_prepare_reset(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
bool ret = true;
......@@ -202,14 +202,6 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool flush)
if (!ath_stoprecv(sc))
ret = false;
if (!flush) {
if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
ath_rx_tasklet(sc, 1, true);
ath_rx_tasklet(sc, 1, false);
} else {
ath_flushrecv(sc);
}
return ret;
}
......@@ -261,11 +253,11 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_hw_cal_data *caldata = NULL;
bool fastcc = true;
bool flush = false;
int r;
__ath_cancel_work(sc);
tasklet_disable(&sc->intr_tq);
spin_lock_bh(&sc->sc_pcu_lock);
if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) {
......@@ -275,11 +267,10 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
if (!hchan) {
fastcc = false;
flush = true;
hchan = ah->curchan;
}
if (!ath_prepare_reset(sc, flush))
if (!ath_prepare_reset(sc))
fastcc = false;
ath_dbg(common, CONFIG, "Reset to %u MHz, HT40: %d fastcc: %d\n",
......@@ -301,6 +292,8 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
out:
spin_unlock_bh(&sc->sc_pcu_lock);
tasklet_enable(&sc->intr_tq);
return r;
}
......@@ -801,7 +794,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
}
ath_prepare_reset(sc, true);
ath_prepare_reset(sc);
if (sc->rx.frag) {
dev_kfree_skb_any(sc->rx.frag);
......@@ -1917,6 +1910,9 @@ static u32 fill_chainmask(u32 cap, u32 new)
static bool validate_antenna_mask(struct ath_hw *ah, u32 val)
{
if (AR_SREV_9300_20_OR_LATER(ah))
return true;
switch (val & 0x7) {
case 0x1:
case 0x3:
......
......@@ -249,8 +249,6 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
static void ath_edma_start_recv(struct ath_softc *sc)
{
spin_lock_bh(&sc->rx.rxbuflock);
ath9k_hw_rxena(sc->sc_ah);
ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_HP,
......@@ -262,8 +260,6 @@ static void ath_edma_start_recv(struct ath_softc *sc)
ath_opmode_init(sc);
ath9k_hw_startpcureceive(sc->sc_ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
spin_unlock_bh(&sc->rx.rxbuflock);
}
static void ath_edma_stop_recv(struct ath_softc *sc)
......@@ -280,8 +276,6 @@ int ath_rx_init(struct ath_softc *sc, int nbufs)
int error = 0;
spin_lock_init(&sc->sc_pcu_lock);
spin_lock_init(&sc->rx.rxbuflock);
clear_bit(SC_OP_RXFLUSH, &sc->sc_flags);
common->rx_bufsize = IEEE80211_MAX_MPDU_LEN / 2 +
sc->sc_ah->caps.rx_status_len;
......@@ -439,7 +433,6 @@ int ath_startrecv(struct ath_softc *sc)
return 0;
}
spin_lock_bh(&sc->rx.rxbuflock);
if (list_empty(&sc->rx.rxbuf))
goto start_recv;
......@@ -460,26 +453,31 @@ int ath_startrecv(struct ath_softc *sc)
ath_opmode_init(sc);
ath9k_hw_startpcureceive(ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
spin_unlock_bh(&sc->rx.rxbuflock);
return 0;
}
static void ath_flushrecv(struct ath_softc *sc)
{
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
ath_rx_tasklet(sc, 1, true);
ath_rx_tasklet(sc, 1, false);
}
bool ath_stoprecv(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
bool stopped, reset = false;
spin_lock_bh(&sc->rx.rxbuflock);
ath9k_hw_abortpcurecv(ah);
ath9k_hw_setrxfilter(ah, 0);
stopped = ath9k_hw_stopdmarecv(ah, &reset);
ath_flushrecv(sc);
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
ath_edma_stop_recv(sc);
else
sc->rx.rxlink = NULL;
spin_unlock_bh(&sc->rx.rxbuflock);
if (!(ah->ah_flags & AH_UNPLUGGED) &&
unlikely(!stopped)) {
......@@ -491,15 +489,6 @@ bool ath_stoprecv(struct ath_softc *sc)
return stopped && !reset;
}
void ath_flushrecv(struct ath_softc *sc)
{
set_bit(SC_OP_RXFLUSH, &sc->sc_flags);
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
ath_rx_tasklet(sc, 1, true);
ath_rx_tasklet(sc, 1, false);
clear_bit(SC_OP_RXFLUSH, &sc->sc_flags);
}
static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb)
{
/* Check whether the Beacon frame has DTIM indicating buffered bc/mc */
......@@ -736,6 +725,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
return NULL;
}
list_del(&bf->list);
if (!bf->bf_mpdu)
return bf;
......@@ -1153,16 +1143,12 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
dma_type = DMA_FROM_DEVICE;
qtype = hp ? ATH9K_RX_QUEUE_HP : ATH9K_RX_QUEUE_LP;
spin_lock_bh(&sc->rx.rxbuflock);
tsf = ath9k_hw_gettsf64(ah);
tsf_lower = tsf & 0xffffffff;
do {
bool decrypt_error = false;
/* If handling rx interrupt and flush is in progress => exit */
if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags) && (flush == 0))
break;
memset(&rs, 0, sizeof(rs));
if (edma)
......@@ -1205,15 +1191,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
ath_debug_stat_rx(sc, &rs);
/*
* If we're asked to flush receive queue, directly
* chain it back at the queue without processing it.
*/
if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags)) {
RX_STAT_INC(rx_drop_rxflush);
goto requeue_drop_frag;
}
memset(rxs, 0, sizeof(struct ieee80211_rx_status));
rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp;
......@@ -1351,19 +1328,18 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
sc->rx.frag = NULL;
}
requeue:
list_add_tail(&bf->list, &sc->rx.rxbuf);
if (flush)
continue;
if (edma) {
list_add_tail(&bf->list, &sc->rx.rxbuf);
ath_rx_edma_buf_link(sc, qtype);
} else {
list_move_tail(&bf->list, &sc->rx.rxbuf);
ath_rx_buf_link(sc, bf);
if (!flush)
ath9k_hw_rxena(ah);
ath9k_hw_rxena(ah);
}
} while (1);
spin_unlock_bh(&sc->rx.rxbuflock);
if (!(ah->imask & ATH9K_INT_RXEOL)) {
ah->imask |= (ATH9K_INT_RXEOL | ATH9K_INT_RXORN);
ath9k_hw_set_interrupts(ah);
......
......@@ -354,8 +354,12 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len)
if (SUPP(CARL9170FW_WLANTX_CAB)) {
if_comb_types |=
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_MESH_POINT) |
BIT(NL80211_IFTYPE_P2P_GO);
#ifdef CONFIG_MAC80211_MESH
if_comb_types |=
BIT(NL80211_IFTYPE_MESH_POINT);
#endif /* CONFIG_MAC80211_MESH */
}
}
......
config WIL6210
tristate "Wilocity 60g WiFi card wil6210 support"
depends on CFG80211
depends on PCI
default n
---help---
This module adds support for wireless adapter based on
wil6210 chip by Wilocity. It supports operation on the
60 GHz band, covered by the IEEE802.11ad standard.
http://wireless.kernel.org/en/users/Drivers/wil6210
If you choose to build it as a module, it will be called
wil6210
config WIL6210_ISR_COR
bool "Use Clear-On-Read mode for ISR registers for wil6210"
depends on WIL6210
default y
---help---
ISR registers on wil6210 chip may operate in either
COR (Clear-On-Read) or W1C (Write-1-to-Clear) mode.
For production code, use COR (say y); is default since
it saves extra target transaction;
For ISR debug, use W1C (say n); is allows to monitor ISR
registers with debugfs. If COR were used, ISR would
self-clear when accessed for debug purposes, it makes
such monitoring impossible.
Say y unless you debug interrupts
obj-$(CONFIG_WIL6210) += wil6210.o
wil6210-objs := main.o
wil6210-objs += netdev.o
wil6210-objs += cfg80211.o
wil6210-objs += pcie_bus.o
wil6210-objs += debugfs.o
wil6210-objs += wmi.o
wil6210-objs += interrupt.o
wil6210-objs += txrx.o
subdir-ccflags-y += -Werror
subdir-ccflags-y += -D__CHECK_ENDIAN__
/*
* Copyright (c) 2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/sched.h>
#include <linux/etherdevice.h>
#include <linux/wireless.h>
#include <linux/ieee80211.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <net/cfg80211.h>
#include "wil6210.h"
#include "wmi.h"
#define CHAN60G(_channel, _flags) { \
.band = IEEE80211_BAND_60GHZ, \
.center_freq = 56160 + (2160 * (_channel)), \
.hw_value = (_channel), \
.flags = (_flags), \
.max_antenna_gain = 0, \
.max_power = 40, \
}
static struct ieee80211_channel wil_60ghz_channels[] = {
CHAN60G(1, 0),
CHAN60G(2, 0),
CHAN60G(3, 0),
/* channel 4 not supported yet */
};
static struct ieee80211_supported_band wil_band_60ghz = {
.channels = wil_60ghz_channels,
.n_channels = ARRAY_SIZE(wil_60ghz_channels),
.ht_cap = {
.ht_supported = true,
.cap = 0, /* TODO */
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, /* TODO */
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, /* TODO */
.mcs = {
/* MCS 1..12 - SC PHY */
.rx_mask = {0xfe, 0x1f}, /* 1..12 */
.tx_params = IEEE80211_HT_MCS_TX_DEFINED, /* TODO */
},
},
};
static const struct ieee80211_txrx_stypes
wil_mgmt_stypes[NUM_NL80211_IFTYPES] = {
[NL80211_IFTYPE_STATION] = {
.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
},
[NL80211_IFTYPE_AP] = {
.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
},
[NL80211_IFTYPE_P2P_CLIENT] = {
.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
},
[NL80211_IFTYPE_P2P_GO] = {
.tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
},
};
static const u32 wil_cipher_suites[] = {
WLAN_CIPHER_SUITE_GCMP,
};
int wil_iftype_nl2wmi(enum nl80211_iftype type)
{
static const struct {
enum nl80211_iftype nl;
enum wmi_network_type wmi;
} __nl2wmi[] = {
{NL80211_IFTYPE_ADHOC, WMI_NETTYPE_ADHOC},
{NL80211_IFTYPE_STATION, WMI_NETTYPE_INFRA},
{NL80211_IFTYPE_AP, WMI_NETTYPE_AP},
{NL80211_IFTYPE_P2P_CLIENT, WMI_NETTYPE_P2P},
{NL80211_IFTYPE_P2P_GO, WMI_NETTYPE_P2P},
{NL80211_IFTYPE_MONITOR, WMI_NETTYPE_ADHOC}, /* FIXME */
};
uint i;
for (i = 0; i < ARRAY_SIZE(__nl2wmi); i++) {
if (__nl2wmi[i].nl == type)
return __nl2wmi[i].wmi;
}
return -EOPNOTSUPP;
}
static int wil_cfg80211_get_station(struct wiphy *wiphy,
struct net_device *ndev,
u8 *mac, struct station_info *sinfo)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
int rc;
struct wmi_notify_req_cmd cmd = {
.cid = 0,
.interval_usec = 0,
};
if (memcmp(mac, wil->dst_addr[0], ETH_ALEN))
return -ENOENT;
/* WMI_NOTIFY_REQ_DONE_EVENTID handler fills wil->stats.bf_mcs */
rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd),
WMI_NOTIFY_REQ_DONE_EVENTID, NULL, 0, 20);
if (rc)
return rc;
sinfo->generation = wil->sinfo_gen;
sinfo->filled |= STATION_INFO_TX_BITRATE;
sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G;
sinfo->txrate.mcs = wil->stats.bf_mcs;
sinfo->filled |= STATION_INFO_RX_BITRATE;
sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G;
sinfo->rxrate.mcs = wil->stats.last_mcs_rx;
if (test_bit(wil_status_fwconnected, &wil->status)) {
sinfo->filled |= STATION_INFO_SIGNAL;
sinfo->signal = 12; /* TODO: provide real value */
}
return 0;
}
static int wil_cfg80211_change_iface(struct wiphy *wiphy,
struct net_device *ndev,
enum nl80211_iftype type, u32 *flags,
struct vif_params *params)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
struct wireless_dev *wdev = wil->wdev;
switch (type) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_GO:
break;
case NL80211_IFTYPE_MONITOR:
if (flags)
wil->monitor_flags = *flags;
else
wil->monitor_flags = 0;
break;
default:
return -EOPNOTSUPP;
}
wdev->iftype = type;
return 0;
}
static int wil_cfg80211_scan(struct wiphy *wiphy,
struct cfg80211_scan_request *request)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
struct wireless_dev *wdev = wil->wdev;
struct {
struct wmi_start_scan_cmd cmd;
u16 chnl[4];
} __packed cmd;
uint i, n;
if (wil->scan_request) {
wil_err(wil, "Already scanning\n");
return -EAGAIN;
}
/* check we are client side */
switch (wdev->iftype) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
break;
default:
return -EOPNOTSUPP;
}
/* FW don't support scan after connection attempt */
if (test_bit(wil_status_dontscan, &wil->status)) {
wil_err(wil, "Scan after connect attempt not supported\n");
return -EBUSY;
}
wil->scan_request = request;
memset(&cmd, 0, sizeof(cmd));
cmd.cmd.num_channels = 0;
n = min(request->n_channels, 4U);
for (i = 0; i < n; i++) {
int ch = request->channels[i]->hw_value;
if (ch == 0) {
wil_err(wil,
"Scan requested for unknown frequency %dMhz\n",
request->channels[i]->center_freq);
continue;
}
/* 0-based channel indexes */
cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1;
wil_dbg(wil, "Scan for ch %d : %d MHz\n", ch,
request->channels[i]->center_freq);
}
return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
}
static int wil_cfg80211_connect(struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_connect_params *sme)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
struct cfg80211_bss *bss;
struct wmi_connect_cmd conn;
const u8 *ssid_eid;
const u8 *rsn_eid;
int ch;
int rc = 0;
bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
sme->ssid, sme->ssid_len,
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
if (!bss) {
wil_err(wil, "Unable to find BSS\n");
return -ENOENT;
}
ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
if (!ssid_eid) {
wil_err(wil, "No SSID\n");
rc = -ENOENT;
goto out;
}
rsn_eid = sme->ie ?
cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) :
NULL;
if (rsn_eid) {
if (sme->ie_len > WMI_MAX_IE_LEN) {
rc = -ERANGE;
wil_err(wil, "IE too large (%td bytes)\n",
sme->ie_len);
goto out;
}
/*
* For secure assoc, send:
* (1) WMI_DELETE_CIPHER_KEY_CMD
* (2) WMI_SET_APPIE_CMD
*/
rc = wmi_del_cipher_key(wil, 0, bss->bssid);
if (rc) {
wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD failed\n");
goto out;
}
/* WMI_SET_APPIE_CMD */
rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie);
if (rc) {
wil_err(wil, "WMI_SET_APPIE_CMD failed\n");
goto out;
}
}
/* WMI_CONNECT_CMD */
memset(&conn, 0, sizeof(conn));
switch (bss->capability & 0x03) {
case WLAN_CAPABILITY_DMG_TYPE_AP:
conn.network_type = WMI_NETTYPE_INFRA;
break;
case WLAN_CAPABILITY_DMG_TYPE_PBSS:
conn.network_type = WMI_NETTYPE_P2P;
break;
default:
wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n",
bss->capability);
goto out;
}
if (rsn_eid) {
conn.dot11_auth_mode = WMI_AUTH11_SHARED;
conn.auth_mode = WMI_AUTH_WPA2_PSK;
conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP;
conn.pairwise_crypto_len = 16;
} else {
conn.dot11_auth_mode = WMI_AUTH11_OPEN;
conn.auth_mode = WMI_AUTH_NONE;
}
conn.ssid_len = min_t(u8, ssid_eid[1], 32);
memcpy(conn.ssid, ssid_eid+2, conn.ssid_len);
ch = bss->channel->hw_value;
if (ch == 0) {
wil_err(wil, "BSS at unknown frequency %dMhz\n",
bss->channel->center_freq);
rc = -EOPNOTSUPP;
goto out;
}
conn.channel = ch - 1;
memcpy(conn.bssid, bss->bssid, 6);
memcpy(conn.dst_mac, bss->bssid, 6);
/*
* FW don't support scan after connection attempt
*/
set_bit(wil_status_dontscan, &wil->status);
rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn));
if (rc == 0) {
/* Connect can take lots of time */
mod_timer(&wil->connect_timer,
jiffies + msecs_to_jiffies(2000));
}
out:
cfg80211_put_bss(bss);
return rc;
}
static int wil_cfg80211_disconnect(struct wiphy *wiphy,
struct net_device *ndev,
u16 reason_code)
{
int rc;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
rc = wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0);
return rc;
}
static int wil_cfg80211_set_channel(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
struct wireless_dev *wdev = wil->wdev;
wdev->preset_chandef = *chandef;
return 0;
}
static int wil_cfg80211_add_key(struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index, bool pairwise,
const u8 *mac_addr,
struct key_params *params)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
/* group key is not used */
if (!pairwise)
return 0;
return wmi_add_cipher_key(wil, key_index, mac_addr,
params->key_len, params->key);
}
static int wil_cfg80211_del_key(struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index, bool pairwise,
const u8 *mac_addr)
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
/* group key is not used */
if (!pairwise)
return 0;
return wmi_del_cipher_key(wil, key_index, mac_addr);
}
/* Need to be present or wiphy_new() will WARN */
static int wil_cfg80211_set_default_key(struct wiphy *wiphy,
struct net_device *ndev,
u8 key_index, bool unicast,
bool multicast)
{
return 0;
}
static int wil_cfg80211_start_ap(struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_ap_settings *info)
{
int rc = 0;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
struct wireless_dev *wdev = ndev->ieee80211_ptr;
struct ieee80211_channel *channel = info->chandef.chan;
struct cfg80211_beacon_data *bcon = &info->beacon;
u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
if (!channel) {
wil_err(wil, "AP: No channel???\n");
return -EINVAL;
}
wil_dbg(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value,
channel->center_freq, info->privacy ? "secure" : "open");
print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET,
info->ssid, info->ssid_len);
rc = wil_reset(wil);
if (rc)
return rc;
rc = wmi_set_ssid(wil, info->ssid_len, info->ssid);
if (rc)
return rc;
rc = wmi_set_channel(wil, channel->hw_value);
if (rc)
return rc;
/* MAC address - pre-requisite for other commands */
wmi_set_mac_address(wil, ndev->dev_addr);
/* IE's */
/* bcon 'head IE's are not relevant for 60g band */
wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len,
bcon->beacon_ies);
wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, bcon->proberesp_ies_len,
bcon->proberesp_ies);
wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len,
bcon->assocresp_ies);
wil->secure_pcp = info->privacy;
rc = wmi_set_bcon(wil, info->beacon_interval, wmi_nettype);
if (rc)
return rc;
/* Rx VRING. After MAC and beacon */
rc = wil_rx_init(wil);
netif_carrier_on(ndev);
return rc;
}
static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
struct net_device *ndev)
{
int rc = 0;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
struct wireless_dev *wdev = ndev->ieee80211_ptr;
u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
/* To stop beaconing, set BI to 0 */
rc = wmi_set_bcon(wil, 0, wmi_nettype);
return rc;
}
static struct cfg80211_ops wil_cfg80211_ops = {
.scan = wil_cfg80211_scan,
.connect = wil_cfg80211_connect,
.disconnect = wil_cfg80211_disconnect,
.change_virtual_intf = wil_cfg80211_change_iface,
.get_station = wil_cfg80211_get_station,
.set_monitor_channel = wil_cfg80211_set_channel,
.add_key = wil_cfg80211_add_key,
.del_key = wil_cfg80211_del_key,
.set_default_key = wil_cfg80211_set_default_key,
/* AP mode */
.start_ap = wil_cfg80211_start_ap,
.stop_ap = wil_cfg80211_stop_ap,
};
static void wil_wiphy_init(struct wiphy *wiphy)
{
/* TODO: set real value */
wiphy->max_scan_ssids = 10;
wiphy->max_num_pmkids = 0 /* TODO: */;
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_MONITOR);
/* TODO: enable P2P when integrated with supplicant:
* BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO)
*/
wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME |
WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
dev_warn(wiphy_dev(wiphy), "%s : flags = 0x%08x\n",
__func__, wiphy->flags);
wiphy->probe_resp_offload =
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
wiphy->bands[IEEE80211_BAND_60GHZ] = &wil_band_60ghz;
/* TODO: figure this out */
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
wiphy->cipher_suites = wil_cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites);
wiphy->mgmt_stypes = wil_mgmt_stypes;
}
struct wireless_dev *wil_cfg80211_init(struct device *dev)
{
int rc = 0;
struct wireless_dev *wdev;
wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
if (!wdev)
return ERR_PTR(-ENOMEM);
wdev->wiphy = wiphy_new(&wil_cfg80211_ops,
sizeof(struct wil6210_priv));
if (!wdev->wiphy) {
rc = -ENOMEM;
goto out;
}
set_wiphy_dev(wdev->wiphy, dev);
wil_wiphy_init(wdev->wiphy);
rc = wiphy_register(wdev->wiphy);
if (rc < 0)
goto out_failed_reg;
return wdev;
out_failed_reg:
wiphy_free(wdev->wiphy);
out:
kfree(wdev);
return ERR_PTR(rc);
}
void wil_wdev_free(struct wil6210_priv *wil)
{
struct wireless_dev *wdev = wil_to_wdev(wil);
if (!wdev)
return;
wiphy_unregister(wdev->wiphy);
wiphy_free(wdev->wiphy);
kfree(wdev);
}
#ifndef WIL_DBG_HEXDUMP_H_
#define WIL_DBG_HEXDUMP_H_
#if defined(CONFIG_DYNAMIC_DEBUG)
#define wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize, \
groupsize, buf, len, ascii) \
do { \
DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, \
__builtin_constant_p(prefix_str) ? prefix_str : "hexdump");\
if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \
print_hex_dump(KERN_DEBUG, prefix_str, \
prefix_type, rowsize, groupsize, \
buf, len, ascii); \
} while (0)
#define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize, \
groupsize, buf, len, ascii) \
wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize, \
groupsize, buf, len, ascii)
#define print_hex_dump_bytes(prefix_str, prefix_type, buf, len) \
wil_dynamic_hex_dump(prefix_str, prefix_type, 16, 1, buf, len, true)
#else /* defined(CONFIG_DYNAMIC_DEBUG) */
#define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize, \
groupsize, buf, len, ascii) \
print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, rowsize, \
groupsize, buf, len, ascii)
#endif /* defined(CONFIG_DYNAMIC_DEBUG) */
#endif /* WIL_DBG_HEXDUMP_H_ */
/*
* Copyright (c) 2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/pci.h>
#include <linux/rtnetlink.h>
#include "wil6210.h"
#include "txrx.h"
/* Nasty hack. Better have per device instances */
static u32 mem_addr;
static u32 dbg_txdesc_index;
static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil,
const char *name, struct vring *vring)
{
void __iomem *x = wmi_addr(wil, vring->hwtail);
seq_printf(s, "VRING %s = {\n", name);
seq_printf(s, " pa = 0x%016llx\n", (unsigned long long)vring->pa);
seq_printf(s, " va = 0x%p\n", vring->va);
seq_printf(s, " size = %d\n", vring->size);
seq_printf(s, " swtail = %d\n", vring->swtail);
seq_printf(s, " swhead = %d\n", vring->swhead);
seq_printf(s, " hwtail = [0x%08x] -> ", vring->hwtail);
if (x)
seq_printf(s, "0x%08x\n", ioread32(x));
else
seq_printf(s, "???\n");
if (vring->va && (vring->size < 1025)) {
uint i;
for (i = 0; i < vring->size; i++) {
volatile struct vring_tx_desc *d = &vring->va[i].tx;
if ((i % 64) == 0 && (i != 0))
seq_printf(s, "\n");
seq_printf(s, "%s", (d->dma.status & BIT(0)) ?
"S" : (vring->ctx[i] ? "H" : "h"));
}
seq_printf(s, "\n");
}
seq_printf(s, "}\n");
}
static int wil_vring_debugfs_show(struct seq_file *s, void *data)
{
uint i;
struct wil6210_priv *wil = s->private;
wil_print_vring(s, wil, "rx", &wil->vring_rx);
for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
struct vring *vring = &(wil->vring_tx[i]);
if (vring->va) {
char name[10];
snprintf(name, sizeof(name), "tx_%2d", i);
wil_print_vring(s, wil, name, vring);
}
}
return 0;
}
static int wil_vring_seq_open(struct inode *inode, struct file *file)
{
return single_open(file, wil_vring_debugfs_show, inode->i_private);
}
static const struct file_operations fops_vring = {
.open = wil_vring_seq_open,
.release = single_release,
.read = seq_read,
.llseek = seq_lseek,
};
static void wil_print_ring(struct seq_file *s, const char *prefix,
void __iomem *off)
{
struct wil6210_priv *wil = s->private;
struct wil6210_mbox_ring r;
int rsize;
uint i;
wil_memcpy_fromio_32(&r, off, sizeof(r));
wil_mbox_ring_le2cpus(&r);
/*
* we just read memory block from NIC. This memory may be
* garbage. Check validity before using it.
*/
rsize = r.size / sizeof(struct wil6210_mbox_ring_desc);
seq_printf(s, "ring %s = {\n", prefix);
seq_printf(s, " base = 0x%08x\n", r.base);
seq_printf(s, " size = 0x%04x bytes -> %d entries\n", r.size, rsize);
seq_printf(s, " tail = 0x%08x\n", r.tail);
seq_printf(s, " head = 0x%08x\n", r.head);
seq_printf(s, " entry size = %d\n", r.entry_size);
if (r.size % sizeof(struct wil6210_mbox_ring_desc)) {
seq_printf(s, " ??? size is not multiple of %zd, garbage?\n",
sizeof(struct wil6210_mbox_ring_desc));
goto out;
}
if (!wmi_addr(wil, r.base) ||
!wmi_addr(wil, r.tail) ||
!wmi_addr(wil, r.head)) {
seq_printf(s, " ??? pointers are garbage?\n");
goto out;
}
for (i = 0; i < rsize; i++) {
struct wil6210_mbox_ring_desc d;
struct wil6210_mbox_hdr hdr;
size_t delta = i * sizeof(d);
void __iomem *x = wil->csr + HOSTADDR(r.base) + delta;
wil_memcpy_fromio_32(&d, x, sizeof(d));
seq_printf(s, " [%2x] %s %s%s 0x%08x", i,
d.sync ? "F" : "E",
(r.tail - r.base == delta) ? "t" : " ",
(r.head - r.base == delta) ? "h" : " ",
le32_to_cpu(d.addr));
if (0 == wmi_read_hdr(wil, d.addr, &hdr)) {
u16 len = le16_to_cpu(hdr.len);
seq_printf(s, " -> %04x %04x %04x %02x\n",
le16_to_cpu(hdr.seq), len,
le16_to_cpu(hdr.type), hdr.flags);
if (len <= MAX_MBOXITEM_SIZE) {
int n = 0;
unsigned char printbuf[16 * 3 + 2];
unsigned char databuf[MAX_MBOXITEM_SIZE];
void __iomem *src = wmi_buffer(wil, d.addr) +
sizeof(struct wil6210_mbox_hdr);
/*
* No need to check @src for validity -
* we already validated @d.addr while
* reading header
*/
wil_memcpy_fromio_32(databuf, src, len);
while (n < len) {
int l = min(len - n, 16);
hex_dump_to_buffer(databuf + n, l,
16, 1, printbuf,
sizeof(printbuf),
false);
seq_printf(s, " : %s\n", printbuf);
n += l;
}
}
} else {
seq_printf(s, "\n");
}
}
out:
seq_printf(s, "}\n");
}
static int wil_mbox_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
wil_print_ring(s, "tx", wil->csr + HOST_MBOX +
offsetof(struct wil6210_mbox_ctl, tx));
wil_print_ring(s, "rx", wil->csr + HOST_MBOX +
offsetof(struct wil6210_mbox_ctl, rx));
return 0;
}
static int wil_mbox_seq_open(struct inode *inode, struct file *file)
{
return single_open(file, wil_mbox_debugfs_show, inode->i_private);
}
static const struct file_operations fops_mbox = {
.open = wil_mbox_seq_open,
.release = single_release,
.read = seq_read,
.llseek = seq_lseek,
};
static int wil_debugfs_iomem_x32_set(void *data, u64 val)
{
iowrite32(val, (void __iomem *)data);
wmb(); /* make sure write propagated to HW */
return 0;
}
static int wil_debugfs_iomem_x32_get(void *data, u64 *val)
{
*val = ioread32((void __iomem *)data);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get,
wil_debugfs_iomem_x32_set, "0x%08llx\n");
static struct dentry *wil_debugfs_create_iomem_x32(const char *name,
mode_t mode,
struct dentry *parent,
void __iomem *value)
{
return debugfs_create_file(name, mode, parent, (void * __force)value,
&fops_iomem_x32);
}
static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil,
const char *name,
struct dentry *parent, u32 off)
{
struct dentry *d = debugfs_create_dir(name, parent);
if (IS_ERR_OR_NULL(d))
return -ENODEV;
wil_debugfs_create_iomem_x32("ICC", S_IRUGO | S_IWUSR, d,
wil->csr + off);
wil_debugfs_create_iomem_x32("ICR", S_IRUGO | S_IWUSR, d,
wil->csr + off + 4);
wil_debugfs_create_iomem_x32("ICM", S_IRUGO | S_IWUSR, d,
wil->csr + off + 8);
wil_debugfs_create_iomem_x32("ICS", S_IWUSR, d,
wil->csr + off + 12);
wil_debugfs_create_iomem_x32("IMV", S_IRUGO | S_IWUSR, d,
wil->csr + off + 16);
wil_debugfs_create_iomem_x32("IMS", S_IWUSR, d,
wil->csr + off + 20);
wil_debugfs_create_iomem_x32("IMC", S_IWUSR, d,
wil->csr + off + 24);
return 0;
}
static int wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil,
struct dentry *parent)
{
struct dentry *d = debugfs_create_dir("PSEUDO_ISR", parent);
if (IS_ERR_OR_NULL(d))
return -ENODEV;
wil_debugfs_create_iomem_x32("CAUSE", S_IRUGO, d, wil->csr +
HOSTADDR(RGF_DMA_PSEUDO_CAUSE));
wil_debugfs_create_iomem_x32("MASK_SW", S_IRUGO, d, wil->csr +
HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW));
wil_debugfs_create_iomem_x32("MASK_FW", S_IRUGO, d, wil->csr +
HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW));
return 0;
}
static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil,
struct dentry *parent)
{
struct dentry *d = debugfs_create_dir("ITR_CNT", parent);
if (IS_ERR_OR_NULL(d))
return -ENODEV;
wil_debugfs_create_iomem_x32("TRSH", S_IRUGO, d, wil->csr +
HOSTADDR(RGF_DMA_ITR_CNT_TRSH));
wil_debugfs_create_iomem_x32("DATA", S_IRUGO, d, wil->csr +
HOSTADDR(RGF_DMA_ITR_CNT_DATA));
wil_debugfs_create_iomem_x32("CTL", S_IRUGO, d, wil->csr +
HOSTADDR(RGF_DMA_ITR_CNT_CRL));
return 0;
}
static int wil_memread_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
void __iomem *a = wmi_buffer(wil, cpu_to_le32(mem_addr));
if (a)
seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, ioread32(a));
else
seq_printf(s, "[0x%08x] = INVALID\n", mem_addr);
return 0;
}
static int wil_memread_seq_open(struct inode *inode, struct file *file)
{
return single_open(file, wil_memread_debugfs_show, inode->i_private);
}
static const struct file_operations fops_memread = {
.open = wil_memread_seq_open,
.release = single_release,
.read = seq_read,
.llseek = seq_lseek,
};
static int wil_default_open(struct inode *inode, struct file *file)
{
if (inode->i_private)
file->private_data = inode->i_private;
return 0;
}
static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
enum { max_count = 4096 };
struct debugfs_blob_wrapper *blob = file->private_data;
loff_t pos = *ppos;
size_t available = blob->size;
void *buf;
size_t ret;
if (pos < 0)
return -EINVAL;
if (pos >= available || !count)
return 0;
if (count > available - pos)
count = available - pos;
if (count > max_count)
count = max_count;
buf = kmalloc(count, GFP_KERNEL);
if (!buf)
return -ENOMEM;
wil_memcpy_fromio_32(buf, (const volatile void __iomem *)blob->data +
pos, count);
ret = copy_to_user(user_buf, buf, count);
kfree(buf);
if (ret == count)
return -EFAULT;
count -= ret;
*ppos = pos + count;
return count;
}
static const struct file_operations fops_ioblob = {
.read = wil_read_file_ioblob,
.open = wil_default_open,
.llseek = default_llseek,
};
static
struct dentry *wil_debugfs_create_ioblob(const char *name,
mode_t mode,
struct dentry *parent,
struct debugfs_blob_wrapper *blob)
{
return debugfs_create_file(name, mode, parent, blob, &fops_ioblob);
}
/*---reset---*/
static ssize_t wil_write_file_reset(struct file *file, const char __user *buf,
size_t len, loff_t *ppos)
{
struct wil6210_priv *wil = file->private_data;
struct net_device *ndev = wil_to_ndev(wil);
/**
* BUG:
* this code does NOT sync device state with the rest of system
* use with care, debug only!!!
*/
rtnl_lock();
dev_close(ndev);
ndev->flags &= ~IFF_UP;
rtnl_unlock();
wil_reset(wil);
return len;
}
static const struct file_operations fops_reset = {
.write = wil_write_file_reset,
.open = wil_default_open,
};
/*---------Tx descriptor------------*/
static int wil_txdesc_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
struct vring *vring = &(wil->vring_tx[0]);
if (!vring->va) {
seq_printf(s, "No Tx VRING\n");
return 0;
}
if (dbg_txdesc_index < vring->size) {
volatile struct vring_tx_desc *d =
&(vring->va[dbg_txdesc_index].tx);
volatile u32 *u = (volatile u32 *)d;
struct sk_buff *skb = vring->ctx[dbg_txdesc_index];
seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index);
seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n",
u[0], u[1], u[2], u[3]);
seq_printf(s, " DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n",
u[4], u[5], u[6], u[7]);
seq_printf(s, " SKB = %p\n", skb);
if (skb) {
unsigned char printbuf[16 * 3 + 2];
int i = 0;
int len = skb_headlen(skb);
void *p = skb->data;
seq_printf(s, " len = %d\n", len);
while (i < len) {
int l = min(len - i, 16);
hex_dump_to_buffer(p + i, l, 16, 1, printbuf,
sizeof(printbuf), false);
seq_printf(s, " : %s\n", printbuf);
i += l;
}
}
seq_printf(s, "}\n");
} else {
seq_printf(s, "TxDesc index (%d) >= size (%d)\n",
dbg_txdesc_index, vring->size);
}
return 0;
}
static int wil_txdesc_seq_open(struct inode *inode, struct file *file)
{
return single_open(file, wil_txdesc_debugfs_show, inode->i_private);
}
static const struct file_operations fops_txdesc = {
.open = wil_txdesc_seq_open,
.release = single_release,
.read = seq_read,
.llseek = seq_lseek,
};
/*---------beamforming------------*/
static int wil_bf_debugfs_show(struct seq_file *s, void *data)
{
struct wil6210_priv *wil = s->private;
seq_printf(s,
"TSF : 0x%016llx\n"
"TxMCS : %d\n"
"Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n",
wil->stats.tsf, wil->stats.bf_mcs,
wil->stats.my_rx_sector, wil->stats.my_tx_sector,
wil->stats.peer_rx_sector, wil->stats.peer_tx_sector);
return 0;
}
static int wil_bf_seq_open(struct inode *inode, struct file *file)
{
return single_open(file, wil_bf_debugfs_show, inode->i_private);
}
static const struct file_operations fops_bf = {
.open = wil_bf_seq_open,
.release = single_release,
.read = seq_read,
.llseek = seq_lseek,
};
/*---------SSID------------*/
static ssize_t wil_read_file_ssid(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct wil6210_priv *wil = file->private_data;
struct wireless_dev *wdev = wil_to_wdev(wil);
return simple_read_from_buffer(user_buf, count, ppos,
wdev->ssid, wdev->ssid_len);
}
static ssize_t wil_write_file_ssid(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct wil6210_priv *wil = file->private_data;
struct wireless_dev *wdev = wil_to_wdev(wil);
struct net_device *ndev = wil_to_ndev(wil);
if (*ppos != 0) {
wil_err(wil, "Unable to set SSID substring from [%d]\n",
(int)*ppos);
return -EINVAL;
}
if (count > sizeof(wdev->ssid)) {
wil_err(wil, "SSID too long, len = %d\n", (int)count);
return -EINVAL;
}
if (netif_running(ndev)) {
wil_err(wil, "Unable to change SSID on running interface\n");
return -EINVAL;
}
wdev->ssid_len = count;
return simple_write_to_buffer(wdev->ssid, wdev->ssid_len, ppos,
buf, count);
}
static const struct file_operations fops_ssid = {
.read = wil_read_file_ssid,
.write = wil_write_file_ssid,
.open = wil_default_open,
};
/*----------------*/
int wil6210_debugfs_init(struct wil6210_priv *wil)
{
struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME,
wil_to_wiphy(wil)->debugfsdir);
if (IS_ERR_OR_NULL(dbg))
return -ENODEV;
debugfs_create_file("mbox", S_IRUGO, dbg, wil, &fops_mbox);
debugfs_create_file("vrings", S_IRUGO, dbg, wil, &fops_vring);
debugfs_create_file("txdesc", S_IRUGO, dbg, wil, &fops_txdesc);
debugfs_create_u32("txdesc_index", S_IRUGO | S_IWUSR, dbg,
&dbg_txdesc_index);
debugfs_create_file("bf", S_IRUGO, dbg, wil, &fops_bf);
debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid);
debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg,
&wil->secure_pcp);
wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg,
HOSTADDR(RGF_USER_USER_ICR));
wil6210_debugfs_create_ISR(wil, "DMA_EP_TX_ICR", dbg,
HOSTADDR(RGF_DMA_EP_TX_ICR));
wil6210_debugfs_create_ISR(wil, "DMA_EP_RX_ICR", dbg,
HOSTADDR(RGF_DMA_EP_RX_ICR));
wil6210_debugfs_create_ISR(wil, "DMA_EP_MISC_ICR", dbg,
HOSTADDR(RGF_DMA_EP_MISC_ICR));
wil6210_debugfs_create_pseudo_ISR(wil, dbg);
wil6210_debugfs_create_ITR_CNT(wil, dbg);
debugfs_create_u32("mem_addr", S_IRUGO | S_IWUSR, dbg, &mem_addr);
debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread);
debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset);
wil->rgf_blob.data = (void * __force)wil->csr + 0;
wil->rgf_blob.size = 0xa000;
wil_debugfs_create_ioblob("blob_rgf", S_IRUGO, dbg, &wil->rgf_blob);
wil->fw_code_blob.data = (void * __force)wil->csr + 0x40000;
wil->fw_code_blob.size = 0x40000;
wil_debugfs_create_ioblob("blob_fw_code", S_IRUGO, dbg,
&wil->fw_code_blob);
wil->fw_data_blob.data = (void * __force)wil->csr + 0x80000;
wil->fw_data_blob.size = 0x8000;
wil_debugfs_create_ioblob("blob_fw_data", S_IRUGO, dbg,
&wil->fw_data_blob);
wil->fw_peri_blob.data = (void * __force)wil->csr + 0x88000;
wil->fw_peri_blob.size = 0x18000;
wil_debugfs_create_ioblob("blob_fw_peri", S_IRUGO, dbg,
&wil->fw_peri_blob);
wil->uc_code_blob.data = (void * __force)wil->csr + 0xa0000;
wil->uc_code_blob.size = 0x10000;
wil_debugfs_create_ioblob("blob_uc_code", S_IRUGO, dbg,
&wil->uc_code_blob);
wil->uc_data_blob.data = (void * __force)wil->csr + 0xb0000;
wil->uc_data_blob.size = 0x4000;
wil_debugfs_create_ioblob("blob_uc_data", S_IRUGO, dbg,
&wil->uc_data_blob);
return 0;
}
void wil6210_debugfs_remove(struct wil6210_priv *wil)
{
debugfs_remove_recursive(wil->debug);
wil->debug = NULL;
}
/*
* Copyright (c) 2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/interrupt.h>
#include "wil6210.h"
/**
* Theory of operation:
*
* There is ISR pseudo-cause register,
* dma_rgf->DMA_RGF.PSEUDO_CAUSE.PSEUDO_CAUSE
* Its bits represents OR'ed bits from 3 real ISR registers:
* TX, RX, and MISC.
*
* Registers may be configured to either "write 1 to clear" or
* "clear on read" mode
*
* When handling interrupt, one have to mask/unmask interrupts for the
* real ISR registers, or hardware may malfunction.
*
*/
#define WIL6210_IRQ_DISABLE (0xFFFFFFFFUL)
#define WIL6210_IMC_RX BIT_DMA_EP_RX_ICR_RX_DONE
#define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \
BIT_DMA_EP_TX_ICR_TX_DONE_N(0))
#define WIL6210_IMC_MISC (ISR_MISC_FW_READY | ISR_MISC_MBOX_EVT)
#define WIL6210_IRQ_PSEUDO_MASK (u32)(~(BIT_DMA_PSEUDO_CAUSE_RX | \
BIT_DMA_PSEUDO_CAUSE_TX | \
BIT_DMA_PSEUDO_CAUSE_MISC))
#if defined(CONFIG_WIL6210_ISR_COR)
/* configure to Clear-On-Read mode */
#define WIL_ICR_ICC_VALUE (0xFFFFFFFFUL)
static inline void wil_icr_clear(u32 x, void __iomem *addr)
{
}
#else /* defined(CONFIG_WIL6210_ISR_COR) */
/* configure to Write-1-to-Clear mode */
#define WIL_ICR_ICC_VALUE (0UL)
static inline void wil_icr_clear(u32 x, void __iomem *addr)
{
iowrite32(x, addr);
}
#endif /* defined(CONFIG_WIL6210_ISR_COR) */
static inline u32 wil_ioread32_and_clear(void __iomem *addr)
{
u32 x = ioread32(addr);
wil_icr_clear(x, addr);
return x;
}
static void wil6210_mask_irq_tx(struct wil6210_priv *wil)
{
iowrite32(WIL6210_IRQ_DISABLE, wil->csr +
HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, IMS));
}
static void wil6210_mask_irq_rx(struct wil6210_priv *wil)
{
iowrite32(WIL6210_IRQ_DISABLE, wil->csr +
HOSTADDR(RGF_DMA_EP_RX_ICR) +
offsetof(struct RGF_ICR, IMS));
}
static void wil6210_mask_irq_misc(struct wil6210_priv *wil)
{
iowrite32(WIL6210_IRQ_DISABLE, wil->csr +
HOSTADDR(RGF_DMA_EP_MISC_ICR) +
offsetof(struct RGF_ICR, IMS));
}
static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil)
{
wil_dbg_IRQ(wil, "%s()\n", __func__);
iowrite32(WIL6210_IRQ_DISABLE, wil->csr +
HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW));
clear_bit(wil_status_irqen, &wil->status);
}
static void wil6210_unmask_irq_tx(struct wil6210_priv *wil)
{
iowrite32(WIL6210_IMC_TX, wil->csr +
HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, IMC));
}
static void wil6210_unmask_irq_rx(struct wil6210_priv *wil)
{
iowrite32(WIL6210_IMC_RX, wil->csr +
HOSTADDR(RGF_DMA_EP_RX_ICR) +
offsetof(struct RGF_ICR, IMC));
}
static void wil6210_unmask_irq_misc(struct wil6210_priv *wil)
{
iowrite32(WIL6210_IMC_MISC, wil->csr +
HOSTADDR(RGF_DMA_EP_MISC_ICR) +
offsetof(struct RGF_ICR, IMC));
}
static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil)
{
wil_dbg_IRQ(wil, "%s()\n", __func__);
set_bit(wil_status_irqen, &wil->status);
iowrite32(WIL6210_IRQ_PSEUDO_MASK, wil->csr +
HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW));
}
void wil6210_disable_irq(struct wil6210_priv *wil)
{
wil_dbg_IRQ(wil, "%s()\n", __func__);
wil6210_mask_irq_tx(wil);
wil6210_mask_irq_rx(wil);
wil6210_mask_irq_misc(wil);
wil6210_mask_irq_pseudo(wil);
}
void wil6210_enable_irq(struct wil6210_priv *wil)
{
wil_dbg_IRQ(wil, "%s()\n", __func__);
iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) +
offsetof(struct RGF_ICR, ICC));
iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, ICC));
iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) +
offsetof(struct RGF_ICR, ICC));
wil6210_unmask_irq_pseudo(wil);
wil6210_unmask_irq_tx(wil);
wil6210_unmask_irq_rx(wil);
wil6210_unmask_irq_misc(wil);
}
static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
{
struct wil6210_priv *wil = cookie;
u32 isr = wil_ioread32_and_clear(wil->csr +
HOSTADDR(RGF_DMA_EP_RX_ICR) +
offsetof(struct RGF_ICR, ICR));
wil_dbg_IRQ(wil, "ISR RX 0x%08x\n", isr);
if (!isr) {
wil_err(wil, "spurious IRQ: RX\n");
return IRQ_NONE;
}
wil6210_mask_irq_rx(wil);
if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) {
wil_dbg_IRQ(wil, "RX done\n");
isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE;
wil_rx_handle(wil);
}
if (isr)
wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr);
wil6210_unmask_irq_rx(wil);
return IRQ_HANDLED;
}
static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
{
struct wil6210_priv *wil = cookie;
u32 isr = wil_ioread32_and_clear(wil->csr +
HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, ICR));
wil_dbg_IRQ(wil, "ISR TX 0x%08x\n", isr);
if (!isr) {
wil_err(wil, "spurious IRQ: TX\n");
return IRQ_NONE;
}
wil6210_mask_irq_tx(wil);
if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) {
uint i;
wil_dbg_IRQ(wil, "TX done\n");
isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE;
for (i = 0; i < 24; i++) {
u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i);
if (isr & mask) {
isr &= ~mask;
wil_dbg_IRQ(wil, "TX done(%i)\n", i);
wil_tx_complete(wil, i);
}
}
}
if (isr)
wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr);
wil6210_unmask_irq_tx(wil);
return IRQ_HANDLED;
}
static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
{
struct wil6210_priv *wil = cookie;
u32 isr = wil_ioread32_and_clear(wil->csr +
HOSTADDR(RGF_DMA_EP_MISC_ICR) +
offsetof(struct RGF_ICR, ICR));
wil_dbg_IRQ(wil, "ISR MISC 0x%08x\n", isr);
if (!isr) {
wil_err(wil, "spurious IRQ: MISC\n");
return IRQ_NONE;
}
wil6210_mask_irq_misc(wil);
if (isr & ISR_MISC_FW_READY) {
wil_dbg_IRQ(wil, "IRQ: FW ready\n");
/**
* Actual FW ready indicated by the
* WMI_FW_READY_EVENTID
*/
isr &= ~ISR_MISC_FW_READY;
}
wil->isr_misc = isr;
if (isr) {
return IRQ_WAKE_THREAD;
} else {
wil6210_unmask_irq_misc(wil);
return IRQ_HANDLED;
}
}
static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
{
struct wil6210_priv *wil = cookie;
u32 isr = wil->isr_misc;
wil_dbg_IRQ(wil, "Thread ISR MISC 0x%08x\n", isr);
if (isr & ISR_MISC_MBOX_EVT) {
wil_dbg_IRQ(wil, "MBOX event\n");
wmi_recv_cmd(wil);
isr &= ~ISR_MISC_MBOX_EVT;
}
if (isr)
wil_err(wil, "un-handled MISC ISR bits 0x%08x\n", isr);
wil->isr_misc = 0;
wil6210_unmask_irq_misc(wil);
return IRQ_HANDLED;
}
/**
* thread IRQ handler
*/
static irqreturn_t wil6210_thread_irq(int irq, void *cookie)
{
struct wil6210_priv *wil = cookie;
wil_dbg_IRQ(wil, "Thread IRQ\n");
/* Discover real IRQ cause */
if (wil->isr_misc)
wil6210_irq_misc_thread(irq, cookie);
wil6210_unmask_irq_pseudo(wil);
return IRQ_HANDLED;
}
/* DEBUG
* There is subtle bug in hardware that causes IRQ to raise when it should be
* masked. It is quite rare and hard to debug.
*
* Catch irq issue if it happens and print all I can.
*/
static int wil6210_debug_irq_mask(struct wil6210_priv *wil, u32 pseudo_cause)
{
if (!test_bit(wil_status_irqen, &wil->status)) {
u32 icm_rx = wil_ioread32_and_clear(wil->csr +
HOSTADDR(RGF_DMA_EP_RX_ICR) +
offsetof(struct RGF_ICR, ICM));
u32 icr_rx = wil_ioread32_and_clear(wil->csr +
HOSTADDR(RGF_DMA_EP_RX_ICR) +
offsetof(struct RGF_ICR, ICR));
u32 imv_rx = ioread32(wil->csr +
HOSTADDR(RGF_DMA_EP_RX_ICR) +
offsetof(struct RGF_ICR, IMV));
u32 icm_tx = wil_ioread32_and_clear(wil->csr +
HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, ICM));
u32 icr_tx = wil_ioread32_and_clear(wil->csr +
HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, ICR));
u32 imv_tx = ioread32(wil->csr +
HOSTADDR(RGF_DMA_EP_TX_ICR) +
offsetof(struct RGF_ICR, IMV));
u32 icm_misc = wil_ioread32_and_clear(wil->csr +
HOSTADDR(RGF_DMA_EP_MISC_ICR) +
offsetof(struct RGF_ICR, ICM));
u32 icr_misc = wil_ioread32_and_clear(wil->csr +
HOSTADDR(RGF_DMA_EP_MISC_ICR) +
offsetof(struct RGF_ICR, ICR));
u32 imv_misc = ioread32(wil->csr +
HOSTADDR(RGF_DMA_EP_MISC_ICR) +
offsetof(struct RGF_ICR, IMV));
wil_err(wil, "IRQ when it should be masked: pseudo 0x%08x\n"
"Rx icm:icr:imv 0x%08x 0x%08x 0x%08x\n"
"Tx icm:icr:imv 0x%08x 0x%08x 0x%08x\n"
"Misc icm:icr:imv 0x%08x 0x%08x 0x%08x\n",
pseudo_cause,
icm_rx, icr_rx, imv_rx,
icm_tx, icr_tx, imv_tx,
icm_misc, icr_misc, imv_misc);
return -EINVAL;
}
return 0;
}
static irqreturn_t wil6210_hardirq(int irq, void *cookie)
{
irqreturn_t rc = IRQ_HANDLED;
struct wil6210_priv *wil = cookie;
u32 pseudo_cause = ioread32(wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE));
/**
* pseudo_cause is Clear-On-Read, no need to ACK
*/
if ((pseudo_cause == 0) || ((pseudo_cause & 0xff) == 0xff))
return IRQ_NONE;
/* FIXME: IRQ mask debug */
if (wil6210_debug_irq_mask(wil, pseudo_cause))
return IRQ_NONE;
wil6210_mask_irq_pseudo(wil);
/* Discover real IRQ cause
* There are 2 possible phases for every IRQ:
* - hard IRQ handler called right here
* - threaded handler called later
*
* Hard IRQ handler reads and clears ISR.
*
* If threaded handler requested, hard IRQ handler
* returns IRQ_WAKE_THREAD and saves ISR register value
* for the threaded handler use.
*
* voting for wake thread - need at least 1 vote
*/
if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_RX) &&
(wil6210_irq_rx(irq, cookie) == IRQ_WAKE_THREAD))
rc = IRQ_WAKE_THREAD;
if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_TX) &&
(wil6210_irq_tx(irq, cookie) == IRQ_WAKE_THREAD))
rc = IRQ_WAKE_THREAD;
if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_MISC) &&
(wil6210_irq_misc(irq, cookie) == IRQ_WAKE_THREAD))
rc = IRQ_WAKE_THREAD;
/* if thread is requested, it will unmask IRQ */
if (rc != IRQ_WAKE_THREAD)
wil6210_unmask_irq_pseudo(wil);
wil_dbg_IRQ(wil, "Hard IRQ 0x%08x\n", pseudo_cause);
return rc;
}
static int wil6210_request_3msi(struct wil6210_priv *wil, int irq)
{
int rc;
/*
* IRQ's are in the following order:
* - Tx
* - Rx
* - Misc
*/
rc = request_irq(irq, wil6210_irq_tx, IRQF_SHARED,
WIL_NAME"_tx", wil);
if (rc)
return rc;
rc = request_irq(irq + 1, wil6210_irq_rx, IRQF_SHARED,
WIL_NAME"_rx", wil);
if (rc)
goto free0;
rc = request_threaded_irq(irq + 2, wil6210_irq_misc,
wil6210_irq_misc_thread,
IRQF_SHARED, WIL_NAME"_misc", wil);
if (rc)
goto free1;
return 0;
/* error branch */
free1:
free_irq(irq + 1, wil);
free0:
free_irq(irq, wil);
return rc;
}
int wil6210_init_irq(struct wil6210_priv *wil, int irq)
{
int rc;
if (wil->n_msi == 3)
rc = wil6210_request_3msi(wil, irq);
else
rc = request_threaded_irq(irq, wil6210_hardirq,
wil6210_thread_irq,
wil->n_msi ? 0 : IRQF_SHARED,
WIL_NAME, wil);
if (rc)
return rc;
wil6210_enable_irq(wil);
return 0;
}
void wil6210_fini_irq(struct wil6210_priv *wil, int irq)
{
wil6210_disable_irq(wil);
free_irq(irq, wil);
if (wil->n_msi == 3) {
free_irq(irq + 1, wil);
free_irq(irq + 2, wil);
}
}
/*
* Copyright (c) 2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/sched.h>
#include <linux/ieee80211.h>
#include <linux/wireless.h>
#include <linux/slab.h>
#include <linux/moduleparam.h>
#include <linux/if_arp.h>
#include "wil6210.h"
/*
* Due to a hardware issue,
* one has to read/write to/from NIC in 32-bit chunks;
* regular memcpy_fromio and siblings will
* not work on 64-bit platform - it uses 64-bit transactions
*
* Force 32-bit transactions to enable NIC on 64-bit platforms
*
* To avoid byte swap on big endian host, __raw_{read|write}l
* should be used - {read|write}l would swap bytes to provide
* little endian on PCI value in host endianness.
*/
void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src,
size_t count)
{
u32 *d = dst;
const volatile u32 __iomem *s = src;
/* size_t is unsigned, if (count%4 != 0) it will wrap */
for (count += 4; count > 4; count -= 4)
*d++ = __raw_readl(s++);
}
void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
size_t count)
{
volatile u32 __iomem *d = dst;
const u32 *s = src;
for (count += 4; count > 4; count -= 4)
__raw_writel(*s++, d++);
}
static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
{
uint i;
struct net_device *ndev = wil_to_ndev(wil);
struct wireless_dev *wdev = wil->wdev;
wil_dbg(wil, "%s()\n", __func__);
wil_link_off(wil);
clear_bit(wil_status_fwconnected, &wil->status);
switch (wdev->sme_state) {
case CFG80211_SME_CONNECTED:
cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE,
NULL, 0, GFP_KERNEL);
break;
case CFG80211_SME_CONNECTING:
cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE,
GFP_KERNEL);
break;
default:
;
}
for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++)
wil_vring_fini_tx(wil, i);
}
static void wil_disconnect_worker(struct work_struct *work)
{
struct wil6210_priv *wil = container_of(work,
struct wil6210_priv, disconnect_worker);
_wil6210_disconnect(wil, NULL);
}
static void wil_connect_timer_fn(ulong x)
{
struct wil6210_priv *wil = (void *)x;
wil_dbg(wil, "Connect timeout\n");
/* reschedule to thread context - disconnect won't
* run from atomic context
*/
schedule_work(&wil->disconnect_worker);
}
int wil_priv_init(struct wil6210_priv *wil)
{
wil_dbg(wil, "%s()\n", __func__);
mutex_init(&wil->mutex);
mutex_init(&wil->wmi_mutex);
init_completion(&wil->wmi_ready);
wil->pending_connect_cid = -1;
setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil);
INIT_WORK(&wil->wmi_connect_worker, wmi_connect_worker);
INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker);
INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
INIT_LIST_HEAD(&wil->pending_wmi_ev);
spin_lock_init(&wil->wmi_ev_lock);
wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi");
if (!wil->wmi_wq)
return -EAGAIN;
wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect");
if (!wil->wmi_wq_conn) {
destroy_workqueue(wil->wmi_wq);
return -EAGAIN;
}
/* make shadow copy of registers that should not change on run time */
wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX,
sizeof(struct wil6210_mbox_ctl));
wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx);
wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
return 0;
}
void wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
{
del_timer_sync(&wil->connect_timer);
_wil6210_disconnect(wil, bssid);
}
void wil_priv_deinit(struct wil6210_priv *wil)
{
cancel_work_sync(&wil->disconnect_worker);
wil6210_disconnect(wil, NULL);
wmi_event_flush(wil);
destroy_workqueue(wil->wmi_wq_conn);
destroy_workqueue(wil->wmi_wq);
}
static void wil_target_reset(struct wil6210_priv *wil)
{
wil_dbg(wil, "Resetting...\n");
/* register write */
#define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a))
/* register set = read, OR, write */
#define S(a, v) iowrite32(ioread32(wil->csr + HOSTADDR(a)) | v, \
wil->csr + HOSTADDR(a))
/* hpal_perst_from_pad_src_n_mask */
S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6));
/* car_perst_rst_src_n_mask */
S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7));
W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */
W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */
msleep(100);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00);
msleep(100);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080);
W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0);
msleep(2000);
W(RGF_USER_USER_CPU_0, BIT(0)); /* user_cpu_man_de_rst */
msleep(2000);
wil_dbg(wil, "Reset completed\n");
#undef W
#undef S
}
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
{
le32_to_cpus(&r->base);
le16_to_cpus(&r->entry_size);
le16_to_cpus(&r->size);
le32_to_cpus(&r->tail);
le32_to_cpus(&r->head);
}
static int wil_wait_for_fw_ready(struct wil6210_priv *wil)
{
ulong to = msecs_to_jiffies(1000);
ulong left = wait_for_completion_timeout(&wil->wmi_ready, to);
if (0 == left) {
wil_err(wil, "Firmware not ready\n");
return -ETIME;
} else {
wil_dbg(wil, "FW ready after %d ms\n",
jiffies_to_msecs(to-left));
}
return 0;
}
/*
* We reset all the structures, and we reset the UMAC.
* After calling this routine, you're expected to reload
* the firmware.
*/
int wil_reset(struct wil6210_priv *wil)
{
int rc;
cancel_work_sync(&wil->disconnect_worker);
wil6210_disconnect(wil, NULL);
wmi_event_flush(wil);
flush_workqueue(wil->wmi_wq);
flush_workqueue(wil->wmi_wq_conn);
wil6210_disable_irq(wil);
wil->status = 0;
/* TODO: put MAC in reset */
wil_target_reset(wil);
/* init after reset */
wil->pending_connect_cid = -1;
INIT_COMPLETION(wil->wmi_ready);
/* make shadow copy of registers that should not change on run time */
wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX,
sizeof(struct wil6210_mbox_ctl));
wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx);
wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
/* TODO: release MAC reset */
wil6210_enable_irq(wil);
/* we just started MAC, wait for FW ready */
rc = wil_wait_for_fw_ready(wil);
return rc;
}
void wil_link_on(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
wil_dbg(wil, "%s()\n", __func__);
netif_carrier_on(ndev);
netif_tx_wake_all_queues(ndev);
}
void wil_link_off(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
wil_dbg(wil, "%s()\n", __func__);
netif_tx_stop_all_queues(ndev);
netif_carrier_off(ndev);
}
static int __wil_up(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
struct wireless_dev *wdev = wil->wdev;
struct ieee80211_channel *channel = wdev->preset_chandef.chan;
int rc;
int bi;
u16 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
rc = wil_reset(wil);
if (rc)
return rc;
/* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */
wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC);
switch (wdev->iftype) {
case NL80211_IFTYPE_STATION:
wil_dbg(wil, "type: STATION\n");
bi = 0;
ndev->type = ARPHRD_ETHER;
break;
case NL80211_IFTYPE_AP:
wil_dbg(wil, "type: AP\n");
bi = 100;
ndev->type = ARPHRD_ETHER;
break;
case NL80211_IFTYPE_P2P_CLIENT:
wil_dbg(wil, "type: P2P_CLIENT\n");
bi = 0;
ndev->type = ARPHRD_ETHER;
break;
case NL80211_IFTYPE_P2P_GO:
wil_dbg(wil, "type: P2P_GO\n");
bi = 100;
ndev->type = ARPHRD_ETHER;
break;
case NL80211_IFTYPE_MONITOR:
wil_dbg(wil, "type: Monitor\n");
bi = 0;
ndev->type = ARPHRD_IEEE80211_RADIOTAP;
/* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */
break;
default:
return -EOPNOTSUPP;
}
/* Apply profile in the following order: */
/* SSID and channel for the AP */
switch (wdev->iftype) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
if (wdev->ssid_len == 0) {
wil_err(wil, "SSID not set\n");
return -EINVAL;
}
wmi_set_ssid(wil, wdev->ssid_len, wdev->ssid);
if (channel)
wmi_set_channel(wil, channel->hw_value);
break;
default:
;
}
/* MAC address - pre-requisite for other commands */
wmi_set_mac_address(wil, ndev->dev_addr);
/* Set up beaconing if required. */
rc = wmi_set_bcon(wil, bi, wmi_nettype);
if (rc)
return rc;
/* Rx VRING. After MAC and beacon */
wil_rx_init(wil);
return 0;
}
int wil_up(struct wil6210_priv *wil)
{
int rc;
mutex_lock(&wil->mutex);
rc = __wil_up(wil);
mutex_unlock(&wil->mutex);
return rc;
}
static int __wil_down(struct wil6210_priv *wil)
{
if (wil->scan_request) {
cfg80211_scan_done(wil->scan_request, true);
wil->scan_request = NULL;
}
wil6210_disconnect(wil, NULL);
wil_rx_fini(wil);
return 0;
}
int wil_down(struct wil6210_priv *wil)
{
int rc;
mutex_lock(&wil->mutex);
rc = __wil_down(wil);
mutex_unlock(&wil->mutex);
return rc;
}
/*
* Copyright (c) 2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/slab.h>
#include "wil6210.h"
static int wil_open(struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
return wil_up(wil);
}
static int wil_stop(struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
return wil_down(wil);
}
/*
* AC to queue mapping
*
* AC_VO -> queue 3
* AC_VI -> queue 2
* AC_BE -> queue 1
* AC_BK -> queue 0
*/
static u16 wil_select_queue(struct net_device *ndev, struct sk_buff *skb)
{
static const u16 wil_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
struct wil6210_priv *wil = ndev_to_wil(ndev);
u16 rc;
skb->priority = cfg80211_classify8021d(skb);
rc = wil_1d_to_queue[skb->priority];
wil_dbg_TXRX(wil, "%s() %d -> %d\n", __func__, (int)skb->priority,
(int)rc);
return rc;
}
static const struct net_device_ops wil_netdev_ops = {
.ndo_open = wil_open,
.ndo_stop = wil_stop,
.ndo_start_xmit = wil_start_xmit,
.ndo_select_queue = wil_select_queue,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
void *wil_if_alloc(struct device *dev, void __iomem *csr)
{
struct net_device *ndev;
struct wireless_dev *wdev;
struct wil6210_priv *wil;
struct ieee80211_channel *ch;
int rc = 0;
wdev = wil_cfg80211_init(dev);
if (IS_ERR(wdev)) {
dev_err(dev, "wil_cfg80211_init failed\n");
return wdev;
}
wil = wdev_to_wil(wdev);
wil->csr = csr;
wil->wdev = wdev;
rc = wil_priv_init(wil);
if (rc) {
dev_err(dev, "wil_priv_init failed\n");
goto out_wdev;
}
wdev->iftype = NL80211_IFTYPE_STATION; /* TODO */
/* default monitor channel */
ch = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels;
cfg80211_chandef_create(&wdev->preset_chandef, ch, NL80211_CHAN_NO_HT);
ndev = alloc_netdev_mqs(0, "wlan%d", ether_setup, WIL6210_TX_QUEUES, 1);
if (!ndev) {
dev_err(dev, "alloc_netdev_mqs failed\n");
rc = -ENOMEM;
goto out_priv;
}
ndev->netdev_ops = &wil_netdev_ops;
ndev->ieee80211_ptr = wdev;
SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
wdev->netdev = ndev;
wil_link_off(wil);
return wil;
out_priv:
wil_priv_deinit(wil);
out_wdev:
wil_wdev_free(wil);
return ERR_PTR(rc);
}
void wil_if_free(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
if (!ndev)
return;
free_netdev(ndev);
wil_priv_deinit(wil);
wil_wdev_free(wil);
}
int wil_if_add(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
int rc;
rc = register_netdev(ndev);
if (rc < 0) {
dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc);
return rc;
}
wil_link_off(wil);
return 0;
}
void wil_if_remove(struct wil6210_priv *wil)
{
struct net_device *ndev = wil_to_ndev(wil);
unregister_netdev(ndev);
}
/*
* Copyright (c) 2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/netdevice.h>
#include <linux/debugfs.h>
#include <linux/pci.h>
#include <linux/moduleparam.h>
#include "wil6210.h"
static int use_msi = 1;
module_param(use_msi, int, S_IRUGO);
MODULE_PARM_DESC(use_msi,
" Use MSI interrupt: "
"0 - don't, 1 - (default) - single, or 3");
/* Bus ops */
static int wil_if_pcie_enable(struct wil6210_priv *wil)
{
struct pci_dev *pdev = wil->pdev;
int rc;
pci_set_master(pdev);
/*
* how many MSI interrupts to request?
*/
switch (use_msi) {
case 3:
case 1:
case 0:
break;
default:
wil_err(wil, "Invalid use_msi=%d, default to 1\n",
use_msi);
use_msi = 1;
}
wil->n_msi = use_msi;
if (wil->n_msi) {
wil_dbg(wil, "Setup %d MSI interrupts\n", use_msi);
rc = pci_enable_msi_block(pdev, wil->n_msi);
if (rc && (wil->n_msi == 3)) {
wil_err(wil, "3 MSI mode failed, try 1 MSI\n");
wil->n_msi = 1;
rc = pci_enable_msi_block(pdev, wil->n_msi);
}
if (rc) {
wil_err(wil, "pci_enable_msi failed, use INTx\n");
wil->n_msi = 0;
}
} else {
wil_dbg(wil, "MSI interrupts disabled, use INTx\n");
}
rc = wil6210_init_irq(wil, pdev->irq);
if (rc)
goto stop_master;
/* need reset here to obtain MAC */
rc = wil_reset(wil);
if (rc)
goto release_irq;
return 0;
release_irq:
wil6210_fini_irq(wil, pdev->irq);
/* safe to call if no MSI */
pci_disable_msi(pdev);
stop_master:
pci_clear_master(pdev);
return rc;
}
static int wil_if_pcie_disable(struct wil6210_priv *wil)
{
struct pci_dev *pdev = wil->pdev;
pci_clear_master(pdev);
/* disable and release IRQ */
wil6210_fini_irq(wil, pdev->irq);
/* safe to call if no MSI */
pci_disable_msi(pdev);
/* TODO: disable HW */
return 0;
}
static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct wil6210_priv *wil;
struct device *dev = &pdev->dev;
void __iomem *csr;
int rc;
/* check HW */
dev_info(&pdev->dev, WIL_NAME " device found [%04x:%04x] (rev %x)\n",
(int)pdev->vendor, (int)pdev->device, (int)pdev->revision);
if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) {
dev_err(&pdev->dev, "Not " WIL_NAME "? "
"BAR0 size is %lu while expecting %lu\n",
(ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE);
return -ENODEV;
}
rc = pci_enable_device(pdev);
if (rc) {
dev_err(&pdev->dev, "pci_enable_device failed\n");
return -ENODEV;
}
/* rollback to err_disable_pdev */
rc = pci_request_region(pdev, 0, WIL_NAME);
if (rc) {
dev_err(&pdev->dev, "pci_request_region failed\n");
goto err_disable_pdev;
}
/* rollback to err_release_reg */
csr = pci_ioremap_bar(pdev, 0);
if (!csr) {
dev_err(&pdev->dev, "pci_ioremap_bar failed\n");
rc = -ENODEV;
goto err_release_reg;
}
/* rollback to err_iounmap */
dev_info(&pdev->dev, "CSR at %pR -> %p\n", &pdev->resource[0], csr);
wil = wil_if_alloc(dev, csr);
if (IS_ERR(wil)) {
rc = (int)PTR_ERR(wil);
dev_err(dev, "wil_if_alloc failed: %d\n", rc);
goto err_iounmap;
}
/* rollback to if_free */
pci_set_drvdata(pdev, wil);
wil->pdev = pdev;
/* FW should raise IRQ when ready */
rc = wil_if_pcie_enable(wil);
if (rc) {
wil_err(wil, "Enable device failed\n");
goto if_free;
}
/* rollback to bus_disable */
rc = wil_if_add(wil);
if (rc) {
wil_err(wil, "wil_if_add failed: %d\n", rc);
goto bus_disable;
}
wil6210_debugfs_init(wil);
/* check FW is alive */
wmi_echo(wil);
return 0;
bus_disable:
wil_if_pcie_disable(wil);
if_free:
wil_if_free(wil);
err_iounmap:
pci_iounmap(pdev, csr);
err_release_reg:
pci_release_region(pdev, 0);
err_disable_pdev:
pci_disable_device(pdev);
return rc;
}
static void wil_pcie_remove(struct pci_dev *pdev)
{
struct wil6210_priv *wil = pci_get_drvdata(pdev);
wil6210_debugfs_remove(wil);
wil_if_pcie_disable(wil);
wil_if_remove(wil);
wil_if_free(wil);
pci_iounmap(pdev, wil->csr);
pci_release_region(pdev, 0);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
}
static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = {
{ PCI_DEVICE(0x1ae9, 0x0301) },
{ /* end: all zeroes */ },
};
MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids);
static struct pci_driver wil6210_driver = {
.probe = wil_pcie_probe,
.remove = wil_pcie_remove,
.id_table = wil6210_pcie_ids,
.name = WIL_NAME,
};
module_pci_driver(wil6210_driver);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Qualcomm Atheros <wil6210@qca.qualcomm.com>");
MODULE_DESCRIPTION("Driver for 60g WiFi WIL6210 card");
此差异已折叠。
/*
* Copyright (c) 2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef WIL6210_TXRX_H
#define WIL6210_TXRX_H
#define BUF_SW_OWNED (1)
#define BUF_HW_OWNED (0)
/* size of max. Rx packet */
#define RX_BUF_LEN (2048)
#define TX_BUF_LEN (2048)
/* how many bytes to reserve for rtap header? */
#define WIL6210_RTAP_SIZE (128)
/* Tx/Rx path */
/*
* Tx descriptor - MAC part
* [dword 0]
* bit 0.. 9 : lifetime_expiry_value:10
* bit 10 : interrup_en:1
* bit 11 : status_en:1
* bit 12..13 : txss_override:2
* bit 14 : timestamp_insertion:1
* bit 15 : duration_preserve:1
* bit 16..21 : reserved0:6
* bit 22..26 : mcs_index:5
* bit 27 : mcs_en:1
* bit 28..29 : reserved1:2
* bit 30 : reserved2:1
* bit 31 : sn_preserved:1
* [dword 1]
* bit 0.. 3 : pkt_mode:4
* bit 4 : pkt_mode_en:1
* bit 5.. 7 : reserved0:3
* bit 8..13 : reserved1:6
* bit 14 : reserved2:1
* bit 15 : ack_policy_en:1
* bit 16..19 : dst_index:4
* bit 20 : dst_index_en:1
* bit 21..22 : ack_policy:2
* bit 23 : lifetime_en:1
* bit 24..30 : max_retry:7
* bit 31 : max_retry_en:1
* [dword 2]
* bit 0.. 7 : num_of_descriptors:8
* bit 8..17 : reserved:10
* bit 18..19 : l2_translation_type:2
* bit 20 : snap_hdr_insertion_en:1
* bit 21 : vlan_removal_en:1
* bit 22..31 : reserved0:10
* [dword 3]
* bit 0.. 31: ucode_cmd:32
*/
struct vring_tx_mac {
u32 d[3];
u32 ucode_cmd;
} __packed;
/* TX MAC Dword 0 */
#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_POS 0
#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_LEN 10
#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_MSK 0x3FF
#define MAC_CFG_DESC_TX_0_INTERRUP_EN_POS 10
#define MAC_CFG_DESC_TX_0_INTERRUP_EN_LEN 1
#define MAC_CFG_DESC_TX_0_INTERRUP_EN_MSK 0x400
#define MAC_CFG_DESC_TX_0_STATUS_EN_POS 11
#define MAC_CFG_DESC_TX_0_STATUS_EN_LEN 1
#define MAC_CFG_DESC_TX_0_STATUS_EN_MSK 0x800
#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_POS 12
#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_LEN 2
#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_MSK 0x3000
#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_POS 14
#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_LEN 1
#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_MSK 0x4000
#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_POS 15
#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_LEN 1
#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_MSK 0x8000
#define MAC_CFG_DESC_TX_0_MCS_INDEX_POS 22
#define MAC_CFG_DESC_TX_0_MCS_INDEX_LEN 5
#define MAC_CFG_DESC_TX_0_MCS_INDEX_MSK 0x7C00000
#define MAC_CFG_DESC_TX_0_MCS_EN_POS 27
#define MAC_CFG_DESC_TX_0_MCS_EN_LEN 1
#define MAC_CFG_DESC_TX_0_MCS_EN_MSK 0x8000000
#define MAC_CFG_DESC_TX_0_SN_PRESERVED_POS 31
#define MAC_CFG_DESC_TX_0_SN_PRESERVED_LEN 1
#define MAC_CFG_DESC_TX_0_SN_PRESERVED_MSK 0x80000000
/* TX MAC Dword 1 */
#define MAC_CFG_DESC_TX_1_PKT_MODE_POS 0
#define MAC_CFG_DESC_TX_1_PKT_MODE_LEN 4
#define MAC_CFG_DESC_TX_1_PKT_MODE_MSK 0xF
#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_POS 4
#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_LEN 1
#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_MSK 0x10
#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_POS 15
#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_LEN 1
#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_MSK 0x8000
#define MAC_CFG_DESC_TX_1_DST_INDEX_POS 16
#define MAC_CFG_DESC_TX_1_DST_INDEX_LEN 4
#define MAC_CFG_DESC_TX_1_DST_INDEX_MSK 0xF0000
#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS 20
#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_LEN 1
#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_MSK 0x100000
#define MAC_CFG_DESC_TX_1_ACK_POLICY_POS 21
#define MAC_CFG_DESC_TX_1_ACK_POLICY_LEN 2
#define MAC_CFG_DESC_TX_1_ACK_POLICY_MSK 0x600000
#define MAC_CFG_DESC_TX_1_LIFETIME_EN_POS 23
#define MAC_CFG_DESC_TX_1_LIFETIME_EN_LEN 1
#define MAC_CFG_DESC_TX_1_LIFETIME_EN_MSK 0x800000
#define MAC_CFG_DESC_TX_1_MAX_RETRY_POS 24
#define MAC_CFG_DESC_TX_1_MAX_RETRY_LEN 7
#define MAC_CFG_DESC_TX_1_MAX_RETRY_MSK 0x7F000000
#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_POS 31
#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_LEN 1
#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_MSK 0x80000000
/* TX MAC Dword 2 */
#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS 0
#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_LEN 8
#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_MSK 0xFF
#define MAC_CFG_DESC_TX_2_RESERVED_POS 8
#define MAC_CFG_DESC_TX_2_RESERVED_LEN 10
#define MAC_CFG_DESC_TX_2_RESERVED_MSK 0x3FF00
#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS 18
#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_LEN 2
#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_MSK 0xC0000
#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS 20
#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_LEN 1
#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_MSK 0x100000
#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_POS 21
#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_LEN 1
#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_MSK 0x200000
/* TX MAC Dword 3 */
#define MAC_CFG_DESC_TX_3_UCODE_CMD_POS 0
#define MAC_CFG_DESC_TX_3_UCODE_CMD_LEN 32
#define MAC_CFG_DESC_TX_3_UCODE_CMD_MSK 0xFFFFFFFF
/* TX DMA Dword 0 */
#define DMA_CFG_DESC_TX_0_L4_LENGTH_POS 0
#define DMA_CFG_DESC_TX_0_L4_LENGTH_LEN 8
#define DMA_CFG_DESC_TX_0_L4_LENGTH_MSK 0xFF
#define DMA_CFG_DESC_TX_0_CMD_EOP_POS 8
#define DMA_CFG_DESC_TX_0_CMD_EOP_LEN 1
#define DMA_CFG_DESC_TX_0_CMD_EOP_MSK 0x100
#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS 10
#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_LEN 1
#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_MSK 0x400
#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_POS 11
#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_LEN 2
#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_MSK 0x1800
#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_POS 13
#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_LEN 1
#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_MSK 0x2000
#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_POS 14
#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_LEN 1
#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_MSK 0x4000
#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_POS 15
#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_LEN 1
#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_MSK 0x8000
#define DMA_CFG_DESC_TX_0_QID_POS 16
#define DMA_CFG_DESC_TX_0_QID_LEN 5
#define DMA_CFG_DESC_TX_0_QID_MSK 0x1F0000
#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_POS 21
#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_LEN 1
#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_MSK 0x200000
#define DMA_CFG_DESC_TX_0_L4_TYPE_POS 30
#define DMA_CFG_DESC_TX_0_L4_TYPE_LEN 2
#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000
#define TX_DMA_STATUS_DU BIT(0)
struct vring_tx_dma {
u32 d0;
u32 addr_low;
u16 addr_high;
u8 ip_length;
u8 b11; /* 0..6: mac_length; 7:ip_version */
u8 error; /* 0..2: err; 3..7: reserved; */
u8 status; /* 0: used; 1..7; reserved */
u16 length;
} __packed;
/*
* Rx descriptor - MAC part
* [dword 0]
* bit 0.. 3 : tid:4 The QoS (b3-0) TID Field
* bit 4.. 6 : connection_id:3 :The Source index that was found during
* Parsing the TA. This field is used to define the source of the packet
* bit 7 : reserved:1
* bit 8.. 9 : mac_id:2 : The MAC virtual Ring number (always zero)
* bit 10..11 : frame_type:2 : The FC Control (b3-2) - MPDU Type
* (management, data, control and extension)
* bit 12..15 : frame_subtype:4 : The FC Control (b7-4) - Frame Subtype
* bit 16..27 : seq_number:12 The received Sequence number field
* bit 28..31 : extended:4 extended subtype
* [dword 1]
* bit 0.. 3 : reserved
* bit 4.. 5 : key_id:2
* bit 6 : decrypt_bypass:1
* bit 7 : security:1
* bit 8.. 9 : ds_bits:2
* bit 10 : a_msdu_present:1 from qos header
* bit 11 : a_msdu_type:1 from qos header
* bit 12 : a_mpdu:1 part of AMPDU aggregation
* bit 13 : broadcast:1
* bit 14 : mutlicast:1
* bit 15 : reserved:1
* bit 16..20 : rx_mac_qid:5 The Queue Identifier that the packet
* is received from
* bit 21..24 : mcs:4
* bit 25..28 : mic_icr:4
* bit 29..31 : reserved:3
* [dword 2]
* bit 0.. 2 : time_slot:3 The timeslot that the MPDU is received
* bit 3 : fc_protocol_ver:1 The FC Control (b0) - Protocol Version
* bit 4 : fc_order:1 The FC Control (b15) -Order
* bit 5.. 7 : qos_ack_policy:3 The QoS (b6-5) ack policy Field
* bit 8 : esop:1 The QoS (b4) ESOP field
* bit 9 : qos_rdg_more_ppdu:1 The QoS (b9) RDG field
* bit 10..14 : qos_reserved:5 The QoS (b14-10) Reserved field
* bit 15 : qos_ac_constraint:1
* bit 16..31 : pn_15_0:16 low 2 bytes of PN
* [dword 3]
* bit 0..31 : pn_47_16:32 high 4 bytes of PN
*/
struct vring_rx_mac {
u32 d0;
u32 d1;
u16 w4;
u16 pn_15_0;
u32 pn_47_16;
} __packed;
/*
* Rx descriptor - DMA part
* [dword 0]
* bit 0.. 7 : l4_length:8 layer 4 length
* bit 8.. 9 : reserved:2
* bit 10 : cmd_dma_it:1
* bit 11..15 : reserved:5
* bit 16..29 : phy_info_length:14
* bit 30..31 : l4_type:2 valid if the L4I bit is set in the status field
* [dword 1]
* bit 0..31 : addr_low:32 The payload buffer low address
* [dword 2]
* bit 0..15 : addr_high:16 The payload buffer high address
* bit 16..23 : ip_length:8
* bit 24..30 : mac_length:7
* bit 31 : ip_version:1
* [dword 3]
* [byte 12] error
* [byte 13] status
* bit 0 : du:1
* bit 1 : eop:1
* bit 2 : error:1
* bit 3 : mi:1
* bit 4 : l3_identified:1
* bit 5 : l4_identified:1
* bit 6 : phy_info_included:1
* bit 7 : reserved:1
* [word 7] length
*
*/
#define RX_DMA_D0_CMD_DMA_IT BIT(10)
#define RX_DMA_STATUS_DU BIT(0)
#define RX_DMA_STATUS_ERROR BIT(2)
#define RX_DMA_STATUS_PHY_INFO BIT(6)
struct vring_rx_dma {
u32 d0;
u32 addr_low;
u16 addr_high;
u8 ip_length;
u8 b11;
u8 error;
u8 status;
u16 length;
} __packed;
struct vring_tx_desc {
struct vring_tx_mac mac;
struct vring_tx_dma dma;
} __packed;
struct vring_rx_desc {
struct vring_rx_mac mac;
struct vring_rx_dma dma;
} __packed;
union vring_desc {
struct vring_tx_desc tx;
struct vring_rx_desc rx;
} __packed;
static inline int wil_rxdesc_phy_length(volatile struct vring_rx_desc *d)
{
return WIL_GET_BITS(d->dma.d0, 16, 29);
}
static inline int wil_rxdesc_mcs(volatile struct vring_rx_desc *d)
{
return WIL_GET_BITS(d->mac.d1, 21, 24);
}
static inline int wil_rxdesc_ds_bits(volatile struct vring_rx_desc *d)
{
return WIL_GET_BITS(d->mac.d1, 8, 9);
}
static inline int wil_rxdesc_ftype(volatile struct vring_rx_desc *d)
{
return WIL_GET_BITS(d->mac.d0, 10, 11);
}
#endif /* WIL6210_TXRX_H */
/*
* Copyright (c) 2012 Qualcomm Atheros, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef __WIL6210_H__
#define __WIL6210_H__
#include <linux/netdevice.h>
#include <linux/wireless.h>
#include <net/cfg80211.h>
#include "dbg_hexdump.h"
#define WIL_NAME "wil6210"
/**
* extract bits [@b0:@b1] (inclusive) from the value @x
* it should be @b0 <= @b1, or result is incorrect
*/
static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
{
return (x >> b0) & ((1 << (b1 - b0 + 1)) - 1);
}
#define WIL6210_MEM_SIZE (2*1024*1024UL)
#define WIL6210_TX_QUEUES (4)
#define WIL6210_RX_RING_SIZE (128)
#define WIL6210_TX_RING_SIZE (128)
#define WIL6210_MAX_TX_RINGS (24)
/* Hardware definitions begin */
/*
* Mapping
* RGF File | Host addr | FW addr
* | |
* user_rgf | 0x000000 | 0x880000
* dma_rgf | 0x001000 | 0x881000
* pcie_rgf | 0x002000 | 0x882000
* | |
*/
/* Where various structures placed in host address space */
#define WIL6210_FW_HOST_OFF (0x880000UL)
#define HOSTADDR(fwaddr) (fwaddr - WIL6210_FW_HOST_OFF)
/*
* Interrupt control registers block
*
* each interrupt controlled by the same bit in all registers
*/
struct RGF_ICR {
u32 ICC; /* Cause Control, RW: 0 - W1C, 1 - COR */
u32 ICR; /* Cause, W1C/COR depending on ICC */
u32 ICM; /* Cause masked (ICR & ~IMV), W1C/COR depending on ICC */
u32 ICS; /* Cause Set, WO */
u32 IMV; /* Mask, RW+S/C */
u32 IMS; /* Mask Set, write 1 to set */
u32 IMC; /* Mask Clear, write 1 to clear */
} __packed;
/* registers - FW addresses */
#define RGF_USER_USER_SCRATCH_PAD (0x8802bc)
#define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */
#define BIT_USER_USER_ICR_SW_INT_2 BIT(18)
#define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14)
#define RGF_USER_MAC_CPU_0 (0x8801fc)
#define RGF_USER_USER_CPU_0 (0x8801e0)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_0 (0x880b04)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_1 (0x880b08)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_2 (0x880b0c)
#define RGF_USER_CLKS_CTL_SW_RST_VEC_3 (0x880b10)
#define RGF_DMA_PSEUDO_CAUSE (0x881c68)
#define RGF_DMA_PSEUDO_CAUSE_MASK_SW (0x881c6c)
#define RGF_DMA_PSEUDO_CAUSE_MASK_FW (0x881c70)
#define BIT_DMA_PSEUDO_CAUSE_RX BIT(0)
#define BIT_DMA_PSEUDO_CAUSE_TX BIT(1)
#define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2)
#define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */
#define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0)
#define BIT_DMA_EP_TX_ICR_TX_DONE_N(n) BIT(n+1) /* n = [0..23] */
#define RGF_DMA_EP_RX_ICR (0x881bd0) /* struct RGF_ICR */
#define BIT_DMA_EP_RX_ICR_RX_DONE BIT(0)
#define RGF_DMA_EP_MISC_ICR (0x881bec) /* struct RGF_ICR */
#define BIT_DMA_EP_MISC_ICR_RX_HTRSH BIT(0)
#define BIT_DMA_EP_MISC_ICR_TX_NO_ACT BIT(1)
#define BIT_DMA_EP_MISC_ICR_FW_INT0 BIT(28)
#define BIT_DMA_EP_MISC_ICR_FW_INT1 BIT(29)
/* Interrupt moderation control */
#define RGF_DMA_ITR_CNT_TRSH (0x881c5c)
#define RGF_DMA_ITR_CNT_DATA (0x881c60)
#define RGF_DMA_ITR_CNT_CRL (0x881C64)
#define BIT_DMA_ITR_CNT_CRL_EN BIT(0)
#define BIT_DMA_ITR_CNT_CRL_EXT_TICK BIT(1)
#define BIT_DMA_ITR_CNT_CRL_FOREVER BIT(2)
#define BIT_DMA_ITR_CNT_CRL_CLR BIT(3)
#define BIT_DMA_ITR_CNT_CRL_REACH_TRSH BIT(4)
/* popular locations */
#define HOST_MBOX HOSTADDR(RGF_USER_USER_SCRATCH_PAD)
#define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \
offsetof(struct RGF_ICR, ICS))
#define SW_INT_MBOX BIT_USER_USER_ICR_SW_INT_2
/* ISR register bits */
#define ISR_MISC_FW_READY BIT_DMA_EP_MISC_ICR_FW_INT0
#define ISR_MISC_MBOX_EVT BIT_DMA_EP_MISC_ICR_FW_INT1
/* Hardware definitions end */
struct wil6210_mbox_ring {
u32 base;
u16 entry_size; /* max. size of mbox entry, incl. all headers */
u16 size;
u32 tail;
u32 head;
} __packed;
struct wil6210_mbox_ring_desc {
__le32 sync;
__le32 addr;
} __packed;
/* at HOST_OFF_WIL6210_MBOX_CTL */
struct wil6210_mbox_ctl {
struct wil6210_mbox_ring tx;
struct wil6210_mbox_ring rx;
} __packed;
struct wil6210_mbox_hdr {
__le16 seq;
__le16 len; /* payload, bytes after this header */
__le16 type;
u8 flags;
u8 reserved;
} __packed;
#define WIL_MBOX_HDR_TYPE_WMI (0)
/* max. value for wil6210_mbox_hdr.len */
#define MAX_MBOXITEM_SIZE (240)
struct wil6210_mbox_hdr_wmi {
u8 reserved0[2];
__le16 id;
__le16 info1; /* bits [0..3] - device_id, rest - unused */
u8 reserved1[2];
} __packed;
struct pending_wmi_event {
struct list_head list;
struct {
struct wil6210_mbox_hdr hdr;
struct wil6210_mbox_hdr_wmi wmi;
u8 data[0];
} __packed event;
};
union vring_desc;
struct vring {
dma_addr_t pa;
volatile union vring_desc *va; /* vring_desc[size], WriteBack by DMA */
u16 size; /* number of vring_desc elements */
u32 swtail;
u32 swhead;
u32 hwtail; /* write here to inform hw */
void **ctx; /* void *ctx[size] - software context */
};
enum { /* for wil6210_priv.status */
wil_status_fwready = 0,
wil_status_fwconnected,
wil_status_dontscan,
wil_status_irqen, /* FIXME: interrupts enabled - for debug */
};
struct pci_dev;
struct wil6210_stats {
u64 tsf;
u32 snr;
u16 last_mcs_rx;
u16 bf_mcs; /* last BF, used for Tx */
u16 my_rx_sector;
u16 my_tx_sector;
u16 peer_rx_sector;
u16 peer_tx_sector;
};
struct wil6210_priv {
struct pci_dev *pdev;
int n_msi;
struct wireless_dev *wdev;
void __iomem *csr;
ulong status;
/* profile */
u32 monitor_flags;
u32 secure_pcp; /* create secure PCP? */
int sinfo_gen;
/* cached ISR registers */
u32 isr_misc;
/* mailbox related */
struct mutex wmi_mutex;
struct wil6210_mbox_ctl mbox_ctl;
struct completion wmi_ready;
u16 wmi_seq;
u16 reply_id; /**< wait for this WMI event */
void *reply_buf;
u16 reply_size;
struct workqueue_struct *wmi_wq; /* for deferred calls */
struct work_struct wmi_event_worker;
struct workqueue_struct *wmi_wq_conn; /* for connect worker */
struct work_struct wmi_connect_worker;
struct work_struct disconnect_worker;
struct timer_list connect_timer;
int pending_connect_cid;
struct list_head pending_wmi_ev;
/*
* protect pending_wmi_ev
* - fill in IRQ from wil6210_irq_misc,
* - consumed in thread by wmi_event_worker
*/
spinlock_t wmi_ev_lock;
/* DMA related */
struct vring vring_rx;
struct vring vring_tx[WIL6210_MAX_TX_RINGS];
u8 dst_addr[WIL6210_MAX_TX_RINGS][ETH_ALEN];
/* scan */
struct cfg80211_scan_request *scan_request;
struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */
/* statistics */
struct wil6210_stats stats;
/* debugfs */
struct dentry *debug;
struct debugfs_blob_wrapper fw_code_blob;
struct debugfs_blob_wrapper fw_data_blob;
struct debugfs_blob_wrapper fw_peri_blob;
struct debugfs_blob_wrapper uc_code_blob;
struct debugfs_blob_wrapper uc_data_blob;
struct debugfs_blob_wrapper rgf_blob;
};
#define wil_to_wiphy(i) (i->wdev->wiphy)
#define wil_to_dev(i) (wiphy_dev(wil_to_wiphy(i)))
#define wiphy_to_wil(w) (struct wil6210_priv *)(wiphy_priv(w))
#define wil_to_wdev(i) (i->wdev)
#define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w))
#define wil_to_ndev(i) (wil_to_wdev(i)->netdev)
#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr))
#define wil_dbg(wil, fmt, arg...) netdev_dbg(wil_to_ndev(wil), fmt, ##arg)
#define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg)
#define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg)
#define wil_dbg_IRQ(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg)
#define wil_dbg_TXRX(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg)
#define wil_dbg_WMI(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg)
#define wil_hex_dump_TXRX(prefix_str, prefix_type, rowsize, \
groupsize, buf, len, ascii) \
wil_print_hex_dump_debug("DBG[TXRX]" prefix_str,\
prefix_type, rowsize, \
groupsize, buf, len, ascii)
#define wil_hex_dump_WMI(prefix_str, prefix_type, rowsize, \
groupsize, buf, len, ascii) \
wil_print_hex_dump_debug("DBG[ WMI]" prefix_str,\
prefix_type, rowsize, \
groupsize, buf, len, ascii)
void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src,
size_t count);
void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
size_t count);
void *wil_if_alloc(struct device *dev, void __iomem *csr);
void wil_if_free(struct wil6210_priv *wil);
int wil_if_add(struct wil6210_priv *wil);
void wil_if_remove(struct wil6210_priv *wil);
int wil_priv_init(struct wil6210_priv *wil);
void wil_priv_deinit(struct wil6210_priv *wil);
int wil_reset(struct wil6210_priv *wil);
void wil_link_on(struct wil6210_priv *wil);
void wil_link_off(struct wil6210_priv *wil);
int wil_up(struct wil6210_priv *wil);
int wil_down(struct wil6210_priv *wil);
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r);
void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr);
void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr);
int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr,
struct wil6210_mbox_hdr *hdr);
int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len);
void wmi_recv_cmd(struct wil6210_priv *wil);
int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
u16 reply_id, void *reply, u8 reply_size, int to_msec);
void wmi_connect_worker(struct work_struct *work);
void wmi_event_worker(struct work_struct *work);
void wmi_event_flush(struct wil6210_priv *wil);
int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid);
int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid);
int wmi_set_channel(struct wil6210_priv *wil, int channel);
int wmi_get_channel(struct wil6210_priv *wil, int *channel);
int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb);
int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index,
const void *mac_addr);
int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index,
const void *mac_addr, int key_len, const void *key);
int wmi_echo(struct wil6210_priv *wil);
int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie);
int wil6210_init_irq(struct wil6210_priv *wil, int irq);
void wil6210_fini_irq(struct wil6210_priv *wil, int irq);
void wil6210_disable_irq(struct wil6210_priv *wil);
void wil6210_enable_irq(struct wil6210_priv *wil);
int wil6210_debugfs_init(struct wil6210_priv *wil);
void wil6210_debugfs_remove(struct wil6210_priv *wil);
struct wireless_dev *wil_cfg80211_init(struct device *dev);
void wil_wdev_free(struct wil6210_priv *wil);
int wmi_set_mac_address(struct wil6210_priv *wil, void *addr);
int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype);
void wil6210_disconnect(struct wil6210_priv *wil, void *bssid);
int wil_rx_init(struct wil6210_priv *wil);
void wil_rx_fini(struct wil6210_priv *wil);
/* TX API */
int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size,
int cid, int tid);
void wil_vring_fini_tx(struct wil6210_priv *wil, int id);
netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev);
void wil_tx_complete(struct wil6210_priv *wil, int ringid);
/* RX API */
void wil_rx_handle(struct wil6210_priv *wil);
int wil_iftype_nl2wmi(enum nl80211_iftype type);
#endif /* __WIL6210_H__ */
此差异已折叠。
此差异已折叠。
......@@ -7,6 +7,7 @@
#include <linux/hw_random.h>
#include <linux/bcma/bcma.h>
#include <linux/ssb/ssb.h>
#include <linux/completion.h>
#include <net/mac80211.h>
#include "debugfs.h"
......@@ -722,6 +723,10 @@ enum b43_firmware_file_type {
struct b43_request_fw_context {
/* The device we are requesting the fw for. */
struct b43_wldev *dev;
/* a completion event structure needed if this call is asynchronous */
struct completion fw_load_complete;
/* a pointer to the firmware object */
const struct firmware *blob;
/* The type of firmware to request. */
enum b43_firmware_file_type req_type;
/* Error messages for each firmware type. */
......
......@@ -2088,11 +2088,18 @@ static void b43_print_fw_helptext(struct b43_wl *wl, bool error)
b43warn(wl, text);
}
static void b43_fw_cb(const struct firmware *firmware, void *context)
{
struct b43_request_fw_context *ctx = context;
ctx->blob = firmware;
complete(&ctx->fw_load_complete);
}
int b43_do_request_fw(struct b43_request_fw_context *ctx,
const char *name,
struct b43_firmware_file *fw)
struct b43_firmware_file *fw, bool async)
{
const struct firmware *blob;
struct b43_fw_header *hdr;
u32 size;
int err;
......@@ -2131,11 +2138,31 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx,
B43_WARN_ON(1);
return -ENOSYS;
}
err = request_firmware(&blob, ctx->fwname, ctx->dev->dev->dev);
if (async) {
/* do this part asynchronously */
init_completion(&ctx->fw_load_complete);
err = request_firmware_nowait(THIS_MODULE, 1, ctx->fwname,
ctx->dev->dev->dev, GFP_KERNEL,
ctx, b43_fw_cb);
if (err < 0) {
pr_err("Unable to load firmware\n");
return err;
}
/* stall here until fw ready */
wait_for_completion(&ctx->fw_load_complete);
if (ctx->blob)
goto fw_ready;
/* On some ARM systems, the async request will fail, but the next sync
* request works. For this reason, we dall through here
*/
}
err = request_firmware(&ctx->blob, ctx->fwname,
ctx->dev->dev->dev);
if (err == -ENOENT) {
snprintf(ctx->errors[ctx->req_type],
sizeof(ctx->errors[ctx->req_type]),
"Firmware file \"%s\" not found\n", ctx->fwname);
"Firmware file \"%s\" not found\n",
ctx->fwname);
return err;
} else if (err) {
snprintf(ctx->errors[ctx->req_type],
......@@ -2144,14 +2171,15 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx,
ctx->fwname, err);
return err;
}
if (blob->size < sizeof(struct b43_fw_header))
fw_ready:
if (ctx->blob->size < sizeof(struct b43_fw_header))
goto err_format;
hdr = (struct b43_fw_header *)(blob->data);
hdr = (struct b43_fw_header *)(ctx->blob->data);
switch (hdr->type) {
case B43_FW_TYPE_UCODE:
case B43_FW_TYPE_PCM:
size = be32_to_cpu(hdr->size);
if (size != blob->size - sizeof(struct b43_fw_header))
if (size != ctx->blob->size - sizeof(struct b43_fw_header))
goto err_format;
/* fallthrough */
case B43_FW_TYPE_IV:
......@@ -2162,7 +2190,7 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx,
goto err_format;
}
fw->data = blob;
fw->data = ctx->blob;
fw->filename = name;
fw->type = ctx->req_type;
......@@ -2172,7 +2200,7 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx,
snprintf(ctx->errors[ctx->req_type],
sizeof(ctx->errors[ctx->req_type]),
"Firmware file \"%s\" format error.\n", ctx->fwname);
release_firmware(blob);
release_firmware(ctx->blob);
return -EPROTO;
}
......@@ -2223,7 +2251,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)
goto err_no_ucode;
}
}
err = b43_do_request_fw(ctx, filename, &fw->ucode);
err = b43_do_request_fw(ctx, filename, &fw->ucode, true);
if (err)
goto err_load;
......@@ -2235,7 +2263,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)
else
goto err_no_pcm;
fw->pcm_request_failed = false;
err = b43_do_request_fw(ctx, filename, &fw->pcm);
err = b43_do_request_fw(ctx, filename, &fw->pcm, false);
if (err == -ENOENT) {
/* We did not find a PCM file? Not fatal, but
* core rev <= 10 must do without hwcrypto then. */
......@@ -2296,7 +2324,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)
default:
goto err_no_initvals;
}
err = b43_do_request_fw(ctx, filename, &fw->initvals);
err = b43_do_request_fw(ctx, filename, &fw->initvals, false);
if (err)
goto err_load;
......@@ -2355,7 +2383,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)
default:
goto err_no_initvals;
}
err = b43_do_request_fw(ctx, filename, &fw->initvals_band);
err = b43_do_request_fw(ctx, filename, &fw->initvals_band, false);
if (err)
goto err_load;
......
......@@ -137,9 +137,8 @@ void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on);
struct b43_request_fw_context;
int b43_do_request_fw(struct b43_request_fw_context *ctx,
const char *name,
struct b43_firmware_file *fw);
int b43_do_request_fw(struct b43_request_fw_context *ctx, const char *name,
struct b43_firmware_file *fw, bool async);
void b43_do_release_fw(struct b43_firmware_file *fw);
#endif /* B43_MAIN_H_ */
......@@ -3028,10 +3028,11 @@ brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie,
len = wpa_ie->len + TLV_HDR_LEN;
data = (u8 *)wpa_ie;
offset = 0;
offset = TLV_HDR_LEN;
if (!is_rsn_ie)
offset += VS_IE_FIXED_HDR_LEN;
offset += WPA_IE_VERSION_LEN;
else
offset += WPA_IE_VERSION_LEN;
/* check for multicast cipher suite */
if (offset + WPA_IE_MIN_OUI_LEN > len) {
......
/*
* Copyright (c) 2012 Broadcom Corporation
* Copyright (c) 2012 Canonical Ltd.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
......
......@@ -1343,13 +1343,13 @@ static bool wlc_lcnphy_rx_iq_cal_gain(struct brcms_phy *pi, u16 biq1_gain,
wlc_lcnphy_rx_gain_override_enable(pi, true);
wlc_lcnphy_start_tx_tone(pi, 2000, (40 >> 1), 0);
usleep_range(500, 500);
udelay(500);
write_radio_reg(pi, RADIO_2064_REG112, 0);
if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_l))
return false;
wlc_lcnphy_start_tx_tone(pi, 2000, 40, 0);
usleep_range(500, 500);
udelay(500);
write_radio_reg(pi, RADIO_2064_REG112, 0);
if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_h))
return false;
......
......@@ -3273,7 +3273,7 @@ il3945_store_measurement(struct device *d, struct device_attribute *attr,
if (count) {
char *p = buffer;
strncpy(buffer, buf, min(sizeof(buffer), count));
strlcpy(buffer, buf, sizeof(buffer));
channel = simple_strtoul(p, NULL, 0);
if (channel)
params.channel = channel;
......
......@@ -3958,17 +3958,21 @@ il_connection_init_rx_config(struct il_priv *il)
memset(&il->staging, 0, sizeof(il->staging));
if (!il->vif) {
switch (il->iw_mode) {
case NL80211_IFTYPE_UNSPECIFIED:
il->staging.dev_type = RXON_DEV_TYPE_ESS;
} else if (il->vif->type == NL80211_IFTYPE_STATION) {
break;
case NL80211_IFTYPE_STATION:
il->staging.dev_type = RXON_DEV_TYPE_ESS;
il->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK;
} else if (il->vif->type == NL80211_IFTYPE_ADHOC) {
break;
case NL80211_IFTYPE_ADHOC:
il->staging.dev_type = RXON_DEV_TYPE_IBSS;
il->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK;
il->staging.filter_flags =
RXON_FILTER_BCON_AWARE_MSK | RXON_FILTER_ACCEPT_GRP_MSK;
} else {
break;
default:
IL_ERR("Unsupported interface type %d\n", il->vif->type);
return;
}
......@@ -4550,8 +4554,7 @@ il_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
EXPORT_SYMBOL(il_mac_add_interface);
static void
il_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif,
bool mode_change)
il_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif)
{
lockdep_assert_held(&il->mutex);
......@@ -4560,9 +4563,7 @@ il_teardown_interface(struct il_priv *il, struct ieee80211_vif *vif,
il_force_scan_end(il);
}
if (!mode_change)
il_set_mode(il);
il_set_mode(il);
}
void
......@@ -4575,8 +4576,8 @@ il_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
WARN_ON(il->vif != vif);
il->vif = NULL;
il_teardown_interface(il, vif, false);
il->iw_mode = NL80211_IFTYPE_UNSPECIFIED;
il_teardown_interface(il, vif);
memset(il->bssid, 0, ETH_ALEN);
D_MAC80211("leave\n");
......@@ -4685,18 +4686,10 @@ il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
}
/* success */
il_teardown_interface(il, vif, true);
vif->type = newtype;
vif->p2p = false;
err = il_set_mode(il);
WARN_ON(err);
/*
* We've switched internally, but submitting to the
* device may have failed for some reason. Mask this
* error, because otherwise mac80211 will not switch
* (and set the interface type back) and we'll be
* out of sync with it.
*/
il->iw_mode = newtype;
il_teardown_interface(il, vif);
err = 0;
out:
......
......@@ -1071,6 +1071,8 @@ static void iwlagn_set_tx_status(struct iwl_priv *priv,
{
u16 status = le16_to_cpu(tx_resp->status.status);
info->flags &= ~IEEE80211_TX_CTL_AMPDU;
info->status.rates[0].count = tx_resp->failure_frame + 1;
info->flags |= iwl_tx_status_to_mac80211(status);
iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags),
......@@ -1143,13 +1145,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
next_reclaimed = ssn;
}
if (tid != IWL_TID_NON_QOS) {
priv->tid_data[sta_id][tid].next_reclaimed =
next_reclaimed;
IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n",
next_reclaimed);
}
iwl_trans_reclaim(priv->trans, txq_id, ssn, &skbs);
iwlagn_check_ratid_empty(priv, sta_id, tid);
......@@ -1200,11 +1195,28 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,
if (!is_agg)
iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1);
/*
* W/A for FW bug - the seq_ctl isn't updated when the
* queues are flushed. Fetch it from the packet itself
*/
if (!is_agg && status == TX_STATUS_FAIL_FIFO_FLUSHED) {
next_reclaimed = le16_to_cpu(hdr->seq_ctrl);
next_reclaimed =
SEQ_TO_SN(next_reclaimed + 0x10);
}
is_offchannel_skb =
(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN);
freed++;
}
if (tid != IWL_TID_NON_QOS) {
priv->tid_data[sta_id][tid].next_reclaimed =
next_reclaimed;
IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n",
next_reclaimed);
}
if (!is_agg && freed != 1)
IWL_ERR(priv, "Q: %d, freed %d\n", txq_id, freed);
......
......@@ -1172,6 +1172,7 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data)
else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) &&
!trans_pcie->inta)
iwl_enable_interrupts(trans);
return IRQ_HANDLED;
none:
/* re-enable interrupts here since we don't have anything to service. */
......
......@@ -1458,7 +1458,7 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid,
struct cfg80211_ssid req_ssid;
int ret, auth_type = 0;
struct cfg80211_bss *bss = NULL;
u8 is_scanning_required = 0, config_bands = 0;
u8 is_scanning_required = 0;
memset(&req_ssid, 0, sizeof(struct cfg80211_ssid));
......@@ -1477,19 +1477,6 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid,
/* disconnect before try to associate */
mwifiex_deauthenticate(priv, NULL);
if (channel) {
if (mode == NL80211_IFTYPE_STATION) {
if (channel->band == IEEE80211_BAND_2GHZ)
config_bands = BAND_B | BAND_G | BAND_GN;
else
config_bands = BAND_A | BAND_AN;
if (!((config_bands | priv->adapter->fw_bands) &
~priv->adapter->fw_bands))
priv->adapter->config_bands = config_bands;
}
}
/* As this is new association, clear locally stored
* keys and security related flags */
priv->sec_info.wpa_enabled = false;
......@@ -1706,9 +1693,9 @@ static int mwifiex_set_ibss_params(struct mwifiex_private *priv,
if (cfg80211_get_chandef_type(&params->chandef) !=
NL80211_CHAN_NO_HT)
config_bands |= BAND_GN;
config_bands |= BAND_G | BAND_GN;
} else {
if (cfg80211_get_chandef_type(&params->chandef) !=
if (cfg80211_get_chandef_type(&params->chandef) ==
NL80211_CHAN_NO_HT)
config_bands = BAND_A;
else
......
......@@ -164,7 +164,7 @@ static int mwifiex_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
if (pdev) {
card = (struct pcie_service_card *) pci_get_drvdata(pdev);
if (!card || card->adapter) {
if (!card || !card->adapter) {
pr_err("Card or adapter structure is not valid\n");
return 0;
}
......
......@@ -56,7 +56,6 @@ int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist,
*/
int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter)
{
bool cancel_flag = false;
int status;
struct cmd_ctrl_node *cmd_queued;
......@@ -70,14 +69,11 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter)
atomic_inc(&adapter->cmd_pending);
/* Wait for completion */
wait_event_interruptible(adapter->cmd_wait_q.wait,
*(cmd_queued->condition));
if (!*(cmd_queued->condition))
cancel_flag = true;
if (cancel_flag) {
mwifiex_cancel_pending_ioctl(adapter);
dev_dbg(adapter->dev, "cmd cancel\n");
status = wait_event_interruptible(adapter->cmd_wait_q.wait,
*(cmd_queued->condition));
if (status) {
dev_err(adapter->dev, "cmd_wait_q terminated: %d\n", status);
return status;
}
status = adapter->cmd_wait_q.status;
......@@ -287,6 +283,20 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss,
if (ret)
goto done;
if (bss_desc) {
u8 config_bands = 0;
if (mwifiex_band_to_radio_type((u8) bss_desc->bss_band)
== HostCmd_SCAN_RADIO_TYPE_BG)
config_bands = BAND_B | BAND_G | BAND_GN;
else
config_bands = BAND_A | BAND_AN;
if (!((config_bands | adapter->fw_bands) &
~adapter->fw_bands))
adapter->config_bands = config_bands;
}
ret = mwifiex_check_network_compatibility(priv, bss_desc);
if (ret)
goto done;
......@@ -496,8 +506,11 @@ int mwifiex_enable_hs(struct mwifiex_adapter *adapter)
return false;
}
wait_event_interruptible(adapter->hs_activate_wait_q,
adapter->hs_activate_wait_q_woken);
if (wait_event_interruptible(adapter->hs_activate_wait_q,
adapter->hs_activate_wait_q_woken)) {
dev_err(adapter->dev, "hs_activate_wait_q terminated\n");
return false;
}
return true;
}
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -692,7 +692,7 @@ u8 rtl92c_phy_sw_chnl(struct ieee80211_hw *hw)
if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) {
rtl92c_phy_sw_chnl_callback(hw);
RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD,
"sw_chnl_inprogress false schdule workitem\n");
"sw_chnl_inprogress false schedule workitem\n");
rtlphy->sw_chnl_inprogress = false;
} else {
RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD,
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册