1
0
Fork 0

Merge branch 'wl12xx-next' into for-linville

wifi-calibration
Luciano Coelho 2012-06-08 16:43:19 +03:00
commit bf34e68352
46 changed files with 4819 additions and 957 deletions

View File

@ -8,6 +8,7 @@ menuconfig WL_TI
if WL_TI
source "drivers/net/wireless/ti/wl1251/Kconfig"
source "drivers/net/wireless/ti/wl12xx/Kconfig"
source "drivers/net/wireless/ti/wl18xx/Kconfig"
# keep last for automatic dependencies
source "drivers/net/wireless/ti/wlcore/Kconfig"

View File

@ -2,3 +2,4 @@ obj-$(CONFIG_WLCORE) += wlcore/
obj-$(CONFIG_WL12XX) += wl12xx/
obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wlcore/
obj-$(CONFIG_WL1251) += wl1251/
obj-$(CONFIG_WL18XX) += wl18xx/

View File

@ -1,3 +1,3 @@
wl12xx-objs = main.o cmd.o acx.o
wl12xx-objs = main.o cmd.o acx.o debugfs.o
obj-$(CONFIG_WL12XX) += wl12xx.o

View File

@ -24,6 +24,21 @@
#define __WL12XX_ACX_H__
#include "../wlcore/wlcore.h"
#include "../wlcore/acx.h"
#define WL12XX_ACX_ALL_EVENTS_VECTOR (WL1271_ACX_INTR_WATCHDOG | \
WL1271_ACX_INTR_INIT_COMPLETE | \
WL1271_ACX_INTR_EVENT_A | \
WL1271_ACX_INTR_EVENT_B | \
WL1271_ACX_INTR_CMD_COMPLETE | \
WL1271_ACX_INTR_HW_AVAILABLE | \
WL1271_ACX_INTR_DATA)
#define WL12XX_INTR_MASK (WL1271_ACX_INTR_WATCHDOG | \
WL1271_ACX_INTR_EVENT_A | \
WL1271_ACX_INTR_EVENT_B | \
WL1271_ACX_INTR_HW_AVAILABLE | \
WL1271_ACX_INTR_DATA)
struct wl1271_acx_host_config_bitmap {
struct acx_header header;
@ -31,6 +46,228 @@ struct wl1271_acx_host_config_bitmap {
__le32 host_cfg_bitmap;
} __packed;
struct wl12xx_acx_tx_statistics {
__le32 internal_desc_overflow;
} __packed;
struct wl12xx_acx_rx_statistics {
__le32 out_of_mem;
__le32 hdr_overflow;
__le32 hw_stuck;
__le32 dropped;
__le32 fcs_err;
__le32 xfr_hint_trig;
__le32 path_reset;
__le32 reset_counter;
} __packed;
struct wl12xx_acx_dma_statistics {
__le32 rx_requested;
__le32 rx_errors;
__le32 tx_requested;
__le32 tx_errors;
} __packed;
struct wl12xx_acx_isr_statistics {
/* host command complete */
__le32 cmd_cmplt;
/* fiqisr() */
__le32 fiqs;
/* (INT_STS_ND & INT_TRIG_RX_HEADER) */
__le32 rx_headers;
/* (INT_STS_ND & INT_TRIG_RX_CMPLT) */
__le32 rx_completes;
/* (INT_STS_ND & INT_TRIG_NO_RX_BUF) */
__le32 rx_mem_overflow;
/* (INT_STS_ND & INT_TRIG_S_RX_RDY) */
__le32 rx_rdys;
/* irqisr() */
__le32 irqs;
/* (INT_STS_ND & INT_TRIG_TX_PROC) */
__le32 tx_procs;
/* (INT_STS_ND & INT_TRIG_DECRYPT_DONE) */
__le32 decrypt_done;
/* (INT_STS_ND & INT_TRIG_DMA0) */
__le32 dma0_done;
/* (INT_STS_ND & INT_TRIG_DMA1) */
__le32 dma1_done;
/* (INT_STS_ND & INT_TRIG_TX_EXC_CMPLT) */
__le32 tx_exch_complete;
/* (INT_STS_ND & INT_TRIG_COMMAND) */
__le32 commands;
/* (INT_STS_ND & INT_TRIG_RX_PROC) */
__le32 rx_procs;
/* (INT_STS_ND & INT_TRIG_PM_802) */
__le32 hw_pm_mode_changes;
/* (INT_STS_ND & INT_TRIG_ACKNOWLEDGE) */
__le32 host_acknowledges;
/* (INT_STS_ND & INT_TRIG_PM_PCI) */
__le32 pci_pm;
/* (INT_STS_ND & INT_TRIG_ACM_WAKEUP) */
__le32 wakeups;
/* (INT_STS_ND & INT_TRIG_LOW_RSSI) */
__le32 low_rssi;
} __packed;
struct wl12xx_acx_wep_statistics {
/* WEP address keys configured */
__le32 addr_key_count;
/* default keys configured */
__le32 default_key_count;
__le32 reserved;
/* number of times that WEP key not found on lookup */
__le32 key_not_found;
/* number of times that WEP key decryption failed */
__le32 decrypt_fail;
/* WEP packets decrypted */
__le32 packets;
/* WEP decrypt interrupts */
__le32 interrupt;
} __packed;
#define ACX_MISSED_BEACONS_SPREAD 10
struct wl12xx_acx_pwr_statistics {
/* the amount of enters into power save mode (both PD & ELP) */
__le32 ps_enter;
/* the amount of enters into ELP mode */
__le32 elp_enter;
/* the amount of missing beacon interrupts to the host */
__le32 missing_bcns;
/* the amount of wake on host-access times */
__le32 wake_on_host;
/* the amount of wake on timer-expire */
__le32 wake_on_timer_exp;
/* the number of packets that were transmitted with PS bit set */
__le32 tx_with_ps;
/* the number of packets that were transmitted with PS bit clear */
__le32 tx_without_ps;
/* the number of received beacons */
__le32 rcvd_beacons;
/* the number of entering into PowerOn (power save off) */
__le32 power_save_off;
/* the number of entries into power save mode */
__le16 enable_ps;
/*
* the number of exits from power save, not including failed PS
* transitions
*/
__le16 disable_ps;
/*
* the number of times the TSF counter was adjusted because
* of drift
*/
__le32 fix_tsf_ps;
/* Gives statistics about the spread continuous missed beacons.
* The 16 LSB are dedicated for the PS mode.
* The 16 MSB are dedicated for the PS mode.
* cont_miss_bcns_spread[0] - single missed beacon.
* cont_miss_bcns_spread[1] - two continuous missed beacons.
* cont_miss_bcns_spread[2] - three continuous missed beacons.
* ...
* cont_miss_bcns_spread[9] - ten and more continuous missed beacons.
*/
__le32 cont_miss_bcns_spread[ACX_MISSED_BEACONS_SPREAD];
/* the number of beacons in awake mode */
__le32 rcvd_awake_beacons;
} __packed;
struct wl12xx_acx_mic_statistics {
__le32 rx_pkts;
__le32 calc_failure;
} __packed;
struct wl12xx_acx_aes_statistics {
__le32 encrypt_fail;
__le32 decrypt_fail;
__le32 encrypt_packets;
__le32 decrypt_packets;
__le32 encrypt_interrupt;
__le32 decrypt_interrupt;
} __packed;
struct wl12xx_acx_event_statistics {
__le32 heart_beat;
__le32 calibration;
__le32 rx_mismatch;
__le32 rx_mem_empty;
__le32 rx_pool;
__le32 oom_late;
__le32 phy_transmit_error;
__le32 tx_stuck;
} __packed;
struct wl12xx_acx_ps_statistics {
__le32 pspoll_timeouts;
__le32 upsd_timeouts;
__le32 upsd_max_sptime;
__le32 upsd_max_apturn;
__le32 pspoll_max_apturn;
__le32 pspoll_utilization;
__le32 upsd_utilization;
} __packed;
struct wl12xx_acx_rxpipe_statistics {
__le32 rx_prep_beacon_drop;
__le32 descr_host_int_trig_rx_data;
__le32 beacon_buffer_thres_host_int_trig_rx_data;
__le32 missed_beacon_host_int_trig_rx_data;
__le32 tx_xfr_host_int_trig_rx_data;
} __packed;
struct wl12xx_acx_statistics {
struct acx_header header;
struct wl12xx_acx_tx_statistics tx;
struct wl12xx_acx_rx_statistics rx;
struct wl12xx_acx_dma_statistics dma;
struct wl12xx_acx_isr_statistics isr;
struct wl12xx_acx_wep_statistics wep;
struct wl12xx_acx_pwr_statistics pwr;
struct wl12xx_acx_aes_statistics aes;
struct wl12xx_acx_mic_statistics mic;
struct wl12xx_acx_event_statistics event;
struct wl12xx_acx_ps_statistics ps;
struct wl12xx_acx_rxpipe_statistics rxpipe;
} __packed;
int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap);
#endif /* __WL12XX_ACX_H__ */

View File

@ -65,6 +65,7 @@ int wl1271_cmd_general_parms(struct wl1271 *wl)
struct wl1271_general_parms_cmd *gen_parms;
struct wl1271_ini_general_params *gp =
&((struct wl1271_nvs_file *)wl->nvs)->general_params;
struct wl12xx_priv *priv = wl->priv;
bool answer = false;
int ret;
@ -88,7 +89,7 @@ int wl1271_cmd_general_parms(struct wl1271 *wl)
answer = true;
/* Override the REF CLK from the NVS with the one from platform data */
gen_parms->general_params.ref_clock = wl->ref_clock;
gen_parms->general_params.ref_clock = priv->ref_clock;
ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer);
if (ret < 0) {
@ -118,6 +119,7 @@ int wl128x_cmd_general_parms(struct wl1271 *wl)
struct wl128x_general_parms_cmd *gen_parms;
struct wl128x_ini_general_params *gp =
&((struct wl128x_nvs_file *)wl->nvs)->general_params;
struct wl12xx_priv *priv = wl->priv;
bool answer = false;
int ret;
@ -141,8 +143,8 @@ int wl128x_cmd_general_parms(struct wl1271 *wl)
answer = true;
/* Replace REF and TCXO CLKs with the ones from platform data */
gen_parms->general_params.ref_clock = wl->ref_clock;
gen_parms->general_params.tcxo_ref_clock = wl->tcxo_clock;
gen_parms->general_params.ref_clock = priv->ref_clock;
gen_parms->general_params.tcxo_ref_clock = priv->tcxo_clock;
ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer);
if (ret < 0) {

View File

@ -0,0 +1,243 @@
/*
* This file is part of wl12xx
*
* Copyright (C) 2009 Nokia Corporation
* Copyright (C) 2011-2012 Texas Instruments
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "../wlcore/debugfs.h"
#include "../wlcore/wlcore.h"
#include "wl12xx.h"
#include "acx.h"
#include "debugfs.h"
#define WL12XX_DEBUGFS_FWSTATS_FILE(a, b, c) \
DEBUGFS_FWSTATS_FILE(a, b, c, wl12xx_acx_statistics)
WL12XX_DEBUGFS_FWSTATS_FILE(tx, internal_desc_overflow, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(rx, out_of_mem, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(rx, hdr_overflow, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(rx, hw_stuck, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(rx, dropped, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(rx, fcs_err, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(rx, xfr_hint_trig, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(rx, path_reset, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(rx, reset_counter, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(dma, rx_requested, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(dma, rx_errors, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(dma, tx_requested, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(dma, tx_errors, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(isr, cmd_cmplt, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(isr, fiqs, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_headers, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_mem_overflow, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_rdys, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(isr, irqs, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(isr, tx_procs, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(isr, decrypt_done, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(isr, dma0_done, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(isr, dma1_done, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(isr, tx_exch_complete, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(isr, commands, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_procs, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(isr, hw_pm_mode_changes, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(isr, host_acknowledges, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(isr, pci_pm, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(isr, wakeups, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(isr, low_rssi, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(wep, addr_key_count, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(wep, default_key_count, "%u");
/* skipping wep.reserved */
WL12XX_DEBUGFS_FWSTATS_FILE(wep, key_not_found, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(wep, decrypt_fail, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(wep, packets, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(wep, interrupt, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(pwr, ps_enter, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(pwr, elp_enter, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(pwr, missing_bcns, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(pwr, wake_on_host, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(pwr, wake_on_timer_exp, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(pwr, tx_with_ps, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(pwr, tx_without_ps, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_beacons, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(pwr, power_save_off, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(pwr, enable_ps, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(pwr, disable_ps, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(pwr, fix_tsf_ps, "%u");
/* skipping cont_miss_bcns_spread for now */
WL12XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_beacons, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(mic, rx_pkts, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(mic, calc_failure, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(aes, encrypt_fail, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(aes, decrypt_fail, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(aes, encrypt_packets, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(aes, decrypt_packets, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(aes, encrypt_interrupt, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(aes, decrypt_interrupt, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(event, heart_beat, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(event, calibration, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(event, rx_mismatch, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(event, rx_mem_empty, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(event, rx_pool, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(event, oom_late, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(event, phy_transmit_error, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(event, tx_stuck, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(ps, pspoll_timeouts, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_timeouts, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_max_sptime, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_max_apturn, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(ps, pspoll_max_apturn, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(ps, pspoll_utilization, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_utilization, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, rx_prep_beacon_drop, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, descr_host_int_trig_rx_data, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, beacon_buffer_thres_host_int_trig_rx_data,
"%u");
WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, missed_beacon_host_int_trig_rx_data, "%u");
WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, tx_xfr_host_int_trig_rx_data, "%u");
int wl12xx_debugfs_add_files(struct wl1271 *wl,
struct dentry *rootdir)
{
int ret = 0;
struct dentry *entry, *stats, *moddir;
moddir = debugfs_create_dir(KBUILD_MODNAME, rootdir);
if (!moddir || IS_ERR(moddir)) {
entry = moddir;
goto err;
}
stats = debugfs_create_dir("fw_stats", moddir);
if (!stats || IS_ERR(stats)) {
entry = stats;
goto err;
}
DEBUGFS_FWSTATS_ADD(tx, internal_desc_overflow);
DEBUGFS_FWSTATS_ADD(rx, out_of_mem);
DEBUGFS_FWSTATS_ADD(rx, hdr_overflow);
DEBUGFS_FWSTATS_ADD(rx, hw_stuck);
DEBUGFS_FWSTATS_ADD(rx, dropped);
DEBUGFS_FWSTATS_ADD(rx, fcs_err);
DEBUGFS_FWSTATS_ADD(rx, xfr_hint_trig);
DEBUGFS_FWSTATS_ADD(rx, path_reset);
DEBUGFS_FWSTATS_ADD(rx, reset_counter);
DEBUGFS_FWSTATS_ADD(dma, rx_requested);
DEBUGFS_FWSTATS_ADD(dma, rx_errors);
DEBUGFS_FWSTATS_ADD(dma, tx_requested);
DEBUGFS_FWSTATS_ADD(dma, tx_errors);
DEBUGFS_FWSTATS_ADD(isr, cmd_cmplt);
DEBUGFS_FWSTATS_ADD(isr, fiqs);
DEBUGFS_FWSTATS_ADD(isr, rx_headers);
DEBUGFS_FWSTATS_ADD(isr, rx_mem_overflow);
DEBUGFS_FWSTATS_ADD(isr, rx_rdys);
DEBUGFS_FWSTATS_ADD(isr, irqs);
DEBUGFS_FWSTATS_ADD(isr, tx_procs);
DEBUGFS_FWSTATS_ADD(isr, decrypt_done);
DEBUGFS_FWSTATS_ADD(isr, dma0_done);
DEBUGFS_FWSTATS_ADD(isr, dma1_done);
DEBUGFS_FWSTATS_ADD(isr, tx_exch_complete);
DEBUGFS_FWSTATS_ADD(isr, commands);
DEBUGFS_FWSTATS_ADD(isr, rx_procs);
DEBUGFS_FWSTATS_ADD(isr, hw_pm_mode_changes);
DEBUGFS_FWSTATS_ADD(isr, host_acknowledges);
DEBUGFS_FWSTATS_ADD(isr, pci_pm);
DEBUGFS_FWSTATS_ADD(isr, wakeups);
DEBUGFS_FWSTATS_ADD(isr, low_rssi);
DEBUGFS_FWSTATS_ADD(wep, addr_key_count);
DEBUGFS_FWSTATS_ADD(wep, default_key_count);
/* skipping wep.reserved */
DEBUGFS_FWSTATS_ADD(wep, key_not_found);
DEBUGFS_FWSTATS_ADD(wep, decrypt_fail);
DEBUGFS_FWSTATS_ADD(wep, packets);
DEBUGFS_FWSTATS_ADD(wep, interrupt);
DEBUGFS_FWSTATS_ADD(pwr, ps_enter);
DEBUGFS_FWSTATS_ADD(pwr, elp_enter);
DEBUGFS_FWSTATS_ADD(pwr, missing_bcns);
DEBUGFS_FWSTATS_ADD(pwr, wake_on_host);
DEBUGFS_FWSTATS_ADD(pwr, wake_on_timer_exp);
DEBUGFS_FWSTATS_ADD(pwr, tx_with_ps);
DEBUGFS_FWSTATS_ADD(pwr, tx_without_ps);
DEBUGFS_FWSTATS_ADD(pwr, rcvd_beacons);
DEBUGFS_FWSTATS_ADD(pwr, power_save_off);
DEBUGFS_FWSTATS_ADD(pwr, enable_ps);
DEBUGFS_FWSTATS_ADD(pwr, disable_ps);
DEBUGFS_FWSTATS_ADD(pwr, fix_tsf_ps);
/* skipping cont_miss_bcns_spread for now */
DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_beacons);
DEBUGFS_FWSTATS_ADD(mic, rx_pkts);
DEBUGFS_FWSTATS_ADD(mic, calc_failure);
DEBUGFS_FWSTATS_ADD(aes, encrypt_fail);
DEBUGFS_FWSTATS_ADD(aes, decrypt_fail);
DEBUGFS_FWSTATS_ADD(aes, encrypt_packets);
DEBUGFS_FWSTATS_ADD(aes, decrypt_packets);
DEBUGFS_FWSTATS_ADD(aes, encrypt_interrupt);
DEBUGFS_FWSTATS_ADD(aes, decrypt_interrupt);
DEBUGFS_FWSTATS_ADD(event, heart_beat);
DEBUGFS_FWSTATS_ADD(event, calibration);
DEBUGFS_FWSTATS_ADD(event, rx_mismatch);
DEBUGFS_FWSTATS_ADD(event, rx_mem_empty);
DEBUGFS_FWSTATS_ADD(event, rx_pool);
DEBUGFS_FWSTATS_ADD(event, oom_late);
DEBUGFS_FWSTATS_ADD(event, phy_transmit_error);
DEBUGFS_FWSTATS_ADD(event, tx_stuck);
DEBUGFS_FWSTATS_ADD(ps, pspoll_timeouts);
DEBUGFS_FWSTATS_ADD(ps, upsd_timeouts);
DEBUGFS_FWSTATS_ADD(ps, upsd_max_sptime);
DEBUGFS_FWSTATS_ADD(ps, upsd_max_apturn);
DEBUGFS_FWSTATS_ADD(ps, pspoll_max_apturn);
DEBUGFS_FWSTATS_ADD(ps, pspoll_utilization);
DEBUGFS_FWSTATS_ADD(ps, upsd_utilization);
DEBUGFS_FWSTATS_ADD(rxpipe, rx_prep_beacon_drop);
DEBUGFS_FWSTATS_ADD(rxpipe, descr_host_int_trig_rx_data);
DEBUGFS_FWSTATS_ADD(rxpipe, beacon_buffer_thres_host_int_trig_rx_data);
DEBUGFS_FWSTATS_ADD(rxpipe, missed_beacon_host_int_trig_rx_data);
DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data);
return 0;
err:
if (IS_ERR(entry))
ret = PTR_ERR(entry);
else
ret = -ENOMEM;
return ret;
}

View File

@ -0,0 +1,28 @@
/*
* This file is part of wl12xx
*
* Copyright (C) 2012 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL12XX_DEBUGFS_H__
#define __WL12XX_DEBUGFS_H__
int wl12xx_debugfs_add_files(struct wl1271 *wl,
struct dentry *rootdir);
#endif /* __WL12XX_DEBUGFS_H__ */

View File

@ -39,6 +39,10 @@
#include "reg.h"
#include "cmd.h"
#include "acx.h"
#include "debugfs.h"
static char *fref_param;
static char *tcxo_param;
static struct wlcore_conf wl12xx_conf = {
.sg = {
@ -212,7 +216,7 @@ static struct wlcore_conf wl12xx_conf = {
.suspend_wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM,
.suspend_listen_interval = 3,
.bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED,
.bcn_filt_ie_count = 2,
.bcn_filt_ie_count = 3,
.bcn_filt_ie = {
[0] = {
.ie = WLAN_EID_CHANNEL_SWITCH,
@ -222,9 +226,13 @@ static struct wlcore_conf wl12xx_conf = {
.ie = WLAN_EID_HT_OPERATION,
.rule = CONF_BCN_RULE_PASS_ON_CHANGE,
},
[2] = {
.ie = WLAN_EID_ERP_INFO,
.rule = CONF_BCN_RULE_PASS_ON_CHANGE,
},
},
.synch_fail_thold = 10,
.bss_lose_timeout = 100,
.synch_fail_thold = 12,
.bss_lose_timeout = 400,
.beacon_rx_timeout = 10000,
.broadcast_timeout = 20000,
.rx_broadcast_in_ps = 1,
@ -234,7 +242,7 @@ static struct wlcore_conf wl12xx_conf = {
.psm_entry_retries = 8,
.psm_exit_retries = 16,
.psm_entry_nullfunc_retries = 3,
.dynamic_ps_timeout = 40,
.dynamic_ps_timeout = 200,
.forced_ps = false,
.keep_alive_interval = 55000,
.max_listen_interval = 20,
@ -245,7 +253,7 @@ static struct wlcore_conf wl12xx_conf = {
},
.pm_config = {
.host_clk_settling_time = 5000,
.host_fast_wakeup_support = false
.host_fast_wakeup_support = CONF_FAST_WAKEUP_DISABLE,
},
.roam_trigger = {
.trigger_pacing = 1,
@ -305,8 +313,8 @@ static struct wlcore_conf wl12xx_conf = {
.swallow_period = 5,
.n_divider_fref_set_1 = 0xff, /* default */
.n_divider_fref_set_2 = 12,
.m_divider_fref_set_1 = 148,
.m_divider_fref_set_2 = 0xffff, /* default */
.m_divider_fref_set_1 = 0xffff,
.m_divider_fref_set_2 = 148, /* default */
.coex_pll_stabilization_time = 0xffffffff, /* default */
.ldo_stabilization_time = 0xffff, /* default */
.fm_disturbed_band_margin = 0xff, /* default */
@ -593,7 +601,7 @@ static void wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)
{
if (wl->chip.id != CHIP_ID_1283_PG20) {
struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map;
struct wl1271_rx_mem_pool_addr rx_mem_addr;
struct wl127x_rx_mem_pool_addr rx_mem_addr;
/*
* Choose the block we want to read
@ -621,10 +629,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",
wl->chip.id);
/* clear the alignment quirk, since we don't support it */
wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
wl->quirks |= WLCORE_QUIRK_LEGACY_NVS;
wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
WLCORE_QUIRK_TKIP_HEADER_SPACE;
wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
wl->mr_fw_name = WL127X_FW_NAME_MULTI;
memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x,
@ -639,10 +645,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",
wl->chip.id);
/* clear the alignment quirk, since we don't support it */
wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
wl->quirks |= WLCORE_QUIRK_LEGACY_NVS;
wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
WLCORE_QUIRK_TKIP_HEADER_SPACE;
wl->plt_fw_name = WL127X_PLT_FW_NAME;
wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
wl->mr_fw_name = WL127X_FW_NAME_MULTI;
@ -660,6 +664,11 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
wl->plt_fw_name = WL128X_PLT_FW_NAME;
wl->sr_fw_name = WL128X_FW_NAME_SINGLE;
wl->mr_fw_name = WL128X_FW_NAME_MULTI;
/* wl128x requires TX blocksize alignment */
wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
WLCORE_QUIRK_TKIP_HEADER_SPACE;
break;
case CHIP_ID_1283_PG10:
default:
@ -773,6 +782,7 @@ static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk)
u16 spare_reg;
u16 pll_config;
u8 input_freq;
struct wl12xx_priv *priv = wl->priv;
/* Mask bits [3:1] in the sys_clk_cfg register */
spare_reg = wl12xx_top_reg_read(wl, WL_SPARE_REG);
@ -782,8 +792,8 @@ static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk)
wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg);
/* Handle special cases of the TCXO clock */
if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_8 ||
wl->tcxo_clock == WL12XX_TCXOCLOCK_33_6)
if (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_8 ||
priv->tcxo_clock == WL12XX_TCXOCLOCK_33_6)
return wl128x_manually_configure_mcs_pll(wl);
/* Set the input frequency according to the selected clock source */
@ -808,11 +818,12 @@ static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk)
*/
static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock)
{
struct wl12xx_priv *priv = wl->priv;
u16 sys_clk_cfg;
/* For XTAL-only modes, FREF will be used after switching from TCXO */
if (wl->ref_clock == WL12XX_REFCLOCK_26_XTAL ||
wl->ref_clock == WL12XX_REFCLOCK_38_XTAL) {
if (priv->ref_clock == WL12XX_REFCLOCK_26_XTAL ||
priv->ref_clock == WL12XX_REFCLOCK_38_XTAL) {
if (!wl128x_switch_tcxo_to_fref(wl))
return -EINVAL;
goto fref_clk;
@ -826,8 +837,8 @@ static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock)
goto fref_clk;
/* If TCXO is either 32.736MHz or 16.368MHz, switch to FREF */
if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_368 ||
wl->tcxo_clock == WL12XX_TCXOCLOCK_32_736) {
if (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_368 ||
priv->tcxo_clock == WL12XX_TCXOCLOCK_32_736) {
if (!wl128x_switch_tcxo_to_fref(wl))
return -EINVAL;
goto fref_clk;
@ -836,14 +847,14 @@ static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock)
/* TCXO clock is selected */
if (!wl128x_is_tcxo_valid(wl))
return -EINVAL;
*selected_clock = wl->tcxo_clock;
*selected_clock = priv->tcxo_clock;
goto config_mcs_pll;
fref_clk:
/* FREF clock is selected */
if (!wl128x_is_fref_valid(wl))
return -EINVAL;
*selected_clock = wl->ref_clock;
*selected_clock = priv->ref_clock;
config_mcs_pll:
return wl128x_configure_mcs_pll(wl, *selected_clock);
@ -851,25 +862,27 @@ config_mcs_pll:
static int wl127x_boot_clk(struct wl1271 *wl)
{
struct wl12xx_priv *priv = wl->priv;
u32 pause;
u32 clk;
if (WL127X_PG_GET_MAJOR(wl->hw_pg_ver) < 3)
wl->quirks |= WLCORE_QUIRK_END_OF_TRANSACTION;
if (wl->ref_clock == CONF_REF_CLK_19_2_E ||
wl->ref_clock == CONF_REF_CLK_38_4_E ||
wl->ref_clock == CONF_REF_CLK_38_4_M_XTAL)
if (priv->ref_clock == CONF_REF_CLK_19_2_E ||
priv->ref_clock == CONF_REF_CLK_38_4_E ||
priv->ref_clock == CONF_REF_CLK_38_4_M_XTAL)
/* ref clk: 19.2/38.4/38.4-XTAL */
clk = 0x3;
else if (wl->ref_clock == CONF_REF_CLK_26_E ||
wl->ref_clock == CONF_REF_CLK_52_E)
else if (priv->ref_clock == CONF_REF_CLK_26_E ||
priv->ref_clock == CONF_REF_CLK_26_M_XTAL ||
priv->ref_clock == CONF_REF_CLK_52_E)
/* ref clk: 26/52 */
clk = 0x5;
else
return -EINVAL;
if (wl->ref_clock != CONF_REF_CLK_19_2_E) {
if (priv->ref_clock != CONF_REF_CLK_19_2_E) {
u16 val;
/* Set clock type (open drain) */
val = wl12xx_top_reg_read(wl, OCP_REG_CLK_TYPE);
@ -939,6 +952,7 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl)
static int wl12xx_pre_boot(struct wl1271 *wl)
{
struct wl12xx_priv *priv = wl->priv;
int ret = 0;
u32 clk;
int selected_clock = -1;
@ -970,7 +984,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl)
if (wl->chip.id == CHIP_ID_1283_PG20)
clk |= ((selected_clock & 0x3) << 1) << 4;
else
clk |= (wl->ref_clock << 1) << 4;
clk |= (priv->ref_clock << 1) << 4;
wl1271_write32(wl, WL12XX_DRPW_SCRATCH_START, clk);
@ -989,7 +1003,7 @@ out:
static void wl12xx_pre_upload(struct wl1271 *wl)
{
u32 tmp;
u32 tmp, polarity;
/* write firmware's last address (ie. it's length) to
* ACX_EEPROMLESS_IND_REG */
@ -1009,23 +1023,23 @@ static void wl12xx_pre_upload(struct wl1271 *wl)
if (wl->chip.id == CHIP_ID_1283_PG20)
wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA);
}
static void wl12xx_enable_interrupts(struct wl1271 *wl)
{
u32 polarity;
/* polarity must be set before the firmware is loaded */
polarity = wl12xx_top_reg_read(wl, OCP_REG_POLARITY);
/* We use HIGH polarity, so unset the LOW bit */
polarity &= ~POLARITY_LOW;
wl12xx_top_reg_write(wl, OCP_REG_POLARITY, polarity);
wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_ALL_EVENTS_VECTOR);
}
static void wl12xx_enable_interrupts(struct wl1271 *wl)
{
wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL12XX_ACX_ALL_EVENTS_VECTOR);
wlcore_enable_interrupts(wl);
wlcore_write_reg(wl, REG_INTERRUPT_MASK,
WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK));
WL1271_ACX_INTR_ALL & ~(WL12XX_INTR_MASK));
wl1271_write32(wl, WL12XX_HI_CFG, HI_CFG_DEF_VAL);
}
@ -1149,7 +1163,8 @@ static u32 wl12xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data,
static void wl12xx_tx_delayed_compl(struct wl1271 *wl)
{
if (wl->fw_status->tx_results_counter == (wl->tx_results_count & 0xff))
if (wl->fw_status_1->tx_results_counter ==
(wl->tx_results_count & 0xff))
return;
wl1271_tx_complete(wl);
@ -1288,10 +1303,90 @@ static void wl12xx_get_mac(struct wl1271 *wl)
wl12xx_get_fuse_mac(wl);
}
static void wl12xx_set_tx_desc_csum(struct wl1271 *wl,
struct wl1271_tx_hw_descr *desc,
struct sk_buff *skb)
{
desc->wl12xx_reserved = 0;
}
static int wl12xx_plt_init(struct wl1271 *wl)
{
int ret;
ret = wl->ops->boot(wl);
if (ret < 0)
goto out;
ret = wl->ops->hw_init(wl);
if (ret < 0)
goto out_irq_disable;
ret = wl1271_acx_init_mem_config(wl);
if (ret < 0)
goto out_irq_disable;
ret = wl12xx_acx_mem_cfg(wl);
if (ret < 0)
goto out_free_memmap;
/* Enable data path */
ret = wl1271_cmd_data_path(wl, 1);
if (ret < 0)
goto out_free_memmap;
/* Configure for CAM power saving (ie. always active) */
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
if (ret < 0)
goto out_free_memmap;
/* configure PM */
ret = wl1271_acx_pm_config(wl);
if (ret < 0)
goto out_free_memmap;
goto out;
out_free_memmap:
kfree(wl->target_mem_map);
wl->target_mem_map = NULL;
out_irq_disable:
mutex_unlock(&wl->mutex);
/* Unlocking the mutex in the middle of handling is
inherently unsafe. In this case we deem it safe to do,
because we need to let any possibly pending IRQ out of
the system (and while we are WL1271_STATE_OFF the IRQ
work function will not do anything.) Also, any other
possible concurrent operations will fail due to the
current state, hence the wl1271 struct should be safe. */
wlcore_disable_interrupts(wl);
mutex_lock(&wl->mutex);
out:
return ret;
}
static int wl12xx_get_spare_blocks(struct wl1271 *wl, bool is_gem)
{
if (is_gem)
return WL12XX_TX_HW_BLOCK_GEM_SPARE;
return WL12XX_TX_HW_BLOCK_SPARE_DEFAULT;
}
static int wl12xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key_conf)
{
return wlcore_set_key(wl, cmd, vif, sta, key_conf);
}
static struct wlcore_ops wl12xx_ops = {
.identify_chip = wl12xx_identify_chip,
.identify_fw = wl12xx_identify_fw,
.boot = wl12xx_boot,
.plt_init = wl12xx_plt_init,
.trigger_cmd = wl12xx_trigger_cmd,
.ack_event = wl12xx_ack_event,
.calc_tx_blocks = wl12xx_calc_tx_blocks,
@ -1306,6 +1401,13 @@ static struct wlcore_ops wl12xx_ops = {
.sta_get_ap_rate_mask = wl12xx_sta_get_ap_rate_mask,
.get_pg_ver = wl12xx_get_pg_ver,
.get_mac = wl12xx_get_mac,
.set_tx_desc_csum = wl12xx_set_tx_desc_csum,
.set_rx_csum = NULL,
.ap_get_mimo_wide_rate_mask = NULL,
.debugfs_init = wl12xx_debugfs_add_files,
.get_spare_blocks = wl12xx_get_spare_blocks,
.set_key = wl12xx_set_key,
.pre_pkt_send = NULL,
};
static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
@ -1323,6 +1425,7 @@ static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
static int __devinit wl12xx_probe(struct platform_device *pdev)
{
struct wl12xx_platform_data *pdata = pdev->dev.platform_data;
struct wl1271 *wl;
struct ieee80211_hw *hw;
struct wl12xx_priv *priv;
@ -1334,19 +1437,65 @@ static int __devinit wl12xx_probe(struct platform_device *pdev)
}
wl = hw->priv;
priv = wl->priv;
wl->ops = &wl12xx_ops;
wl->ptable = wl12xx_ptable;
wl->rtable = wl12xx_rtable;
wl->num_tx_desc = 16;
wl->normal_tx_spare = WL12XX_TX_HW_BLOCK_SPARE_DEFAULT;
wl->gem_tx_spare = WL12XX_TX_HW_BLOCK_GEM_SPARE;
wl->num_rx_desc = 8;
wl->band_rate_to_idx = wl12xx_band_rate_to_idx;
wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX;
wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0;
wl->fw_status_priv_len = 0;
memcpy(&wl->ht_cap, &wl12xx_ht_cap, sizeof(wl12xx_ht_cap));
wl->stats.fw_stats_len = sizeof(struct wl12xx_acx_statistics);
memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ], &wl12xx_ht_cap,
sizeof(wl12xx_ht_cap));
memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ], &wl12xx_ht_cap,
sizeof(wl12xx_ht_cap));
wl12xx_conf_init(wl);
if (!fref_param) {
priv->ref_clock = pdata->board_ref_clock;
} else {
if (!strcmp(fref_param, "19.2"))
priv->ref_clock = WL12XX_REFCLOCK_19;
else if (!strcmp(fref_param, "26"))
priv->ref_clock = WL12XX_REFCLOCK_26;
else if (!strcmp(fref_param, "26x"))
priv->ref_clock = WL12XX_REFCLOCK_26_XTAL;
else if (!strcmp(fref_param, "38.4"))
priv->ref_clock = WL12XX_REFCLOCK_38;
else if (!strcmp(fref_param, "38.4x"))
priv->ref_clock = WL12XX_REFCLOCK_38_XTAL;
else if (!strcmp(fref_param, "52"))
priv->ref_clock = WL12XX_REFCLOCK_52;
else
wl1271_error("Invalid fref parameter %s", fref_param);
}
if (!tcxo_param) {
priv->tcxo_clock = pdata->board_tcxo_clock;
} else {
if (!strcmp(tcxo_param, "19.2"))
priv->tcxo_clock = WL12XX_TCXOCLOCK_19_2;
else if (!strcmp(tcxo_param, "26"))
priv->tcxo_clock = WL12XX_TCXOCLOCK_26;
else if (!strcmp(tcxo_param, "38.4"))
priv->tcxo_clock = WL12XX_TCXOCLOCK_38_4;
else if (!strcmp(tcxo_param, "52"))
priv->tcxo_clock = WL12XX_TCXOCLOCK_52;
else if (!strcmp(tcxo_param, "16.368"))
priv->tcxo_clock = WL12XX_TCXOCLOCK_16_368;
else if (!strcmp(tcxo_param, "32.736"))
priv->tcxo_clock = WL12XX_TCXOCLOCK_32_736;
else if (!strcmp(tcxo_param, "16.8"))
priv->tcxo_clock = WL12XX_TCXOCLOCK_16_8;
else if (!strcmp(tcxo_param, "33.6"))
priv->tcxo_clock = WL12XX_TCXOCLOCK_33_6;
else
wl1271_error("Invalid tcxo parameter %s", tcxo_param);
}
return wlcore_probe(wl, pdev);
}
@ -1378,6 +1527,13 @@ static void __exit wl12xx_exit(void)
}
module_exit(wl12xx_exit);
module_param_named(fref, fref_param, charp, 0);
MODULE_PARM_DESC(fref, "FREF clock: 19.2, 26, 26x, 38.4, 38.4x, 52");
module_param_named(tcxo, tcxo_param, charp, 0);
MODULE_PARM_DESC(tcxo,
"TCXO clock: 19.2, 26, 38.4, 52, 16.368, 32.736, 16.8, 33.6");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE);

View File

@ -24,8 +24,16 @@
#include "conf.h"
struct wl127x_rx_mem_pool_addr {
u32 addr;
u32 addr_extra;
};
struct wl12xx_priv {
struct wl12xx_priv_conf conf;
int ref_clock;
int tcxo_clock;
};
#endif /* __WL12XX_PRIV_H__ */

View File

@ -0,0 +1,7 @@
config WL18XX
tristate "TI wl18xx support"
depends on MAC80211
select WLCORE
---help---
This module adds support for wireless adapters based on TI
WiLink 8 chipsets.

View File

@ -0,0 +1,3 @@
wl18xx-objs = main.o acx.o tx.o io.o debugfs.o
obj-$(CONFIG_WL18XX) += wl18xx.o

View File

@ -0,0 +1,111 @@
/*
* This file is part of wl18xx
*
* Copyright (C) 2011 Texas Instruments Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "../wlcore/cmd.h"
#include "../wlcore/debug.h"
#include "../wlcore/acx.h"
#include "acx.h"
int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
u32 sdio_blk_size, u32 extra_mem_blks,
u32 len_field_size)
{
struct wl18xx_acx_host_config_bitmap *bitmap_conf;
int ret;
wl1271_debug(DEBUG_ACX, "acx cfg bitmap %d blk %d spare %d field %d",
host_cfg_bitmap, sdio_blk_size, extra_mem_blks,
len_field_size);
bitmap_conf = kzalloc(sizeof(*bitmap_conf), GFP_KERNEL);
if (!bitmap_conf) {
ret = -ENOMEM;
goto out;
}
bitmap_conf->host_cfg_bitmap = cpu_to_le32(host_cfg_bitmap);
bitmap_conf->host_sdio_block_size = cpu_to_le32(sdio_blk_size);
bitmap_conf->extra_mem_blocks = cpu_to_le32(extra_mem_blks);
bitmap_conf->length_field_size = cpu_to_le32(len_field_size);
ret = wl1271_cmd_configure(wl, ACX_HOST_IF_CFG_BITMAP,
bitmap_conf, sizeof(*bitmap_conf));
if (ret < 0) {
wl1271_warning("wl1271 bitmap config opt failed: %d", ret);
goto out;
}
out:
kfree(bitmap_conf);
return ret;
}
int wl18xx_acx_set_checksum_state(struct wl1271 *wl)
{
struct wl18xx_acx_checksum_state *acx;
int ret;
wl1271_debug(DEBUG_ACX, "acx checksum state");
acx = kzalloc(sizeof(*acx), GFP_KERNEL);
if (!acx) {
ret = -ENOMEM;
goto out;
}
acx->checksum_state = CHECKSUM_OFFLOAD_ENABLED;
ret = wl1271_cmd_configure(wl, ACX_CHECKSUM_CONFIG, acx, sizeof(*acx));
if (ret < 0) {
wl1271_warning("failed to set Tx checksum state: %d", ret);
goto out;
}
out:
kfree(acx);
return ret;
}
int wl18xx_acx_clear_statistics(struct wl1271 *wl)
{
struct wl18xx_acx_clear_statistics *acx;
int ret = 0;
wl1271_debug(DEBUG_ACX, "acx clear statistics");
acx = kzalloc(sizeof(*acx), GFP_KERNEL);
if (!acx) {
ret = -ENOMEM;
goto out;
}
ret = wl1271_cmd_configure(wl, ACX_CLEAR_STATISTICS, acx, sizeof(*acx));
if (ret < 0) {
wl1271_warning("failed to clear firmware statistics: %d", ret);
goto out;
}
out:
kfree(acx);
return ret;
}

View File

@ -0,0 +1,291 @@
/*
* This file is part of wl18xx
*
* Copyright (C) 2011 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL18XX_ACX_H__
#define __WL18XX_ACX_H__
#include "../wlcore/wlcore.h"
#include "../wlcore/acx.h"
enum {
ACX_CLEAR_STATISTICS = 0x0047,
};
/* numbers of bits the length field takes (add 1 for the actual number) */
#define WL18XX_HOST_IF_LEN_SIZE_FIELD 15
#define WL18XX_ACX_EVENTS_VECTOR_PG1 (WL1271_ACX_INTR_WATCHDOG | \
WL1271_ACX_INTR_INIT_COMPLETE | \
WL1271_ACX_INTR_EVENT_A | \
WL1271_ACX_INTR_EVENT_B | \
WL1271_ACX_INTR_CMD_COMPLETE | \
WL1271_ACX_INTR_HW_AVAILABLE | \
WL1271_ACX_INTR_DATA)
#define WL18XX_ACX_EVENTS_VECTOR_PG2 (WL18XX_ACX_EVENTS_VECTOR_PG1 | \
WL1271_ACX_SW_INTR_WATCHDOG)
#define WL18XX_INTR_MASK_PG1 (WL1271_ACX_INTR_WATCHDOG | \
WL1271_ACX_INTR_EVENT_A | \
WL1271_ACX_INTR_EVENT_B | \
WL1271_ACX_INTR_HW_AVAILABLE | \
WL1271_ACX_INTR_DATA)
#define WL18XX_INTR_MASK_PG2 (WL18XX_INTR_MASK_PG1 | \
WL1271_ACX_SW_INTR_WATCHDOG)
struct wl18xx_acx_host_config_bitmap {
struct acx_header header;
__le32 host_cfg_bitmap;
__le32 host_sdio_block_size;
/* extra mem blocks per frame in TX. */
__le32 extra_mem_blocks;
/*
* number of bits of the length field in the first TX word
* (up to 15 - for using the entire 16 bits).
*/
__le32 length_field_size;
} __packed;
enum {
CHECKSUM_OFFLOAD_DISABLED = 0,
CHECKSUM_OFFLOAD_ENABLED = 1,
CHECKSUM_OFFLOAD_FAKE_RX = 2,
CHECKSUM_OFFLOAD_INVALID = 0xFF
};
struct wl18xx_acx_checksum_state {
struct acx_header header;
/* enum acx_checksum_state */
u8 checksum_state;
u8 pad[3];
} __packed;
struct wl18xx_acx_error_stats {
u32 error_frame;
u32 error_null_Frame_tx_start;
u32 error_numll_frame_cts_start;
u32 error_bar_retry;
u32 error_frame_cts_nul_flid;
} __packed;
struct wl18xx_acx_debug_stats {
u32 debug1;
u32 debug2;
u32 debug3;
u32 debug4;
u32 debug5;
u32 debug6;
} __packed;
struct wl18xx_acx_ring_stats {
u32 prepared_descs;
u32 tx_cmplt;
} __packed;
struct wl18xx_acx_tx_stats {
u32 tx_prepared_descs;
u32 tx_cmplt;
u32 tx_template_prepared;
u32 tx_data_prepared;
u32 tx_template_programmed;
u32 tx_data_programmed;
u32 tx_burst_programmed;
u32 tx_starts;
u32 tx_imm_resp;
u32 tx_start_templates;
u32 tx_start_int_templates;
u32 tx_start_fw_gen;
u32 tx_start_data;
u32 tx_start_null_frame;
u32 tx_exch;
u32 tx_retry_template;
u32 tx_retry_data;
u32 tx_exch_pending;
u32 tx_exch_expiry;
u32 tx_done_template;
u32 tx_done_data;
u32 tx_done_int_template;
u32 tx_frame_checksum;
u32 tx_checksum_result;
u32 frag_called;
u32 frag_mpdu_alloc_failed;
u32 frag_init_called;
u32 frag_in_process_called;
u32 frag_tkip_called;
u32 frag_key_not_found;
u32 frag_need_fragmentation;
u32 frag_bad_mblk_num;
u32 frag_failed;
u32 frag_cache_hit;
u32 frag_cache_miss;
} __packed;
struct wl18xx_acx_rx_stats {
u32 rx_beacon_early_term;
u32 rx_out_of_mpdu_nodes;
u32 rx_hdr_overflow;
u32 rx_dropped_frame;
u32 rx_done_stage;
u32 rx_done;
u32 rx_defrag;
u32 rx_defrag_end;
u32 rx_cmplt;
u32 rx_pre_complt;
u32 rx_cmplt_task;
u32 rx_phy_hdr;
u32 rx_timeout;
u32 rx_timeout_wa;
u32 rx_wa_density_dropped_frame;
u32 rx_wa_ba_not_expected;
u32 rx_frame_checksum;
u32 rx_checksum_result;
u32 defrag_called;
u32 defrag_init_called;
u32 defrag_in_process_called;
u32 defrag_tkip_called;
u32 defrag_need_defrag;
u32 defrag_decrypt_failed;
u32 decrypt_key_not_found;
u32 defrag_need_decrypt;
u32 rx_tkip_replays;
} __packed;
struct wl18xx_acx_isr_stats {
u32 irqs;
} __packed;
#define PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD 10
struct wl18xx_acx_pwr_stats {
u32 missing_bcns_cnt;
u32 rcvd_bcns_cnt;
u32 connection_out_of_sync;
u32 cont_miss_bcns_spread[PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD];
u32 rcvd_awake_bcns_cnt;
} __packed;
struct wl18xx_acx_event_stats {
u32 calibration;
u32 rx_mismatch;
u32 rx_mem_empty;
} __packed;
struct wl18xx_acx_ps_poll_stats {
u32 ps_poll_timeouts;
u32 upsd_timeouts;
u32 upsd_max_ap_turn;
u32 ps_poll_max_ap_turn;
u32 ps_poll_utilization;
u32 upsd_utilization;
} __packed;
struct wl18xx_acx_rx_filter_stats {
u32 beacon_filter;
u32 arp_filter;
u32 mc_filter;
u32 dup_filter;
u32 data_filter;
u32 ibss_filter;
u32 protection_filter;
u32 accum_arp_pend_requests;
u32 max_arp_queue_dep;
} __packed;
struct wl18xx_acx_rx_rate_stats {
u32 rx_frames_per_rates[50];
} __packed;
#define AGGR_STATS_TX_AGG 16
#define AGGR_STATS_TX_RATE 16
#define AGGR_STATS_RX_SIZE_LEN 16
struct wl18xx_acx_aggr_stats {
u32 tx_agg_vs_rate[AGGR_STATS_TX_AGG * AGGR_STATS_TX_RATE];
u32 rx_size[AGGR_STATS_RX_SIZE_LEN];
} __packed;
#define PIPE_STATS_HW_FIFO 11
struct wl18xx_acx_pipeline_stats {
u32 hs_tx_stat_fifo_int;
u32 hs_rx_stat_fifo_int;
u32 tcp_tx_stat_fifo_int;
u32 tcp_rx_stat_fifo_int;
u32 enc_tx_stat_fifo_int;
u32 enc_rx_stat_fifo_int;
u32 rx_complete_stat_fifo_int;
u32 pre_proc_swi;
u32 post_proc_swi;
u32 sec_frag_swi;
u32 pre_to_defrag_swi;
u32 defrag_to_csum_swi;
u32 csum_to_rx_xfer_swi;
u32 dec_packet_in;
u32 dec_packet_in_fifo_full;
u32 dec_packet_out;
u32 cs_rx_packet_in;
u32 cs_rx_packet_out;
u16 pipeline_fifo_full[PIPE_STATS_HW_FIFO];
} __packed;
struct wl18xx_acx_mem_stats {
u32 rx_free_mem_blks;
u32 tx_free_mem_blks;
u32 fwlog_free_mem_blks;
u32 fw_gen_free_mem_blks;
} __packed;
struct wl18xx_acx_statistics {
struct acx_header header;
struct wl18xx_acx_error_stats error;
struct wl18xx_acx_debug_stats debug;
struct wl18xx_acx_tx_stats tx;
struct wl18xx_acx_rx_stats rx;
struct wl18xx_acx_isr_stats isr;
struct wl18xx_acx_pwr_stats pwr;
struct wl18xx_acx_ps_poll_stats ps_poll;
struct wl18xx_acx_rx_filter_stats rx_filter;
struct wl18xx_acx_rx_rate_stats rx_rate;
struct wl18xx_acx_aggr_stats aggr_size;
struct wl18xx_acx_pipeline_stats pipeline;
struct wl18xx_acx_mem_stats mem;
} __packed;
struct wl18xx_acx_clear_statistics {
struct acx_header header;
};
int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
u32 sdio_blk_size, u32 extra_mem_blks,
u32 len_field_size);
int wl18xx_acx_set_checksum_state(struct wl1271 *wl);
int wl18xx_acx_clear_statistics(struct wl1271 *wl);
#endif /* __WL18XX_ACX_H__ */

View File

@ -0,0 +1,92 @@
/*
* This file is part of wl18xx
*
* Copyright (C) 2011 Texas Instruments Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL18XX_CONF_H__
#define __WL18XX_CONF_H__
#define WL18XX_CONF_MAGIC 0x10e100ca
#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0002)
#define WL18XX_CONF_MASK 0x0000ffff
#define WL18XX_CONF_SIZE (WLCORE_CONF_SIZE + \
sizeof(struct wl18xx_priv_conf))
#define NUM_OF_CHANNELS_11_ABG 150
#define NUM_OF_CHANNELS_11_P 7
#define WL18XX_NUM_OF_SUB_BANDS 9
#define SRF_TABLE_LEN 16
#define PIN_MUXING_SIZE 2
struct wl18xx_mac_and_phy_params {
u8 phy_standalone;
u8 rdl;
u8 enable_clpc;
u8 enable_tx_low_pwr_on_siso_rdl;
u8 auto_detect;
u8 dedicated_fem;
u8 low_band_component;
/* Bit 0: One Hot, Bit 1: Control Enable, Bit 2: 1.8V, Bit 3: 3V */
u8 low_band_component_type;
u8 high_band_component;
/* Bit 0: One Hot, Bit 1: Control Enable, Bit 2: 1.8V, Bit 3: 3V */
u8 high_band_component_type;
u8 number_of_assembled_ant2_4;
u8 number_of_assembled_ant5;
u8 pin_muxing_platform_options[PIN_MUXING_SIZE];
u8 external_pa_dc2dc;
u8 tcxo_ldo_voltage;
u8 xtal_itrim_val;
u8 srf_state;
u8 srf1[SRF_TABLE_LEN];
u8 srf2[SRF_TABLE_LEN];
u8 srf3[SRF_TABLE_LEN];
u8 io_configuration;
u8 sdio_configuration;
u8 settings;
u8 rx_profile;
u8 per_chan_pwr_limit_arr_11abg[NUM_OF_CHANNELS_11_ABG];
u8 pwr_limit_reference_11_abg;
u8 per_chan_pwr_limit_arr_11p[NUM_OF_CHANNELS_11_P];
u8 pwr_limit_reference_11p;
u8 per_sub_band_tx_trace_loss[WL18XX_NUM_OF_SUB_BANDS];
u8 per_sub_band_rx_trace_loss[WL18XX_NUM_OF_SUB_BANDS];
u8 primary_clock_setting_time;
u8 clock_valid_on_wake_up;
u8 secondary_clock_setting_time;
u8 board_type;
/* enable point saturation */
u8 psat;
/* low/medium/high Tx power in dBm */
s8 low_power_val;
s8 med_power_val;
s8 high_power_val;
u8 padding[1];
} __packed;
struct wl18xx_priv_conf {
/* this structure is copied wholesale to FW */
struct wl18xx_mac_and_phy_params phy;
} __packed;
#endif /* __WL18XX_CONF_H__ */

View File

@ -0,0 +1,403 @@
/*
* This file is part of wl18xx
*
* Copyright (C) 2009 Nokia Corporation
* Copyright (C) 2011-2012 Texas Instruments
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "../wlcore/debugfs.h"
#include "../wlcore/wlcore.h"
#include "wl18xx.h"
#include "acx.h"
#include "debugfs.h"
#define WL18XX_DEBUGFS_FWSTATS_FILE(a, b, c) \
DEBUGFS_FWSTATS_FILE(a, b, c, wl18xx_acx_statistics)
#define WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(a, b, c) \
DEBUGFS_FWSTATS_FILE_ARRAY(a, b, c, wl18xx_acx_statistics)
WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug1, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug2, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug3, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug4, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug5, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug6, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(error, error_frame, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(error, error_null_Frame_tx_start, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(error, error_numll_frame_cts_start, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(error, error_bar_retry, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(error, error_frame_cts_nul_flid, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_prepared_descs, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_cmplt, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_template_prepared, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_data_prepared, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_template_programmed, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_data_programmed, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_burst_programmed, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_starts, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_imm_resp, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_templates, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_int_templates, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_fw_gen, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_data, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_null_frame, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_retry_template, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_retry_data, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch_pending, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch_expiry, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_template, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_data, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_int_template, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_frame_checksum, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_checksum_result, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_called, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_mpdu_alloc_failed, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_init_called, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_in_process_called, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_tkip_called, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_key_not_found, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_need_fragmentation, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_bad_mblk_num, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_failed, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_cache_hit, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_cache_miss, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_beacon_early_term, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_out_of_mpdu_nodes, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_hdr_overflow, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_dropped_frame, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_done, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_defrag, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_defrag_end, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_cmplt, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_pre_complt, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_cmplt_task, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_phy_hdr, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_timeout, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_timeout_wa, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_wa_density_dropped_frame, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_wa_ba_not_expected, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_frame_checksum, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_checksum_result, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_called, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_init_called, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_in_process_called, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_tkip_called, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_need_defrag, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_decrypt_failed, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, decrypt_key_not_found, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_need_decrypt, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_tkip_replays, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(isr, irqs, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pwr, missing_bcns_cnt, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_bcns_cnt, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pwr, connection_out_of_sync, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(pwr, cont_miss_bcns_spread,
PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD);
WL18XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_bcns_cnt, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, ps_poll_timeouts, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, upsd_timeouts, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, upsd_max_ap_turn, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, ps_poll_max_ap_turn, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, ps_poll_utilization, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, upsd_utilization, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, beacon_filter, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, arp_filter, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, mc_filter, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, dup_filter, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, data_filter, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, ibss_filter, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, protection_filter, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, accum_arp_pend_requests, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, max_arp_queue_dep, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(rx_rate, rx_frames_per_rates, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, tx_agg_vs_rate,
AGGR_STATS_TX_AGG*AGGR_STATS_TX_RATE);
WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, rx_size,
AGGR_STATS_RX_SIZE_LEN);
WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, hs_tx_stat_fifo_int, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, tcp_tx_stat_fifo_int, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, tcp_rx_stat_fifo_int, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, enc_tx_stat_fifo_int, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, enc_rx_stat_fifo_int, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, rx_complete_stat_fifo_int, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, pre_proc_swi, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, post_proc_swi, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, sec_frag_swi, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, pre_to_defrag_swi, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, defrag_to_csum_swi, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, csum_to_rx_xfer_swi, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_in, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_in_fifo_full, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_out, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, cs_rx_packet_in, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, cs_rx_packet_out, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(pipeline, pipeline_fifo_full,
PIPE_STATS_HW_FIFO);
WL18XX_DEBUGFS_FWSTATS_FILE(mem, rx_free_mem_blks, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(mem, tx_free_mem_blks, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(mem, fwlog_free_mem_blks, "%u");
WL18XX_DEBUGFS_FWSTATS_FILE(mem, fw_gen_free_mem_blks, "%u");
static ssize_t conf_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
struct wl18xx_priv *priv = wl->priv;
struct wlcore_conf_header header;
char *buf, *pos;
size_t len;
int ret;
len = WL18XX_CONF_SIZE;
buf = kmalloc(len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
header.magic = cpu_to_le32(WL18XX_CONF_MAGIC);
header.version = cpu_to_le32(WL18XX_CONF_VERSION);
header.checksum = 0;
mutex_lock(&wl->mutex);
pos = buf;
memcpy(pos, &header, sizeof(header));
pos += sizeof(header);
memcpy(pos, &wl->conf, sizeof(wl->conf));
pos += sizeof(wl->conf);
memcpy(pos, &priv->conf, sizeof(priv->conf));
mutex_unlock(&wl->mutex);
ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
kfree(buf);
return ret;
}
static const struct file_operations conf_ops = {
.read = conf_read,
.open = simple_open,
.llseek = default_llseek,
};
static ssize_t clear_fw_stats_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
int ret;
mutex_lock(&wl->mutex);
if (wl->state == WL1271_STATE_OFF)
goto out;
ret = wl18xx_acx_clear_statistics(wl);
if (ret < 0) {
count = ret;
goto out;
}
out:
mutex_unlock(&wl->mutex);
return count;
}
static const struct file_operations clear_fw_stats_ops = {
.write = clear_fw_stats_write,
.open = simple_open,
.llseek = default_llseek,
};
int wl18xx_debugfs_add_files(struct wl1271 *wl,
struct dentry *rootdir)
{
int ret = 0;
struct dentry *entry, *stats, *moddir;
moddir = debugfs_create_dir(KBUILD_MODNAME, rootdir);
if (!moddir || IS_ERR(moddir)) {
entry = moddir;
goto err;
}
stats = debugfs_create_dir("fw_stats", moddir);
if (!stats || IS_ERR(stats)) {
entry = stats;
goto err;
}
DEBUGFS_ADD(clear_fw_stats, stats);
DEBUGFS_FWSTATS_ADD(debug, debug1);
DEBUGFS_FWSTATS_ADD(debug, debug2);
DEBUGFS_FWSTATS_ADD(debug, debug3);
DEBUGFS_FWSTATS_ADD(debug, debug4);
DEBUGFS_FWSTATS_ADD(debug, debug5);
DEBUGFS_FWSTATS_ADD(debug, debug6);
DEBUGFS_FWSTATS_ADD(error, error_frame);
DEBUGFS_FWSTATS_ADD(error, error_null_Frame_tx_start);
DEBUGFS_FWSTATS_ADD(error, error_numll_frame_cts_start);
DEBUGFS_FWSTATS_ADD(error, error_bar_retry);
DEBUGFS_FWSTATS_ADD(error, error_frame_cts_nul_flid);
DEBUGFS_FWSTATS_ADD(tx, tx_prepared_descs);
DEBUGFS_FWSTATS_ADD(tx, tx_cmplt);
DEBUGFS_FWSTATS_ADD(tx, tx_template_prepared);
DEBUGFS_FWSTATS_ADD(tx, tx_data_prepared);
DEBUGFS_FWSTATS_ADD(tx, tx_template_programmed);
DEBUGFS_FWSTATS_ADD(tx, tx_data_programmed);
DEBUGFS_FWSTATS_ADD(tx, tx_burst_programmed);
DEBUGFS_FWSTATS_ADD(tx, tx_starts);
DEBUGFS_FWSTATS_ADD(tx, tx_imm_resp);
DEBUGFS_FWSTATS_ADD(tx, tx_start_templates);
DEBUGFS_FWSTATS_ADD(tx, tx_start_int_templates);
DEBUGFS_FWSTATS_ADD(tx, tx_start_fw_gen);
DEBUGFS_FWSTATS_ADD(tx, tx_start_data);
DEBUGFS_FWSTATS_ADD(tx, tx_start_null_frame);
DEBUGFS_FWSTATS_ADD(tx, tx_exch);
DEBUGFS_FWSTATS_ADD(tx, tx_retry_template);
DEBUGFS_FWSTATS_ADD(tx, tx_retry_data);
DEBUGFS_FWSTATS_ADD(tx, tx_exch_pending);
DEBUGFS_FWSTATS_ADD(tx, tx_exch_expiry);
DEBUGFS_FWSTATS_ADD(tx, tx_done_template);
DEBUGFS_FWSTATS_ADD(tx, tx_done_data);
DEBUGFS_FWSTATS_ADD(tx, tx_done_int_template);
DEBUGFS_FWSTATS_ADD(tx, tx_frame_checksum);
DEBUGFS_FWSTATS_ADD(tx, tx_checksum_result);
DEBUGFS_FWSTATS_ADD(tx, frag_called);
DEBUGFS_FWSTATS_ADD(tx, frag_mpdu_alloc_failed);
DEBUGFS_FWSTATS_ADD(tx, frag_init_called);
DEBUGFS_FWSTATS_ADD(tx, frag_in_process_called);
DEBUGFS_FWSTATS_ADD(tx, frag_tkip_called);
DEBUGFS_FWSTATS_ADD(tx, frag_key_not_found);
DEBUGFS_FWSTATS_ADD(tx, frag_need_fragmentation);
DEBUGFS_FWSTATS_ADD(tx, frag_bad_mblk_num);
DEBUGFS_FWSTATS_ADD(tx, frag_failed);
DEBUGFS_FWSTATS_ADD(tx, frag_cache_hit);
DEBUGFS_FWSTATS_ADD(tx, frag_cache_miss);
DEBUGFS_FWSTATS_ADD(rx, rx_beacon_early_term);
DEBUGFS_FWSTATS_ADD(rx, rx_out_of_mpdu_nodes);
DEBUGFS_FWSTATS_ADD(rx, rx_hdr_overflow);
DEBUGFS_FWSTATS_ADD(rx, rx_dropped_frame);
DEBUGFS_FWSTATS_ADD(rx, rx_done);
DEBUGFS_FWSTATS_ADD(rx, rx_defrag);
DEBUGFS_FWSTATS_ADD(rx, rx_defrag_end);
DEBUGFS_FWSTATS_ADD(rx, rx_cmplt);
DEBUGFS_FWSTATS_ADD(rx, rx_pre_complt);
DEBUGFS_FWSTATS_ADD(rx, rx_cmplt_task);
DEBUGFS_FWSTATS_ADD(rx, rx_phy_hdr);
DEBUGFS_FWSTATS_ADD(rx, rx_timeout);
DEBUGFS_FWSTATS_ADD(rx, rx_timeout_wa);
DEBUGFS_FWSTATS_ADD(rx, rx_wa_density_dropped_frame);
DEBUGFS_FWSTATS_ADD(rx, rx_wa_ba_not_expected);
DEBUGFS_FWSTATS_ADD(rx, rx_frame_checksum);
DEBUGFS_FWSTATS_ADD(rx, rx_checksum_result);
DEBUGFS_FWSTATS_ADD(rx, defrag_called);
DEBUGFS_FWSTATS_ADD(rx, defrag_init_called);
DEBUGFS_FWSTATS_ADD(rx, defrag_in_process_called);
DEBUGFS_FWSTATS_ADD(rx, defrag_tkip_called);
DEBUGFS_FWSTATS_ADD(rx, defrag_need_defrag);
DEBUGFS_FWSTATS_ADD(rx, defrag_decrypt_failed);
DEBUGFS_FWSTATS_ADD(rx, decrypt_key_not_found);
DEBUGFS_FWSTATS_ADD(rx, defrag_need_decrypt);
DEBUGFS_FWSTATS_ADD(rx, rx_tkip_replays);
DEBUGFS_FWSTATS_ADD(isr, irqs);
DEBUGFS_FWSTATS_ADD(pwr, missing_bcns_cnt);
DEBUGFS_FWSTATS_ADD(pwr, rcvd_bcns_cnt);
DEBUGFS_FWSTATS_ADD(pwr, connection_out_of_sync);
DEBUGFS_FWSTATS_ADD(pwr, cont_miss_bcns_spread);
DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_bcns_cnt);
DEBUGFS_FWSTATS_ADD(ps_poll, ps_poll_timeouts);
DEBUGFS_FWSTATS_ADD(ps_poll, upsd_timeouts);
DEBUGFS_FWSTATS_ADD(ps_poll, upsd_max_ap_turn);
DEBUGFS_FWSTATS_ADD(ps_poll, ps_poll_max_ap_turn);
DEBUGFS_FWSTATS_ADD(ps_poll, ps_poll_utilization);
DEBUGFS_FWSTATS_ADD(ps_poll, upsd_utilization);
DEBUGFS_FWSTATS_ADD(rx_filter, beacon_filter);
DEBUGFS_FWSTATS_ADD(rx_filter, arp_filter);
DEBUGFS_FWSTATS_ADD(rx_filter, mc_filter);
DEBUGFS_FWSTATS_ADD(rx_filter, dup_filter);
DEBUGFS_FWSTATS_ADD(rx_filter, data_filter);
DEBUGFS_FWSTATS_ADD(rx_filter, ibss_filter);
DEBUGFS_FWSTATS_ADD(rx_filter, protection_filter);
DEBUGFS_FWSTATS_ADD(rx_filter, accum_arp_pend_requests);
DEBUGFS_FWSTATS_ADD(rx_filter, max_arp_queue_dep);
DEBUGFS_FWSTATS_ADD(rx_rate, rx_frames_per_rates);
DEBUGFS_FWSTATS_ADD(aggr_size, tx_agg_vs_rate);
DEBUGFS_FWSTATS_ADD(aggr_size, rx_size);
DEBUGFS_FWSTATS_ADD(pipeline, hs_tx_stat_fifo_int);
DEBUGFS_FWSTATS_ADD(pipeline, tcp_tx_stat_fifo_int);
DEBUGFS_FWSTATS_ADD(pipeline, tcp_rx_stat_fifo_int);
DEBUGFS_FWSTATS_ADD(pipeline, enc_tx_stat_fifo_int);
DEBUGFS_FWSTATS_ADD(pipeline, enc_rx_stat_fifo_int);
DEBUGFS_FWSTATS_ADD(pipeline, rx_complete_stat_fifo_int);
DEBUGFS_FWSTATS_ADD(pipeline, pre_proc_swi);
DEBUGFS_FWSTATS_ADD(pipeline, post_proc_swi);
DEBUGFS_FWSTATS_ADD(pipeline, sec_frag_swi);
DEBUGFS_FWSTATS_ADD(pipeline, pre_to_defrag_swi);
DEBUGFS_FWSTATS_ADD(pipeline, defrag_to_csum_swi);
DEBUGFS_FWSTATS_ADD(pipeline, csum_to_rx_xfer_swi);
DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_in);
DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_in_fifo_full);
DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_out);
DEBUGFS_FWSTATS_ADD(pipeline, cs_rx_packet_in);
DEBUGFS_FWSTATS_ADD(pipeline, cs_rx_packet_out);
DEBUGFS_FWSTATS_ADD(pipeline, pipeline_fifo_full);
DEBUGFS_FWSTATS_ADD(mem, rx_free_mem_blks);
DEBUGFS_FWSTATS_ADD(mem, tx_free_mem_blks);
DEBUGFS_FWSTATS_ADD(mem, fwlog_free_mem_blks);
DEBUGFS_FWSTATS_ADD(mem, fw_gen_free_mem_blks);
DEBUGFS_ADD(conf, moddir);
return 0;
err:
if (IS_ERR(entry))
ret = PTR_ERR(entry);
else
ret = -ENOMEM;
return ret;
}

View File

@ -0,0 +1,28 @@
/*
* This file is part of wl18xx
*
* Copyright (C) 2012 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL18XX_DEBUGFS_H__
#define __WL18XX_DEBUGFS_H__
int wl18xx_debugfs_add_files(struct wl1271 *wl,
struct dentry *rootdir);
#endif /* __WL18XX_DEBUGFS_H__ */

View File

@ -0,0 +1,60 @@
/*
* This file is part of wl18xx
*
* Copyright (C) 2011 Texas Instruments
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "../wlcore/wlcore.h"
#include "../wlcore/io.h"
#include "io.h"
void wl18xx_top_reg_write(struct wl1271 *wl, int addr, u16 val)
{
u32 tmp;
if (WARN_ON(addr % 2))
return;
if ((addr % 4) == 0) {
tmp = wl1271_read32(wl, addr);
tmp = (tmp & 0xffff0000) | val;
wl1271_write32(wl, addr, tmp);
} else {
tmp = wl1271_read32(wl, addr - 2);
tmp = (tmp & 0xffff) | (val << 16);
wl1271_write32(wl, addr - 2, tmp);
}
}
u16 wl18xx_top_reg_read(struct wl1271 *wl, int addr)
{
u32 val;
if (WARN_ON(addr % 2))
return 0;
if ((addr % 4) == 0) {
/* address is 4-bytes aligned */
val = wl1271_read32(wl, addr);
return val & 0xffff;
} else {
val = wl1271_read32(wl, addr - 2);
return (val & 0xffff0000) >> 16;
}
}

View File

@ -0,0 +1,28 @@
/*
* This file is part of wl18xx
*
* Copyright (C) 2011 Texas Instruments
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL18XX_IO_H__
#define __WL18XX_IO_H__
void wl18xx_top_reg_write(struct wl1271 *wl, int addr, u16 val);
u16 wl18xx_top_reg_read(struct wl1271 *wl, int addr);
#endif /* __WL18XX_IO_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,191 @@
/*
* This file is part of wlcore
*
* Copyright (C) 2011 Texas Instruments Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __REG_H__
#define __REG_H__
#define WL18XX_REGISTERS_BASE 0x00800000
#define WL18XX_CODE_BASE 0x00000000
#define WL18XX_DATA_BASE 0x00400000
#define WL18XX_DOUBLE_BUFFER_BASE 0x00600000
#define WL18XX_MCU_KEY_SEARCH_BASE 0x00700000
#define WL18XX_PHY_BASE 0x00900000
#define WL18XX_TOP_OCP_BASE 0x00A00000
#define WL18XX_PACKET_RAM_BASE 0x00B00000
#define WL18XX_HOST_BASE 0x00C00000
#define WL18XX_REGISTERS_DOWN_SIZE 0x0000B000
#define WL18XX_REG_BOOT_PART_START 0x00802000
#define WL18XX_REG_BOOT_PART_SIZE 0x00014578
#define WL18XX_PHY_INIT_MEM_ADDR 0x80926000
#define WL18XX_SDIO_WSPI_BASE (WL18XX_REGISTERS_BASE)
#define WL18XX_REG_CONFIG_BASE (WL18XX_REGISTERS_BASE + 0x02000)
#define WL18XX_WGCM_REGS_BASE (WL18XX_REGISTERS_BASE + 0x03000)
#define WL18XX_ENC_BASE (WL18XX_REGISTERS_BASE + 0x04000)
#define WL18XX_INTERRUPT_BASE (WL18XX_REGISTERS_BASE + 0x05000)
#define WL18XX_UART_BASE (WL18XX_REGISTERS_BASE + 0x06000)
#define WL18XX_WELP_BASE (WL18XX_REGISTERS_BASE + 0x07000)
#define WL18XX_TCP_CKSM_BASE (WL18XX_REGISTERS_BASE + 0x08000)
#define WL18XX_FIFO_BASE (WL18XX_REGISTERS_BASE + 0x09000)
#define WL18XX_OCP_BRIDGE_BASE (WL18XX_REGISTERS_BASE + 0x0A000)
#define WL18XX_PMAC_RX_BASE (WL18XX_REGISTERS_BASE + 0x14800)
#define WL18XX_PMAC_ACM_BASE (WL18XX_REGISTERS_BASE + 0x14C00)
#define WL18XX_PMAC_TX_BASE (WL18XX_REGISTERS_BASE + 0x15000)
#define WL18XX_PMAC_CSR_BASE (WL18XX_REGISTERS_BASE + 0x15400)
#define WL18XX_REG_ECPU_CONTROL (WL18XX_REGISTERS_BASE + 0x02004)
#define WL18XX_REG_INTERRUPT_NO_CLEAR (WL18XX_REGISTERS_BASE + 0x050E8)
#define WL18XX_REG_INTERRUPT_ACK (WL18XX_REGISTERS_BASE + 0x050F0)
#define WL18XX_REG_INTERRUPT_TRIG (WL18XX_REGISTERS_BASE + 0x5074)
#define WL18XX_REG_INTERRUPT_TRIG_H (WL18XX_REGISTERS_BASE + 0x5078)
#define WL18XX_REG_INTERRUPT_MASK (WL18XX_REGISTERS_BASE + 0x0050DC)
#define WL18XX_REG_CHIP_ID_B (WL18XX_REGISTERS_BASE + 0x01542C)
#define WL18XX_SLV_MEM_DATA (WL18XX_HOST_BASE + 0x0018)
#define WL18XX_SLV_REG_DATA (WL18XX_HOST_BASE + 0x0008)
/* Scratch Pad registers*/
#define WL18XX_SCR_PAD0 (WL18XX_REGISTERS_BASE + 0x0154EC)
#define WL18XX_SCR_PAD1 (WL18XX_REGISTERS_BASE + 0x0154F0)
#define WL18XX_SCR_PAD2 (WL18XX_REGISTERS_BASE + 0x0154F4)
#define WL18XX_SCR_PAD3 (WL18XX_REGISTERS_BASE + 0x0154F8)
#define WL18XX_SCR_PAD4 (WL18XX_REGISTERS_BASE + 0x0154FC)
#define WL18XX_SCR_PAD4_SET (WL18XX_REGISTERS_BASE + 0x015504)
#define WL18XX_SCR_PAD4_CLR (WL18XX_REGISTERS_BASE + 0x015500)
#define WL18XX_SCR_PAD5 (WL18XX_REGISTERS_BASE + 0x015508)
#define WL18XX_SCR_PAD5_SET (WL18XX_REGISTERS_BASE + 0x015510)
#define WL18XX_SCR_PAD5_CLR (WL18XX_REGISTERS_BASE + 0x01550C)
#define WL18XX_SCR_PAD6 (WL18XX_REGISTERS_BASE + 0x015514)
#define WL18XX_SCR_PAD7 (WL18XX_REGISTERS_BASE + 0x015518)
#define WL18XX_SCR_PAD8 (WL18XX_REGISTERS_BASE + 0x01551C)
#define WL18XX_SCR_PAD9 (WL18XX_REGISTERS_BASE + 0x015520)
/* Spare registers*/
#define WL18XX_SPARE_A1 (WL18XX_REGISTERS_BASE + 0x002194)
#define WL18XX_SPARE_A2 (WL18XX_REGISTERS_BASE + 0x002198)
#define WL18XX_SPARE_A3 (WL18XX_REGISTERS_BASE + 0x00219C)
#define WL18XX_SPARE_A4 (WL18XX_REGISTERS_BASE + 0x0021A0)
#define WL18XX_SPARE_A5 (WL18XX_REGISTERS_BASE + 0x0021A4)
#define WL18XX_SPARE_A6 (WL18XX_REGISTERS_BASE + 0x0021A8)
#define WL18XX_SPARE_A7 (WL18XX_REGISTERS_BASE + 0x0021AC)
#define WL18XX_SPARE_A8 (WL18XX_REGISTERS_BASE + 0x0021B0)
#define WL18XX_SPARE_B1 (WL18XX_REGISTERS_BASE + 0x015524)
#define WL18XX_SPARE_B2 (WL18XX_REGISTERS_BASE + 0x015528)
#define WL18XX_SPARE_B3 (WL18XX_REGISTERS_BASE + 0x01552C)
#define WL18XX_SPARE_B4 (WL18XX_REGISTERS_BASE + 0x015530)
#define WL18XX_SPARE_B5 (WL18XX_REGISTERS_BASE + 0x015534)
#define WL18XX_SPARE_B6 (WL18XX_REGISTERS_BASE + 0x015538)
#define WL18XX_SPARE_B7 (WL18XX_REGISTERS_BASE + 0x01553C)
#define WL18XX_SPARE_B8 (WL18XX_REGISTERS_BASE + 0x015540)
#define WL18XX_REG_COMMAND_MAILBOX_PTR (WL18XX_SCR_PAD0)
#define WL18XX_REG_EVENT_MAILBOX_PTR (WL18XX_SCR_PAD1)
#define WL18XX_EEPROMLESS_IND (WL18XX_SCR_PAD4)
#define WL18XX_WELP_ARM_COMMAND (WL18XX_REGISTERS_BASE + 0x7100)
#define WL18XX_ENABLE (WL18XX_REGISTERS_BASE + 0x01543C)
/* PRCM registers */
#define PLATFORM_DETECTION 0xA0E3E0
#define OCS_EN 0xA02080
#define PRIMARY_CLK_DETECT 0xA020A6
#define PLLSH_WCS_PLL_N 0xA02362
#define PLLSH_WCS_PLL_M 0xA02360
#define PLLSH_WCS_PLL_Q_FACTOR_CFG_1 0xA02364
#define PLLSH_WCS_PLL_Q_FACTOR_CFG_2 0xA02366
#define PLLSH_WCS_PLL_P_FACTOR_CFG_1 0xA02368
#define PLLSH_WCS_PLL_P_FACTOR_CFG_2 0xA0236A
#define PLLSH_WCS_PLL_SWALLOW_EN 0xA0236C
#define PLLSH_WL_PLL_EN 0xA02392
#define PLLSH_WCS_PLL_Q_FACTOR_CFG_1_MASK 0xFFFF
#define PLLSH_WCS_PLL_Q_FACTOR_CFG_2_MASK 0x007F
#define PLLSH_WCS_PLL_P_FACTOR_CFG_1_MASK 0xFFFF
#define PLLSH_WCS_PLL_P_FACTOR_CFG_2_MASK 0x000F
#define PLLSH_WCS_PLL_SWALLOW_EN_VAL1 0x1
#define PLLSH_WCS_PLL_SWALLOW_EN_VAL2 0x12
#define WL18XX_REG_FUSE_DATA_1_3 0xA0260C
#define WL18XX_PG_VER_MASK 0x70
#define WL18XX_PG_VER_OFFSET 4
#define WL18XX_REG_FUSE_BD_ADDR_1 0xA02602
#define WL18XX_REG_FUSE_BD_ADDR_2 0xA02606
#define WL18XX_CMD_MBOX_ADDRESS 0xB007B4
#define WL18XX_FW_STATUS_ADDR 0x50F8
#define CHIP_ID_185x_PG10 (0x06030101)
#define CHIP_ID_185x_PG20 (0x06030111)
/*
* Host Command Interrupt. Setting this bit masks
* the interrupt that the host issues to inform
* the FW that it has sent a command
* to the Wlan hardware Command Mailbox.
*/
#define WL18XX_INTR_TRIG_CMD BIT(28)
/*
* Host Event Acknowlegde Interrupt. The host
* sets this bit to acknowledge that it received
* the unsolicited information from the event
* mailbox.
*/
#define WL18XX_INTR_TRIG_EVENT_ACK BIT(29)
/*
* To boot the firmware in PLT mode we need to write this value in
* SCR_PAD8 before starting.
*/
#define WL18XX_SCR_PAD8_PLT 0xBABABEBE
enum {
COMPONENT_NO_SWITCH = 0x0,
COMPONENT_2_WAY_SWITCH = 0x1,
COMPONENT_3_WAY_SWITCH = 0x2,
COMPONENT_MATCHING = 0x3,
};
enum {
FEM_NONE = 0x0,
FEM_VENDOR_1 = 0x1,
FEM_VENDOR_2 = 0x2,
FEM_VENDOR_3 = 0x3,
};
enum {
BOARD_TYPE_EVB_18XX = 0,
BOARD_TYPE_DVP_18XX = 1,
BOARD_TYPE_HDK_18XX = 2,
BOARD_TYPE_FPGA_18XX = 3,
BOARD_TYPE_COM8_18XX = 4,
NUM_BOARD_TYPES,
};
#endif /* __REG_H__ */

View File

@ -0,0 +1,127 @@
/*
* This file is part of wl18xx
*
* Copyright (C) 2011 Texas Instruments Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include "../wlcore/wlcore.h"
#include "../wlcore/cmd.h"
#include "../wlcore/debug.h"
#include "../wlcore/acx.h"
#include "../wlcore/tx.h"
#include "wl18xx.h"
#include "tx.h"
static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
{
struct ieee80211_tx_info *info;
struct sk_buff *skb;
int id = tx_stat_byte & WL18XX_TX_STATUS_DESC_ID_MASK;
bool tx_success;
/* check for id legality */
if (unlikely(id >= wl->num_tx_desc || wl->tx_frames[id] == NULL)) {
wl1271_warning("illegal id in tx completion: %d", id);
return;
}
/* a zero bit indicates Tx success */
tx_success = !(tx_stat_byte & BIT(WL18XX_TX_STATUS_STAT_BIT_IDX));
skb = wl->tx_frames[id];
info = IEEE80211_SKB_CB(skb);
if (wl12xx_is_dummy_packet(wl, skb)) {
wl1271_free_tx_id(wl, id);
return;
}
/* update the TX status info */
if (tx_success && !(info->flags & IEEE80211_TX_CTL_NO_ACK))
info->flags |= IEEE80211_TX_STAT_ACK;
/* no real data about Tx completion */
info->status.rates[0].idx = -1;
info->status.rates[0].count = 0;
info->status.rates[0].flags = 0;
info->status.ack_signal = -1;
if (!tx_success)
wl->stats.retry_count++;
/*
* TODO: update sequence number for encryption? seems to be
* unsupported for now. needed for recovery with encryption.
*/
/* remove private header from packet */
skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));
/* remove TKIP header space if present */
if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
info->control.hw_key &&
info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data, hdrlen);
skb_pull(skb, WL1271_EXTRA_SPACE_TKIP);
}
wl1271_debug(DEBUG_TX, "tx status id %u skb 0x%p success %d",
id, skb, tx_success);
/* return the packet to the stack */
skb_queue_tail(&wl->deferred_tx_queue, skb);
queue_work(wl->freezable_wq, &wl->netstack_work);
wl1271_free_tx_id(wl, id);
}
void wl18xx_tx_immediate_complete(struct wl1271 *wl)
{
struct wl18xx_fw_status_priv *status_priv =
(struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
struct wl18xx_priv *priv = wl->priv;
u8 i;
/* nothing to do here */
if (priv->last_fw_rls_idx == status_priv->fw_release_idx)
return;
/* freed Tx descriptors */
wl1271_debug(DEBUG_TX, "last released desc = %d, current idx = %d",
priv->last_fw_rls_idx, status_priv->fw_release_idx);
if (status_priv->fw_release_idx >= WL18XX_FW_MAX_TX_STATUS_DESC) {
wl1271_error("invalid desc release index %d",
status_priv->fw_release_idx);
WARN_ON(1);
return;
}
for (i = priv->last_fw_rls_idx;
i != status_priv->fw_release_idx;
i = (i + 1) % WL18XX_FW_MAX_TX_STATUS_DESC) {
wl18xx_tx_complete_packet(wl,
status_priv->released_tx_desc[i]);
wl->tx_results_count++;
}
priv->last_fw_rls_idx = status_priv->fw_release_idx;
}

View File

@ -0,0 +1,46 @@
/*
* This file is part of wl18xx
*
* Copyright (C) 2011 Texas Instruments. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL18XX_TX_H__
#define __WL18XX_TX_H__
#include "../wlcore/wlcore.h"
#define WL18XX_TX_HW_BLOCK_SPARE 1
/* for special cases - namely, TKIP and GEM */
#define WL18XX_TX_HW_EXTRA_BLOCK_SPARE 2
#define WL18XX_TX_HW_BLOCK_SIZE 268
#define WL18XX_TX_STATUS_DESC_ID_MASK 0x7F
#define WL18XX_TX_STATUS_STAT_BIT_IDX 7
/* Indicates this TX HW frame is not padded to SDIO block size */
#define WL18XX_TX_CTRL_NOT_PADDED BIT(7)
/*
* The FW uses a special bit to indicate a wide channel should be used in
* the rate policy.
*/
#define CONF_TX_RATE_USE_WIDE_CHAN BIT(31)
void wl18xx_tx_immediate_complete(struct wl1271 *wl);
#endif /* __WL12XX_TX_H__ */

View File

@ -0,0 +1,88 @@
/*
* This file is part of wl18xx
*
* Copyright (C) 2011 Texas Instruments Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL18XX_PRIV_H__
#define __WL18XX_PRIV_H__
#include "conf.h"
#define WL18XX_CMD_MAX_SIZE 740
struct wl18xx_priv {
/* buffer for sending commands to FW */
u8 cmd_buf[WL18XX_CMD_MAX_SIZE];
struct wl18xx_priv_conf conf;
/* Index of last released Tx desc in FW */
u8 last_fw_rls_idx;
/* number of VIFs requiring extra spare mem-blocks */
int extra_spare_vif_count;
};
#define WL18XX_FW_MAX_TX_STATUS_DESC 33
struct wl18xx_fw_status_priv {
/*
* Index in released_tx_desc for first byte that holds
* released tx host desc
*/
u8 fw_release_idx;
/*
* Array of host Tx descriptors, where fw_release_idx
* indicated the first released idx.
*/
u8 released_tx_desc[WL18XX_FW_MAX_TX_STATUS_DESC];
u8 padding[2];
};
#define WL18XX_PHY_VERSION_MAX_LEN 20
struct wl18xx_static_data_priv {
char phy_version[WL18XX_PHY_VERSION_MAX_LEN];
};
struct wl18xx_clk_cfg {
u32 n;
u32 m;
u32 p;
u32 q;
bool swallow;
};
enum {
CLOCK_CONFIG_16_2_M = 1,
CLOCK_CONFIG_16_368_M,
CLOCK_CONFIG_16_8_M,
CLOCK_CONFIG_19_2_M,
CLOCK_CONFIG_26_M,
CLOCK_CONFIG_32_736_M,
CLOCK_CONFIG_33_6_M,
CLOCK_CONFIG_38_468_M,
CLOCK_CONFIG_52_M,
NUM_CLOCK_CONFIGS,
};
#endif /* __WL18XX_PRIV_H__ */

View File

@ -86,6 +86,7 @@ out:
kfree(auth);
return ret;
}
EXPORT_SYMBOL_GPL(wl1271_acx_sleep_auth);
int wl1271_acx_tx_power(struct wl1271 *wl, struct wl12xx_vif *wlvif,
int power)
@ -708,14 +709,14 @@ out:
return ret;
}
int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats)
int wl1271_acx_statistics(struct wl1271 *wl, void *stats)
{
int ret;
wl1271_debug(DEBUG_ACX, "acx statistics");
ret = wl1271_cmd_interrogate(wl, ACX_STATISTICS, stats,
sizeof(*stats));
wl->stats.fw_stats_len);
if (ret < 0) {
wl1271_warning("acx statistics failed: %d", ret);
return -ENOMEM;
@ -997,6 +998,7 @@ out:
kfree(mem_conf);
return ret;
}
EXPORT_SYMBOL_GPL(wl12xx_acx_mem_cfg);
int wl1271_acx_init_mem_config(struct wl1271 *wl)
{
@ -1027,6 +1029,7 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl)
return 0;
}
EXPORT_SYMBOL_GPL(wl1271_acx_init_mem_config);
int wl1271_acx_init_rx_interrupt(struct wl1271 *wl)
{
@ -1150,6 +1153,7 @@ out:
kfree(acx);
return ret;
}
EXPORT_SYMBOL_GPL(wl1271_acx_pm_config);
int wl1271_acx_keep_alive_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
bool enable)

View File

@ -51,21 +51,18 @@
#define WL1271_ACX_INTR_TRACE_A BIT(7)
/* Trace message on MBOX #B */
#define WL1271_ACX_INTR_TRACE_B BIT(8)
/* SW FW Initiated interrupt Watchdog timer expiration */
#define WL1271_ACX_SW_INTR_WATCHDOG BIT(9)
#define WL1271_ACX_INTR_ALL 0xFFFFFFFF
#define WL1271_ACX_ALL_EVENTS_VECTOR (WL1271_ACX_INTR_WATCHDOG | \
WL1271_ACX_INTR_INIT_COMPLETE | \
WL1271_ACX_INTR_EVENT_A | \
WL1271_ACX_INTR_EVENT_B | \
WL1271_ACX_INTR_CMD_COMPLETE | \
WL1271_ACX_INTR_HW_AVAILABLE | \
WL1271_ACX_INTR_DATA)
#define WL1271_ACX_INTR_ALL 0xFFFFFFFF
#define WL1271_INTR_MASK (WL1271_ACX_INTR_WATCHDOG | \
WL1271_ACX_INTR_EVENT_A | \
WL1271_ACX_INTR_EVENT_B | \
WL1271_ACX_INTR_HW_AVAILABLE | \
WL1271_ACX_INTR_DATA)
/* all possible interrupts - only appropriate ones will be masked in */
#define WLCORE_ALL_INTR_MASK (WL1271_ACX_INTR_WATCHDOG | \
WL1271_ACX_INTR_EVENT_A | \
WL1271_ACX_INTR_EVENT_B | \
WL1271_ACX_INTR_HW_AVAILABLE | \
WL1271_ACX_INTR_DATA | \
WL1271_ACX_SW_INTR_WATCHDOG)
/* Target's information element */
struct acx_header {
@ -417,228 +414,6 @@ struct acx_ctsprotect {
u8 padding[2];
} __packed;
struct acx_tx_statistics {
__le32 internal_desc_overflow;
} __packed;
struct acx_rx_statistics {
__le32 out_of_mem;
__le32 hdr_overflow;
__le32 hw_stuck;
__le32 dropped;
__le32 fcs_err;
__le32 xfr_hint_trig;
__le32 path_reset;
__le32 reset_counter;
} __packed;
struct acx_dma_statistics {
__le32 rx_requested;
__le32 rx_errors;
__le32 tx_requested;
__le32 tx_errors;
} __packed;
struct acx_isr_statistics {
/* host command complete */
__le32 cmd_cmplt;
/* fiqisr() */
__le32 fiqs;
/* (INT_STS_ND & INT_TRIG_RX_HEADER) */
__le32 rx_headers;
/* (INT_STS_ND & INT_TRIG_RX_CMPLT) */
__le32 rx_completes;
/* (INT_STS_ND & INT_TRIG_NO_RX_BUF) */
__le32 rx_mem_overflow;
/* (INT_STS_ND & INT_TRIG_S_RX_RDY) */
__le32 rx_rdys;
/* irqisr() */
__le32 irqs;
/* (INT_STS_ND & INT_TRIG_TX_PROC) */
__le32 tx_procs;
/* (INT_STS_ND & INT_TRIG_DECRYPT_DONE) */
__le32 decrypt_done;
/* (INT_STS_ND & INT_TRIG_DMA0) */
__le32 dma0_done;
/* (INT_STS_ND & INT_TRIG_DMA1) */
__le32 dma1_done;
/* (INT_STS_ND & INT_TRIG_TX_EXC_CMPLT) */
__le32 tx_exch_complete;
/* (INT_STS_ND & INT_TRIG_COMMAND) */
__le32 commands;
/* (INT_STS_ND & INT_TRIG_RX_PROC) */
__le32 rx_procs;
/* (INT_STS_ND & INT_TRIG_PM_802) */
__le32 hw_pm_mode_changes;
/* (INT_STS_ND & INT_TRIG_ACKNOWLEDGE) */
__le32 host_acknowledges;
/* (INT_STS_ND & INT_TRIG_PM_PCI) */
__le32 pci_pm;
/* (INT_STS_ND & INT_TRIG_ACM_WAKEUP) */
__le32 wakeups;
/* (INT_STS_ND & INT_TRIG_LOW_RSSI) */
__le32 low_rssi;
} __packed;
struct acx_wep_statistics {
/* WEP address keys configured */
__le32 addr_key_count;
/* default keys configured */
__le32 default_key_count;
__le32 reserved;
/* number of times that WEP key not found on lookup */
__le32 key_not_found;
/* number of times that WEP key decryption failed */
__le32 decrypt_fail;
/* WEP packets decrypted */
__le32 packets;
/* WEP decrypt interrupts */
__le32 interrupt;
} __packed;
#define ACX_MISSED_BEACONS_SPREAD 10
struct acx_pwr_statistics {
/* the amount of enters into power save mode (both PD & ELP) */
__le32 ps_enter;
/* the amount of enters into ELP mode */
__le32 elp_enter;
/* the amount of missing beacon interrupts to the host */
__le32 missing_bcns;
/* the amount of wake on host-access times */
__le32 wake_on_host;
/* the amount of wake on timer-expire */
__le32 wake_on_timer_exp;
/* the number of packets that were transmitted with PS bit set */
__le32 tx_with_ps;
/* the number of packets that were transmitted with PS bit clear */
__le32 tx_without_ps;
/* the number of received beacons */
__le32 rcvd_beacons;
/* the number of entering into PowerOn (power save off) */
__le32 power_save_off;
/* the number of entries into power save mode */
__le16 enable_ps;
/*
* the number of exits from power save, not including failed PS
* transitions
*/
__le16 disable_ps;
/*
* the number of times the TSF counter was adjusted because
* of drift
*/
__le32 fix_tsf_ps;
/* Gives statistics about the spread continuous missed beacons.
* The 16 LSB are dedicated for the PS mode.
* The 16 MSB are dedicated for the PS mode.
* cont_miss_bcns_spread[0] - single missed beacon.
* cont_miss_bcns_spread[1] - two continuous missed beacons.
* cont_miss_bcns_spread[2] - three continuous missed beacons.
* ...
* cont_miss_bcns_spread[9] - ten and more continuous missed beacons.
*/
__le32 cont_miss_bcns_spread[ACX_MISSED_BEACONS_SPREAD];
/* the number of beacons in awake mode */
__le32 rcvd_awake_beacons;
} __packed;
struct acx_mic_statistics {
__le32 rx_pkts;
__le32 calc_failure;
} __packed;
struct acx_aes_statistics {
__le32 encrypt_fail;
__le32 decrypt_fail;
__le32 encrypt_packets;
__le32 decrypt_packets;
__le32 encrypt_interrupt;
__le32 decrypt_interrupt;
} __packed;
struct acx_event_statistics {
__le32 heart_beat;
__le32 calibration;
__le32 rx_mismatch;
__le32 rx_mem_empty;
__le32 rx_pool;
__le32 oom_late;
__le32 phy_transmit_error;
__le32 tx_stuck;
} __packed;
struct acx_ps_statistics {
__le32 pspoll_timeouts;
__le32 upsd_timeouts;
__le32 upsd_max_sptime;
__le32 upsd_max_apturn;
__le32 pspoll_max_apturn;
__le32 pspoll_utilization;
__le32 upsd_utilization;
} __packed;
struct acx_rxpipe_statistics {
__le32 rx_prep_beacon_drop;
__le32 descr_host_int_trig_rx_data;
__le32 beacon_buffer_thres_host_int_trig_rx_data;
__le32 missed_beacon_host_int_trig_rx_data;
__le32 tx_xfr_host_int_trig_rx_data;
} __packed;
struct acx_statistics {
struct acx_header header;
struct acx_tx_statistics tx;
struct acx_rx_statistics rx;
struct acx_dma_statistics dma;
struct acx_isr_statistics isr;
struct acx_wep_statistics wep;
struct acx_pwr_statistics pwr;
struct acx_aes_statistics aes;
struct acx_mic_statistics mic;
struct acx_event_statistics event;
struct acx_ps_statistics ps;
struct acx_rxpipe_statistics rxpipe;
} __packed;
struct acx_rate_class {
__le32 enabled_rates;
u8 short_retry_limit;
@ -828,6 +603,8 @@ struct wl1271_acx_keep_alive_config {
#define HOST_IF_CFG_RX_FIFO_ENABLE BIT(0)
#define HOST_IF_CFG_TX_EXTRA_BLKS_SWAP BIT(1)
#define HOST_IF_CFG_TX_PAD_TO_SDIO_BLK BIT(3)
#define HOST_IF_CFG_RX_PAD_TO_SDIO_BLK BIT(4)
#define HOST_IF_CFG_ADD_RX_ALIGNMENT BIT(6)
enum {
WL1271_ACX_TRIG_TYPE_LEVEL = 0,
@ -946,7 +723,7 @@ struct wl1271_acx_ht_information {
u8 padding[2];
} __packed;
#define RX_BA_MAX_SESSIONS 2
#define RX_BA_MAX_SESSIONS 3
struct wl1271_acx_ba_initiator_policy {
struct acx_header header;
@ -1243,6 +1020,7 @@ enum {
ACX_CONFIG_HANGOVER = 0x0042,
ACX_FEATURE_CFG = 0x0043,
ACX_PROTECTION_CFG = 0x0044,
ACX_CHECKSUM_CONFIG = 0x0045,
};
@ -1281,7 +1059,7 @@ int wl1271_acx_set_preamble(struct wl1271 *wl, struct wl12xx_vif *wlvif,
enum acx_preamble_type preamble);
int wl1271_acx_cts_protect(struct wl1271 *wl, struct wl12xx_vif *wlvif,
enum acx_ctsprotect_type ctsprotect);
int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats);
int wl1271_acx_statistics(struct wl1271 *wl, void *stats);
int wl1271_acx_sta_rate_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c,
u8 idx);

View File

@ -45,10 +45,17 @@ static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag)
wlcore_write_reg(wl, REG_ECPU_CONTROL, cpu_ctrl);
}
static int wlcore_parse_fw_ver(struct wl1271 *wl)
static int wlcore_boot_parse_fw_ver(struct wl1271 *wl,
struct wl1271_static_data *static_data)
{
int ret;
strncpy(wl->chip.fw_ver_str, static_data->fw_version,
sizeof(wl->chip.fw_ver_str));
/* make sure the string is NULL-terminated */
wl->chip.fw_ver_str[sizeof(wl->chip.fw_ver_str) - 1] = '\0';
ret = sscanf(wl->chip.fw_ver_str + 4, "%u.%u.%u.%u.%u",
&wl->chip.fw_ver[0], &wl->chip.fw_ver[1],
&wl->chip.fw_ver[2], &wl->chip.fw_ver[3],
@ -57,43 +64,43 @@ static int wlcore_parse_fw_ver(struct wl1271 *wl)
if (ret != 5) {
wl1271_warning("fw version incorrect value");
memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver));
return -EINVAL;
ret = -EINVAL;
goto out;
}
ret = wlcore_identify_fw(wl);
if (ret < 0)
return ret;
return 0;
goto out;
out:
return ret;
}
static int wlcore_boot_fw_version(struct wl1271 *wl)
static int wlcore_boot_static_data(struct wl1271 *wl)
{
struct wl1271_static_data *static_data;
size_t len = sizeof(*static_data) + wl->static_data_priv_len;
int ret;
static_data = kmalloc(sizeof(*static_data), GFP_KERNEL | GFP_DMA);
static_data = kmalloc(len, GFP_KERNEL);
if (!static_data) {
wl1271_error("Couldn't allocate memory for static data!");
return -ENOMEM;
ret = -ENOMEM;
goto out;
}
wl1271_read(wl, wl->cmd_box_addr, static_data, sizeof(*static_data),
false);
wl1271_read(wl, wl->cmd_box_addr, static_data, len, false);
strncpy(wl->chip.fw_ver_str, static_data->fw_version,
sizeof(wl->chip.fw_ver_str));
kfree(static_data);
/* make sure the string is NULL-terminated */
wl->chip.fw_ver_str[sizeof(wl->chip.fw_ver_str) - 1] = '\0';
ret = wlcore_parse_fw_ver(wl);
ret = wlcore_boot_parse_fw_ver(wl, static_data);
if (ret < 0)
return ret;
goto out_free;
return 0;
ret = wlcore_handle_static_data(wl, static_data);
if (ret < 0)
goto out_free;
out_free:
kfree(static_data);
out:
return ret;
}
static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
@ -204,8 +211,10 @@ int wlcore_boot_upload_nvs(struct wl1271 *wl)
u32 dest_addr, val;
u8 *nvs_ptr, *nvs_aligned;
if (wl->nvs == NULL)
if (wl->nvs == NULL) {
wl1271_error("NVS file is needed during boot");
return -ENODEV;
}
if (wl->quirks & WLCORE_QUIRK_LEGACY_NVS) {
struct wl1271_nvs_file *nvs =
@ -400,9 +409,9 @@ int wlcore_boot_run_firmware(struct wl1271 *wl)
wl1271_debug(DEBUG_MAILBOX, "MBOX ptrs: 0x%x 0x%x",
wl->mbox_ptr[0], wl->mbox_ptr[1]);
ret = wlcore_boot_fw_version(wl);
ret = wlcore_boot_static_data(wl);
if (ret < 0) {
wl1271_error("couldn't boot firmware");
wl1271_error("error getting static data");
return ret;
}

View File

@ -40,6 +40,7 @@ struct wl1271_static_data {
u8 fw_version[WL1271_FW_VERSION_MAX_LEN];
u32 hw_version;
u8 tx_power_table[WL1271_NO_SUBBANDS][WL1271_NO_POWER_LEVELS];
u8 priv[0];
};
/* number of times we try to read the INIT interrupt */

View File

@ -36,6 +36,7 @@
#include "cmd.h"
#include "event.h"
#include "tx.h"
#include "hw_ops.h"
#define WL1271_CMD_FAST_POLL_COUNT 50
@ -291,6 +292,23 @@ static int wl12xx_get_new_session_id(struct wl1271 *wl,
return wlvif->session_counter;
}
static u8 wlcore_get_native_channel_type(u8 nl_channel_type)
{
switch (nl_channel_type) {
case NL80211_CHAN_NO_HT:
return WLCORE_CHAN_NO_HT;
case NL80211_CHAN_HT20:
return WLCORE_CHAN_HT20;
case NL80211_CHAN_HT40MINUS:
return WLCORE_CHAN_HT40MINUS;
case NL80211_CHAN_HT40PLUS:
return WLCORE_CHAN_HT40PLUS;
default:
WARN_ON(1);
return WLCORE_CHAN_NO_HT;
}
}
static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
@ -407,6 +425,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
memcpy(cmd->sta.ssid, wlvif->ssid, wlvif->ssid_len);
memcpy(cmd->sta.bssid, vif->bss_conf.bssid, ETH_ALEN);
cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set);
cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type);
if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) {
ret = wl12xx_allocate_link(wl, wlvif, &wlvif->sta.hlid);
@ -482,6 +501,7 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
struct wl12xx_cmd_role_start *cmd;
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
u32 supported_rates;
int ret;
wl1271_debug(DEBUG_CMD, "cmd role start ap %d", wlvif->role_id);
@ -519,6 +539,7 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
/* FIXME: Change when adding DFS */
cmd->ap.reset_tsf = 1; /* By default reset AP TSF */
cmd->channel = wlvif->channel;
cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type);
if (!bss_conf->hidden_ssid) {
/* take the SSID from the beacon for backward compatibility */
@ -531,7 +552,13 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
memcpy(cmd->ap.ssid, bss_conf->ssid, bss_conf->ssid_len);
}
cmd->ap.local_rates = cpu_to_le32(0xffffffff);
supported_rates = CONF_TX_AP_ENABLED_RATES | CONF_TX_MCS_RATES |
wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif);
wl1271_debug(DEBUG_CMD, "cmd role start ap with supported_rates 0x%08x",
supported_rates);
cmd->ap.local_rates = cpu_to_le32(supported_rates);
switch (wlvif->band) {
case IEEE80211_BAND_2GHZ:
@ -797,6 +824,7 @@ out:
kfree(cmd);
return ret;
}
EXPORT_SYMBOL_GPL(wl1271_cmd_data_path);
int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 ps_mode, u16 auto_ps_timeout)
@ -1018,7 +1046,7 @@ out:
int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int ret, extra;
int ret, extra = 0;
u16 fc;
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
struct sk_buff *skb;
@ -1057,7 +1085,8 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif)
/* encryption space */
switch (wlvif->encryption_type) {
case KEY_TKIP:
extra = WL1271_EXTRA_SPACE_TKIP;
if (wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE)
extra = WL1271_EXTRA_SPACE_TKIP;
break;
case KEY_AES:
extra = WL1271_EXTRA_SPACE_AES;
@ -1346,13 +1375,18 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++)
if (sta->wme && (sta->uapsd_queues & BIT(i)))
cmd->psd_type[i] = WL1271_PSD_UPSD_TRIGGER;
cmd->psd_type[NUM_ACCESS_CATEGORIES_COPY-1-i] =
WL1271_PSD_UPSD_TRIGGER;
else
cmd->psd_type[i] = WL1271_PSD_LEGACY;
cmd->psd_type[NUM_ACCESS_CATEGORIES_COPY-1-i] =
WL1271_PSD_LEGACY;
sta_rates = sta->supp_rates[wlvif->band];
if (sta->ht_cap.ht_supported)
sta_rates |= sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET;
sta_rates |=
(sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) |
(sta->ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET);
cmd->supported_rates =
cpu_to_le32(wl1271_tx_enabled_rates_get(wl, sta_rates,
@ -1573,19 +1607,25 @@ out:
int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id)
{
int ret = 0;
bool is_first_roc;
if (WARN_ON(test_bit(role_id, wl->roc_map)))
return 0;
is_first_roc = (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >=
WL12XX_MAX_ROLES);
ret = wl12xx_cmd_roc(wl, wlvif, role_id);
if (ret < 0)
goto out;
ret = wl1271_cmd_wait_for_event(wl,
REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID);
if (ret < 0) {
wl1271_error("cmd roc event completion error");
goto out;
if (is_first_roc) {
ret = wl1271_cmd_wait_for_event(wl,
REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID);
if (ret < 0) {
wl1271_error("cmd roc event completion error");
goto out;
}
}
__set_bit(role_id, wl->roc_map);

View File

@ -192,7 +192,7 @@ enum cmd_templ {
#define WL1271_COMMAND_TIMEOUT 2000
#define WL1271_CMD_TEMPL_DFLT_SIZE 252
#define WL1271_CMD_TEMPL_MAX_SIZE 512
#define WL1271_EVENT_TIMEOUT 750
#define WL1271_EVENT_TIMEOUT 1000
struct wl1271_cmd_header {
__le16 id;
@ -266,13 +266,22 @@ enum wlcore_band {
WLCORE_BAND_MAX_RADIO = 0x7F,
};
enum wlcore_channel_type {
WLCORE_CHAN_NO_HT,
WLCORE_CHAN_HT20,
WLCORE_CHAN_HT40MINUS,
WLCORE_CHAN_HT40PLUS
};
struct wl12xx_cmd_role_start {
struct wl1271_cmd_header header;
u8 role_id;
u8 band;
u8 channel;
u8 padding;
/* enum wlcore_channel_type */
u8 channel_type;
union {
struct {

View File

@ -45,7 +45,15 @@ enum {
CONF_HW_BIT_RATE_MCS_4 = BIT(17),
CONF_HW_BIT_RATE_MCS_5 = BIT(18),
CONF_HW_BIT_RATE_MCS_6 = BIT(19),
CONF_HW_BIT_RATE_MCS_7 = BIT(20)
CONF_HW_BIT_RATE_MCS_7 = BIT(20),
CONF_HW_BIT_RATE_MCS_8 = BIT(21),
CONF_HW_BIT_RATE_MCS_9 = BIT(22),
CONF_HW_BIT_RATE_MCS_10 = BIT(23),
CONF_HW_BIT_RATE_MCS_11 = BIT(24),
CONF_HW_BIT_RATE_MCS_12 = BIT(25),
CONF_HW_BIT_RATE_MCS_13 = BIT(26),
CONF_HW_BIT_RATE_MCS_14 = BIT(27),
CONF_HW_BIT_RATE_MCS_15 = BIT(28),
};
enum {
@ -310,7 +318,7 @@ enum {
struct conf_sg_settings {
u32 params[CONF_SG_PARAMS_MAX];
u8 state;
};
} __packed;
enum conf_rx_queue_type {
CONF_RX_QUEUE_TYPE_LOW_PRIORITY, /* All except the high priority */
@ -394,7 +402,7 @@ struct conf_rx_settings {
* Range: RX_QUEUE_TYPE_RX_LOW_PRIORITY, RX_QUEUE_TYPE_RX_HIGH_PRIORITY,
*/
u8 queue_type;
};
} __packed;
#define CONF_TX_MAX_RATE_CLASSES 10
@ -435,6 +443,12 @@ struct conf_rx_settings {
CONF_HW_BIT_RATE_MCS_5 | CONF_HW_BIT_RATE_MCS_6 | \
CONF_HW_BIT_RATE_MCS_7)
#define CONF_TX_MIMO_RATES (CONF_HW_BIT_RATE_MCS_8 | \
CONF_HW_BIT_RATE_MCS_9 | CONF_HW_BIT_RATE_MCS_10 | \
CONF_HW_BIT_RATE_MCS_11 | CONF_HW_BIT_RATE_MCS_12 | \
CONF_HW_BIT_RATE_MCS_13 | CONF_HW_BIT_RATE_MCS_14 | \
CONF_HW_BIT_RATE_MCS_15)
/*
* Default rates for management traffic when operating in AP mode. This
* should be configured according to the basic rate set of the AP
@ -487,7 +501,7 @@ struct conf_tx_rate_class {
* the policy (0 - long preamble, 1 - short preamble.
*/
u8 aflags;
};
} __packed;
#define CONF_TX_MAX_AC_COUNT 4
@ -504,7 +518,7 @@ enum conf_tx_ac {
CONF_TX_AC_VI = 2, /* video */
CONF_TX_AC_VO = 3, /* voice */
CONF_TX_AC_CTS2SELF = 4, /* fictitious AC, follows AC_VO */
CONF_TX_AC_ANY_TID = 0x1f
CONF_TX_AC_ANY_TID = 0xff
};
struct conf_tx_ac_category {
@ -544,7 +558,7 @@ struct conf_tx_ac_category {
* Range: u16
*/
u16 tx_op_limit;
};
} __packed;
#define CONF_TX_MAX_TID_COUNT 8
@ -578,7 +592,7 @@ struct conf_tx_tid {
u8 ps_scheme;
u8 ack_policy;
u32 apsd_conf[2];
};
} __packed;
struct conf_tx_settings {
/*
@ -664,7 +678,7 @@ struct conf_tx_settings {
/* Time in ms for Tx watchdog timer to expire */
u32 tx_watchdog_timeout;
};
} __packed;
enum {
CONF_WAKE_UP_EVENT_BEACON = 0x01, /* Wake on every Beacon*/
@ -711,7 +725,7 @@ struct conf_bcn_filt_rule {
* Version for the vendor specifie IE (221)
*/
u8 version[CONF_BCN_IE_VER_LEN];
};
} __packed;
#define CONF_MAX_RSSI_SNR_TRIGGERS 8
@ -762,7 +776,7 @@ struct conf_sig_weights {
* Range: u8
*/
u8 snr_pkt_avg_weight;
};
} __packed;
enum conf_bcn_filt_mode {
CONF_BCN_FILT_MODE_DISABLED = 0,
@ -810,7 +824,7 @@ struct conf_conn_settings {
*
* Range: CONF_BCN_FILT_MODE_*
*/
enum conf_bcn_filt_mode bcn_filt_mode;
u8 bcn_filt_mode;
/*
* Configure Beacon filter pass-thru rules.
@ -937,7 +951,7 @@ struct conf_conn_settings {
* Range: u16
*/
u8 max_listen_interval;
};
} __packed;
enum {
CONF_REF_CLK_19_2_E,
@ -965,6 +979,11 @@ struct conf_itrim_settings {
/* moderation timeout in microsecs from the last TX */
u32 timeout;
} __packed;
enum conf_fast_wakeup {
CONF_FAST_WAKEUP_ENABLE,
CONF_FAST_WAKEUP_DISABLE,
};
struct conf_pm_config_settings {
@ -978,10 +997,10 @@ struct conf_pm_config_settings {
/*
* Host fast wakeup support
*
* Range: true, false
* Range: enum conf_fast_wakeup
*/
bool host_fast_wakeup_support;
};
u8 host_fast_wakeup_support;
} __packed;
struct conf_roam_trigger_settings {
/*
@ -1018,7 +1037,7 @@ struct conf_roam_trigger_settings {
* Range: 0 - 255
*/
u8 avg_weight_snr_data;
};
} __packed;
struct conf_scan_settings {
/*
@ -1064,7 +1083,7 @@ struct conf_scan_settings {
* Range: u32 Microsecs
*/
u32 split_scan_timeout;
};
} __packed;
struct conf_sched_scan_settings {
/*
@ -1102,7 +1121,7 @@ struct conf_sched_scan_settings {
/* SNR threshold to be used for filtering */
s8 snr_threshold;
};
} __packed;
struct conf_ht_setting {
u8 rx_ba_win_size;
@ -1111,7 +1130,7 @@ struct conf_ht_setting {
/* bitmap of enabled TIDs for TX BA sessions */
u8 tx_ba_tid_bitmap;
};
} __packed;
struct conf_memory_settings {
/* Number of stations supported in IBSS mode */
@ -1151,7 +1170,7 @@ struct conf_memory_settings {
* Range: 0-120
*/
u8 tx_min;
};
} __packed;
struct conf_fm_coex {
u8 enable;
@ -1164,7 +1183,7 @@ struct conf_fm_coex {
u16 ldo_stabilization_time;
u8 fm_disturbed_band_margin;
u8 swallow_clk_diff;
};
} __packed;
struct conf_rx_streaming_settings {
/*
@ -1193,7 +1212,7 @@ struct conf_rx_streaming_settings {
* enable rx streaming also when there is no coex activity
*/
u8 always;
};
} __packed;
struct conf_fwlog {
/* Continuous or on-demand */
@ -1217,7 +1236,7 @@ struct conf_fwlog {
/* Regulates the frequency of log messages */
u8 threshold;
};
} __packed;
#define ACX_RATE_MGMT_NUM_OF_RATES 13
struct conf_rate_policy_settings {
@ -1236,7 +1255,7 @@ struct conf_rate_policy_settings {
u8 rate_check_up;
u8 rate_check_down;
u8 rate_retry_policy[ACX_RATE_MGMT_NUM_OF_RATES];
};
} __packed;
struct conf_hangover_settings {
u32 recover_time;
@ -1250,7 +1269,23 @@ struct conf_hangover_settings {
u8 quiet_time;
u8 increase_time;
u8 window_size;
};
} __packed;
/*
* The conf version consists of 4 bytes. The two MSB are the wlcore
* version, the two LSB are the lower driver's private conf
* version.
*/
#define WLCORE_CONF_VERSION (0x0001 << 16)
#define WLCORE_CONF_MASK 0xffff0000
#define WLCORE_CONF_SIZE (sizeof(struct wlcore_conf_header) + \
sizeof(struct wlcore_conf))
struct wlcore_conf_header {
__le32 magic;
__le32 version;
__le32 checksum;
} __packed;
struct wlcore_conf {
struct conf_sg_settings sg;
@ -1269,6 +1304,12 @@ struct wlcore_conf {
struct conf_fwlog fwlog;
struct conf_rate_policy_settings rate;
struct conf_hangover_settings hangover;
};
} __packed;
struct wlcore_conf_file {
struct wlcore_conf_header header;
struct wlcore_conf core;
u8 priv[0];
} __packed;
#endif

View File

@ -25,6 +25,7 @@
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/module.h>
#include "wlcore.h"
#include "debug.h"
@ -32,14 +33,14 @@
#include "ps.h"
#include "io.h"
#include "tx.h"
#include "hw_ops.h"
/* ms */
#define WL1271_DEBUGFS_STATS_LIFETIME 1000
/* debugfs macros idea from mac80211 */
#define DEBUGFS_FORMAT_BUFFER_SIZE 100
static int wl1271_format_buffer(char __user *userbuf, size_t count,
loff_t *ppos, char *fmt, ...)
int wl1271_format_buffer(char __user *userbuf, size_t count,
loff_t *ppos, char *fmt, ...)
{
va_list args;
char buf[DEBUGFS_FORMAT_BUFFER_SIZE];
@ -51,59 +52,9 @@ static int wl1271_format_buffer(char __user *userbuf, size_t count,
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
EXPORT_SYMBOL_GPL(wl1271_format_buffer);
#define DEBUGFS_READONLY_FILE(name, fmt, value...) \
static ssize_t name## _read(struct file *file, char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
struct wl1271 *wl = file->private_data; \
return wl1271_format_buffer(userbuf, count, ppos, \
fmt "\n", ##value); \
} \
\
static const struct file_operations name## _ops = { \
.read = name## _read, \
.open = simple_open, \
.llseek = generic_file_llseek, \
};
#define DEBUGFS_ADD(name, parent) \
entry = debugfs_create_file(#name, 0400, parent, \
wl, &name## _ops); \
if (!entry || IS_ERR(entry)) \
goto err; \
#define DEBUGFS_ADD_PREFIX(prefix, name, parent) \
do { \
entry = debugfs_create_file(#name, 0400, parent, \
wl, &prefix## _## name## _ops); \
if (!entry || IS_ERR(entry)) \
goto err; \
} while (0);
#define DEBUGFS_FWSTATS_FILE(sub, name, fmt) \
static ssize_t sub## _ ##name## _read(struct file *file, \
char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
struct wl1271 *wl = file->private_data; \
\
wl1271_debugfs_update_stats(wl); \
\
return wl1271_format_buffer(userbuf, count, ppos, fmt "\n", \
wl->stats.fw_stats->sub.name); \
} \
\
static const struct file_operations sub## _ ##name## _ops = { \
.read = sub## _ ##name## _read, \
.open = simple_open, \
.llseek = generic_file_llseek, \
};
#define DEBUGFS_FWSTATS_ADD(sub, name) \
DEBUGFS_ADD(sub## _ ##name, stats)
static void wl1271_debugfs_update_stats(struct wl1271 *wl)
void wl1271_debugfs_update_stats(struct wl1271 *wl)
{
int ret;
@ -125,97 +76,7 @@ static void wl1271_debugfs_update_stats(struct wl1271 *wl)
out:
mutex_unlock(&wl->mutex);
}
DEBUGFS_FWSTATS_FILE(tx, internal_desc_overflow, "%u");
DEBUGFS_FWSTATS_FILE(rx, out_of_mem, "%u");
DEBUGFS_FWSTATS_FILE(rx, hdr_overflow, "%u");
DEBUGFS_FWSTATS_FILE(rx, hw_stuck, "%u");
DEBUGFS_FWSTATS_FILE(rx, dropped, "%u");
DEBUGFS_FWSTATS_FILE(rx, fcs_err, "%u");
DEBUGFS_FWSTATS_FILE(rx, xfr_hint_trig, "%u");
DEBUGFS_FWSTATS_FILE(rx, path_reset, "%u");
DEBUGFS_FWSTATS_FILE(rx, reset_counter, "%u");
DEBUGFS_FWSTATS_FILE(dma, rx_requested, "%u");
DEBUGFS_FWSTATS_FILE(dma, rx_errors, "%u");
DEBUGFS_FWSTATS_FILE(dma, tx_requested, "%u");
DEBUGFS_FWSTATS_FILE(dma, tx_errors, "%u");
DEBUGFS_FWSTATS_FILE(isr, cmd_cmplt, "%u");
DEBUGFS_FWSTATS_FILE(isr, fiqs, "%u");
DEBUGFS_FWSTATS_FILE(isr, rx_headers, "%u");
DEBUGFS_FWSTATS_FILE(isr, rx_mem_overflow, "%u");
DEBUGFS_FWSTATS_FILE(isr, rx_rdys, "%u");
DEBUGFS_FWSTATS_FILE(isr, irqs, "%u");
DEBUGFS_FWSTATS_FILE(isr, tx_procs, "%u");
DEBUGFS_FWSTATS_FILE(isr, decrypt_done, "%u");
DEBUGFS_FWSTATS_FILE(isr, dma0_done, "%u");
DEBUGFS_FWSTATS_FILE(isr, dma1_done, "%u");
DEBUGFS_FWSTATS_FILE(isr, tx_exch_complete, "%u");
DEBUGFS_FWSTATS_FILE(isr, commands, "%u");
DEBUGFS_FWSTATS_FILE(isr, rx_procs, "%u");
DEBUGFS_FWSTATS_FILE(isr, hw_pm_mode_changes, "%u");
DEBUGFS_FWSTATS_FILE(isr, host_acknowledges, "%u");
DEBUGFS_FWSTATS_FILE(isr, pci_pm, "%u");
DEBUGFS_FWSTATS_FILE(isr, wakeups, "%u");
DEBUGFS_FWSTATS_FILE(isr, low_rssi, "%u");
DEBUGFS_FWSTATS_FILE(wep, addr_key_count, "%u");
DEBUGFS_FWSTATS_FILE(wep, default_key_count, "%u");
/* skipping wep.reserved */
DEBUGFS_FWSTATS_FILE(wep, key_not_found, "%u");
DEBUGFS_FWSTATS_FILE(wep, decrypt_fail, "%u");
DEBUGFS_FWSTATS_FILE(wep, packets, "%u");
DEBUGFS_FWSTATS_FILE(wep, interrupt, "%u");
DEBUGFS_FWSTATS_FILE(pwr, ps_enter, "%u");
DEBUGFS_FWSTATS_FILE(pwr, elp_enter, "%u");
DEBUGFS_FWSTATS_FILE(pwr, missing_bcns, "%u");
DEBUGFS_FWSTATS_FILE(pwr, wake_on_host, "%u");
DEBUGFS_FWSTATS_FILE(pwr, wake_on_timer_exp, "%u");
DEBUGFS_FWSTATS_FILE(pwr, tx_with_ps, "%u");
DEBUGFS_FWSTATS_FILE(pwr, tx_without_ps, "%u");
DEBUGFS_FWSTATS_FILE(pwr, rcvd_beacons, "%u");
DEBUGFS_FWSTATS_FILE(pwr, power_save_off, "%u");
DEBUGFS_FWSTATS_FILE(pwr, enable_ps, "%u");
DEBUGFS_FWSTATS_FILE(pwr, disable_ps, "%u");
DEBUGFS_FWSTATS_FILE(pwr, fix_tsf_ps, "%u");
/* skipping cont_miss_bcns_spread for now */
DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_beacons, "%u");
DEBUGFS_FWSTATS_FILE(mic, rx_pkts, "%u");
DEBUGFS_FWSTATS_FILE(mic, calc_failure, "%u");
DEBUGFS_FWSTATS_FILE(aes, encrypt_fail, "%u");
DEBUGFS_FWSTATS_FILE(aes, decrypt_fail, "%u");
DEBUGFS_FWSTATS_FILE(aes, encrypt_packets, "%u");
DEBUGFS_FWSTATS_FILE(aes, decrypt_packets, "%u");
DEBUGFS_FWSTATS_FILE(aes, encrypt_interrupt, "%u");
DEBUGFS_FWSTATS_FILE(aes, decrypt_interrupt, "%u");
DEBUGFS_FWSTATS_FILE(event, heart_beat, "%u");
DEBUGFS_FWSTATS_FILE(event, calibration, "%u");
DEBUGFS_FWSTATS_FILE(event, rx_mismatch, "%u");
DEBUGFS_FWSTATS_FILE(event, rx_mem_empty, "%u");
DEBUGFS_FWSTATS_FILE(event, rx_pool, "%u");
DEBUGFS_FWSTATS_FILE(event, oom_late, "%u");
DEBUGFS_FWSTATS_FILE(event, phy_transmit_error, "%u");
DEBUGFS_FWSTATS_FILE(event, tx_stuck, "%u");
DEBUGFS_FWSTATS_FILE(ps, pspoll_timeouts, "%u");
DEBUGFS_FWSTATS_FILE(ps, upsd_timeouts, "%u");
DEBUGFS_FWSTATS_FILE(ps, upsd_max_sptime, "%u");
DEBUGFS_FWSTATS_FILE(ps, upsd_max_apturn, "%u");
DEBUGFS_FWSTATS_FILE(ps, pspoll_max_apturn, "%u");
DEBUGFS_FWSTATS_FILE(ps, pspoll_utilization, "%u");
DEBUGFS_FWSTATS_FILE(ps, upsd_utilization, "%u");
DEBUGFS_FWSTATS_FILE(rxpipe, rx_prep_beacon_drop, "%u");
DEBUGFS_FWSTATS_FILE(rxpipe, descr_host_int_trig_rx_data, "%u");
DEBUGFS_FWSTATS_FILE(rxpipe, beacon_buffer_thres_host_int_trig_rx_data, "%u");
DEBUGFS_FWSTATS_FILE(rxpipe, missed_beacon_host_int_trig_rx_data, "%u");
DEBUGFS_FWSTATS_FILE(rxpipe, tx_xfr_host_int_trig_rx_data, "%u");
EXPORT_SYMBOL_GPL(wl1271_debugfs_update_stats);
DEBUGFS_READONLY_FILE(retry_count, "%u", wl->stats.retry_count);
DEBUGFS_READONLY_FILE(excessive_retries, "%u",
@ -241,6 +102,89 @@ static const struct file_operations tx_queue_len_ops = {
.llseek = default_llseek,
};
static void chip_op_handler(struct wl1271 *wl, unsigned long value,
void *arg)
{
int ret;
int (*chip_op) (struct wl1271 *wl);
if (!arg) {
wl1271_warning("debugfs chip_op_handler with no callback");
return;
}
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
return;
chip_op = arg;
chip_op(wl);
wl1271_ps_elp_sleep(wl);
}
static inline void no_write_handler(struct wl1271 *wl,
unsigned long value,
unsigned long param)
{
}
#define WL12XX_CONF_DEBUGFS(param, conf_sub_struct, \
min_val, max_val, write_handler_locked, \
write_handler_arg) \
static ssize_t param##_read(struct file *file, \
char __user *user_buf, \
size_t count, loff_t *ppos) \
{ \
struct wl1271 *wl = file->private_data; \
return wl1271_format_buffer(user_buf, count, \
ppos, "%d\n", \
wl->conf.conf_sub_struct.param); \
} \
\
static ssize_t param##_write(struct file *file, \
const char __user *user_buf, \
size_t count, loff_t *ppos) \
{ \
struct wl1271 *wl = file->private_data; \
unsigned long value; \
int ret; \
\
ret = kstrtoul_from_user(user_buf, count, 10, &value); \
if (ret < 0) { \
wl1271_warning("illegal value for " #param); \
return -EINVAL; \
} \
\
if (value < min_val || value > max_val) { \
wl1271_warning(#param " is not in valid range"); \
return -ERANGE; \
} \
\
mutex_lock(&wl->mutex); \
wl->conf.conf_sub_struct.param = value; \
\
write_handler_locked(wl, value, write_handler_arg); \
\
mutex_unlock(&wl->mutex); \
return count; \
} \
\
static const struct file_operations param##_ops = { \
.read = param##_read, \
.write = param##_write, \
.open = simple_open, \
.llseek = default_llseek, \
};
WL12XX_CONF_DEBUGFS(irq_pkt_threshold, rx, 0, 65535,
chip_op_handler, wl1271_acx_init_rx_interrupt)
WL12XX_CONF_DEBUGFS(irq_blk_threshold, rx, 0, 65535,
chip_op_handler, wl1271_acx_init_rx_interrupt)
WL12XX_CONF_DEBUGFS(irq_timeout, rx, 0, 100,
chip_op_handler, wl1271_acx_init_rx_interrupt)
static ssize_t gpio_power_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@ -535,8 +479,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf,
DRIVER_STATE_PRINT_LHEX(ap_ps_map);
DRIVER_STATE_PRINT_HEX(quirks);
DRIVER_STATE_PRINT_HEX(irq);
DRIVER_STATE_PRINT_HEX(ref_clock);
DRIVER_STATE_PRINT_HEX(tcxo_clock);
/* TODO: ref_clock and tcxo_clock were moved to wl12xx priv */
DRIVER_STATE_PRINT_HEX(hw_pg_ver);
DRIVER_STATE_PRINT_HEX(platform_quirks);
DRIVER_STATE_PRINT_HEX(chip.id);
@ -647,7 +590,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
VIF_STATE_PRINT_INT(last_rssi_event);
VIF_STATE_PRINT_INT(ba_support);
VIF_STATE_PRINT_INT(ba_allowed);
VIF_STATE_PRINT_INT(is_gem);
VIF_STATE_PRINT_LLHEX(tx_security_seq);
VIF_STATE_PRINT_INT(tx_security_last_seq_lsb);
}
@ -1002,108 +944,30 @@ static const struct file_operations beacon_filtering_ops = {
.llseek = default_llseek,
};
static ssize_t fw_stats_raw_read(struct file *file,
char __user *userbuf,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
wl1271_debugfs_update_stats(wl);
return simple_read_from_buffer(userbuf, count, ppos,
wl->stats.fw_stats,
wl->stats.fw_stats_len);
}
static const struct file_operations fw_stats_raw_ops = {
.read = fw_stats_raw_read,
.open = simple_open,
.llseek = default_llseek,
};
static int wl1271_debugfs_add_files(struct wl1271 *wl,
struct dentry *rootdir)
struct dentry *rootdir)
{
int ret = 0;
struct dentry *entry, *stats, *streaming;
stats = debugfs_create_dir("fw-statistics", rootdir);
if (!stats || IS_ERR(stats)) {
entry = stats;
goto err;
}
DEBUGFS_FWSTATS_ADD(tx, internal_desc_overflow);
DEBUGFS_FWSTATS_ADD(rx, out_of_mem);
DEBUGFS_FWSTATS_ADD(rx, hdr_overflow);
DEBUGFS_FWSTATS_ADD(rx, hw_stuck);
DEBUGFS_FWSTATS_ADD(rx, dropped);
DEBUGFS_FWSTATS_ADD(rx, fcs_err);
DEBUGFS_FWSTATS_ADD(rx, xfr_hint_trig);
DEBUGFS_FWSTATS_ADD(rx, path_reset);
DEBUGFS_FWSTATS_ADD(rx, reset_counter);
DEBUGFS_FWSTATS_ADD(dma, rx_requested);
DEBUGFS_FWSTATS_ADD(dma, rx_errors);
DEBUGFS_FWSTATS_ADD(dma, tx_requested);
DEBUGFS_FWSTATS_ADD(dma, tx_errors);
DEBUGFS_FWSTATS_ADD(isr, cmd_cmplt);
DEBUGFS_FWSTATS_ADD(isr, fiqs);
DEBUGFS_FWSTATS_ADD(isr, rx_headers);
DEBUGFS_FWSTATS_ADD(isr, rx_mem_overflow);
DEBUGFS_FWSTATS_ADD(isr, rx_rdys);
DEBUGFS_FWSTATS_ADD(isr, irqs);
DEBUGFS_FWSTATS_ADD(isr, tx_procs);
DEBUGFS_FWSTATS_ADD(isr, decrypt_done);
DEBUGFS_FWSTATS_ADD(isr, dma0_done);
DEBUGFS_FWSTATS_ADD(isr, dma1_done);
DEBUGFS_FWSTATS_ADD(isr, tx_exch_complete);
DEBUGFS_FWSTATS_ADD(isr, commands);
DEBUGFS_FWSTATS_ADD(isr, rx_procs);
DEBUGFS_FWSTATS_ADD(isr, hw_pm_mode_changes);
DEBUGFS_FWSTATS_ADD(isr, host_acknowledges);
DEBUGFS_FWSTATS_ADD(isr, pci_pm);
DEBUGFS_FWSTATS_ADD(isr, wakeups);
DEBUGFS_FWSTATS_ADD(isr, low_rssi);
DEBUGFS_FWSTATS_ADD(wep, addr_key_count);
DEBUGFS_FWSTATS_ADD(wep, default_key_count);
/* skipping wep.reserved */
DEBUGFS_FWSTATS_ADD(wep, key_not_found);
DEBUGFS_FWSTATS_ADD(wep, decrypt_fail);
DEBUGFS_FWSTATS_ADD(wep, packets);
DEBUGFS_FWSTATS_ADD(wep, interrupt);
DEBUGFS_FWSTATS_ADD(pwr, ps_enter);
DEBUGFS_FWSTATS_ADD(pwr, elp_enter);
DEBUGFS_FWSTATS_ADD(pwr, missing_bcns);
DEBUGFS_FWSTATS_ADD(pwr, wake_on_host);
DEBUGFS_FWSTATS_ADD(pwr, wake_on_timer_exp);
DEBUGFS_FWSTATS_ADD(pwr, tx_with_ps);
DEBUGFS_FWSTATS_ADD(pwr, tx_without_ps);
DEBUGFS_FWSTATS_ADD(pwr, rcvd_beacons);
DEBUGFS_FWSTATS_ADD(pwr, power_save_off);
DEBUGFS_FWSTATS_ADD(pwr, enable_ps);
DEBUGFS_FWSTATS_ADD(pwr, disable_ps);
DEBUGFS_FWSTATS_ADD(pwr, fix_tsf_ps);
/* skipping cont_miss_bcns_spread for now */
DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_beacons);
DEBUGFS_FWSTATS_ADD(mic, rx_pkts);
DEBUGFS_FWSTATS_ADD(mic, calc_failure);
DEBUGFS_FWSTATS_ADD(aes, encrypt_fail);
DEBUGFS_FWSTATS_ADD(aes, decrypt_fail);
DEBUGFS_FWSTATS_ADD(aes, encrypt_packets);
DEBUGFS_FWSTATS_ADD(aes, decrypt_packets);
DEBUGFS_FWSTATS_ADD(aes, encrypt_interrupt);
DEBUGFS_FWSTATS_ADD(aes, decrypt_interrupt);
DEBUGFS_FWSTATS_ADD(event, heart_beat);
DEBUGFS_FWSTATS_ADD(event, calibration);
DEBUGFS_FWSTATS_ADD(event, rx_mismatch);
DEBUGFS_FWSTATS_ADD(event, rx_mem_empty);
DEBUGFS_FWSTATS_ADD(event, rx_pool);
DEBUGFS_FWSTATS_ADD(event, oom_late);
DEBUGFS_FWSTATS_ADD(event, phy_transmit_error);
DEBUGFS_FWSTATS_ADD(event, tx_stuck);
DEBUGFS_FWSTATS_ADD(ps, pspoll_timeouts);
DEBUGFS_FWSTATS_ADD(ps, upsd_timeouts);
DEBUGFS_FWSTATS_ADD(ps, upsd_max_sptime);
DEBUGFS_FWSTATS_ADD(ps, upsd_max_apturn);
DEBUGFS_FWSTATS_ADD(ps, pspoll_max_apturn);
DEBUGFS_FWSTATS_ADD(ps, pspoll_utilization);
DEBUGFS_FWSTATS_ADD(ps, upsd_utilization);
DEBUGFS_FWSTATS_ADD(rxpipe, rx_prep_beacon_drop);
DEBUGFS_FWSTATS_ADD(rxpipe, descr_host_int_trig_rx_data);
DEBUGFS_FWSTATS_ADD(rxpipe, beacon_buffer_thres_host_int_trig_rx_data);
DEBUGFS_FWSTATS_ADD(rxpipe, missed_beacon_host_int_trig_rx_data);
DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data);
struct dentry *entry, *streaming;
DEBUGFS_ADD(tx_queue_len, rootdir);
DEBUGFS_ADD(retry_count, rootdir);
@ -1120,6 +984,10 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl,
DEBUGFS_ADD(dynamic_ps_timeout, rootdir);
DEBUGFS_ADD(forced_ps, rootdir);
DEBUGFS_ADD(split_scan_timeout, rootdir);
DEBUGFS_ADD(irq_pkt_threshold, rootdir);
DEBUGFS_ADD(irq_blk_threshold, rootdir);
DEBUGFS_ADD(irq_timeout, rootdir);
DEBUGFS_ADD(fw_stats_raw, rootdir);
streaming = debugfs_create_dir("rx_streaming", rootdir);
if (!streaming || IS_ERR(streaming))
@ -1145,7 +1013,7 @@ void wl1271_debugfs_reset(struct wl1271 *wl)
if (!wl->stats.fw_stats)
return;
memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats));
memset(wl->stats.fw_stats, 0, wl->stats.fw_stats_len);
wl->stats.retry_count = 0;
wl->stats.excessive_retries = 0;
}
@ -1160,34 +1028,34 @@ int wl1271_debugfs_init(struct wl1271 *wl)
if (IS_ERR(rootdir)) {
ret = PTR_ERR(rootdir);
goto err;
goto out;
}
wl->stats.fw_stats = kzalloc(sizeof(*wl->stats.fw_stats),
GFP_KERNEL);
wl->stats.fw_stats = kzalloc(wl->stats.fw_stats_len, GFP_KERNEL);
if (!wl->stats.fw_stats) {
ret = -ENOMEM;
goto err_fw;
goto out_remove;
}
wl->stats.fw_stats_update = jiffies;
ret = wl1271_debugfs_add_files(wl, rootdir);
if (ret < 0)
goto err_file;
goto out_exit;
return 0;
ret = wlcore_debugfs_init(wl, rootdir);
if (ret < 0)
goto out_exit;
err_file:
kfree(wl->stats.fw_stats);
wl->stats.fw_stats = NULL;
goto out;
err_fw:
out_exit:
wl1271_debugfs_exit(wl);
out_remove:
debugfs_remove_recursive(rootdir);
err:
out:
return ret;
}

View File

@ -26,8 +26,95 @@
#include "wlcore.h"
int wl1271_format_buffer(char __user *userbuf, size_t count,
loff_t *ppos, char *fmt, ...);
int wl1271_debugfs_init(struct wl1271 *wl);
void wl1271_debugfs_exit(struct wl1271 *wl);
void wl1271_debugfs_reset(struct wl1271 *wl);
void wl1271_debugfs_update_stats(struct wl1271 *wl);
#define DEBUGFS_FORMAT_BUFFER_SIZE 256
#define DEBUGFS_READONLY_FILE(name, fmt, value...) \
static ssize_t name## _read(struct file *file, char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
struct wl1271 *wl = file->private_data; \
return wl1271_format_buffer(userbuf, count, ppos, \
fmt "\n", ##value); \
} \
\
static const struct file_operations name## _ops = { \
.read = name## _read, \
.open = simple_open, \
.llseek = generic_file_llseek, \
};
#define DEBUGFS_ADD(name, parent) \
do { \
entry = debugfs_create_file(#name, 0400, parent, \
wl, &name## _ops); \
if (!entry || IS_ERR(entry)) \
goto err; \
} while (0);
#define DEBUGFS_ADD_PREFIX(prefix, name, parent) \
do { \
entry = debugfs_create_file(#name, 0400, parent, \
wl, &prefix## _## name## _ops); \
if (!entry || IS_ERR(entry)) \
goto err; \
} while (0);
#define DEBUGFS_FWSTATS_FILE(sub, name, fmt, struct_type) \
static ssize_t sub## _ ##name## _read(struct file *file, \
char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
struct wl1271 *wl = file->private_data; \
struct struct_type *stats = wl->stats.fw_stats; \
\
wl1271_debugfs_update_stats(wl); \
\
return wl1271_format_buffer(userbuf, count, ppos, fmt "\n", \
stats->sub.name); \
} \
\
static const struct file_operations sub## _ ##name## _ops = { \
.read = sub## _ ##name## _read, \
.open = simple_open, \
.llseek = generic_file_llseek, \
};
#define DEBUGFS_FWSTATS_FILE_ARRAY(sub, name, len, struct_type) \
static ssize_t sub## _ ##name## _read(struct file *file, \
char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
struct wl1271 *wl = file->private_data; \
struct struct_type *stats = wl->stats.fw_stats; \
char buf[DEBUGFS_FORMAT_BUFFER_SIZE] = ""; \
int res, i; \
\
wl1271_debugfs_update_stats(wl); \
\
for (i = 0; i < len; i++) \
res = snprintf(buf, sizeof(buf), "%s[%d] = %d\n", \
buf, i, stats->sub.name[i]); \
\
return wl1271_format_buffer(userbuf, count, ppos, "%s", buf); \
} \
\
static const struct file_operations sub## _ ##name## _ops = { \
.read = sub## _ ##name## _read, \
.open = simple_open, \
.llseek = generic_file_llseek, \
};
#define DEBUGFS_FWSTATS_ADD(sub, name) \
DEBUGFS_ADD(sub## _ ##name, stats)
#endif /* WL1271_DEBUGFS_H */

View File

@ -148,15 +148,33 @@ static int wl1271_event_process(struct wl1271 *wl)
int delay = wl->conf.conn.synch_fail_thold *
wl->conf.conn.bss_lose_timeout;
wl1271_info("Beacon loss detected.");
cancel_delayed_work_sync(&wl->connection_loss_work);
/*
* if the work is already queued, it should take place. We
* don't want to delay the connection loss indication
* any more.
*/
ieee80211_queue_delayed_work(wl->hw, &wl->connection_loss_work,
msecs_to_jiffies(delay));
msecs_to_jiffies(delay));
wl12xx_for_each_wlvif_sta(wl, wlvif) {
vif = wl12xx_wlvif_to_vif(wlvif);
ieee80211_cqm_rssi_notify(
vif,
NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
GFP_KERNEL);
}
}
if (vector & REGAINED_BSS_EVENT_ID) {
/* TODO: check for multi-role */
wl1271_info("Beacon regained.");
cancel_delayed_work_sync(&wl->connection_loss_work);
cancel_delayed_work(&wl->connection_loss_work);
/* sanity check - we can't lose and gain the beacon together */
WARN(vector & BSS_LOSE_EVENT_ID,
"Concurrent beacon loss and gain from FW");
}
if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {

View File

@ -119,4 +119,82 @@ static inline int wlcore_identify_fw(struct wl1271 *wl)
return 0;
}
static inline void
wlcore_hw_set_tx_desc_csum(struct wl1271 *wl,
struct wl1271_tx_hw_descr *desc,
struct sk_buff *skb)
{
if (!wl->ops->set_tx_desc_csum)
BUG_ON(1);
wl->ops->set_tx_desc_csum(wl, desc, skb);
}
static inline void
wlcore_hw_set_rx_csum(struct wl1271 *wl,
struct wl1271_rx_descriptor *desc,
struct sk_buff *skb)
{
if (wl->ops->set_rx_csum)
wl->ops->set_rx_csum(wl, desc, skb);
}
static inline u32
wlcore_hw_ap_get_mimo_wide_rate_mask(struct wl1271 *wl,
struct wl12xx_vif *wlvif)
{
if (wl->ops->ap_get_mimo_wide_rate_mask)
return wl->ops->ap_get_mimo_wide_rate_mask(wl, wlvif);
return 0;
}
static inline int
wlcore_debugfs_init(struct wl1271 *wl, struct dentry *rootdir)
{
if (wl->ops->debugfs_init)
return wl->ops->debugfs_init(wl, rootdir);
return 0;
}
static inline int
wlcore_handle_static_data(struct wl1271 *wl, void *static_data)
{
if (wl->ops->handle_static_data)
return wl->ops->handle_static_data(wl, static_data);
return 0;
}
static inline int
wlcore_hw_get_spare_blocks(struct wl1271 *wl, bool is_gem)
{
if (!wl->ops->get_spare_blocks)
BUG_ON(1);
return wl->ops->get_spare_blocks(wl, is_gem);
}
static inline int
wlcore_hw_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key_conf)
{
if (!wl->ops->set_key)
BUG_ON(1);
return wl->ops->set_key(wl, cmd, vif, sta, key_conf);
}
static inline u32
wlcore_hw_pre_pkt_send(struct wl1271 *wl, u32 buf_offset, u32 last_len)
{
if (wl->ops->pre_pkt_send)
return wl->ops->pre_pkt_send(wl, buf_offset, last_len);
return buf_offset;
}
#endif

View File

@ -460,6 +460,9 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif)
/* unconditionally enable HT rates */
supported_rates |= CONF_TX_MCS_RATES;
/* get extra MIMO or wide-chan rates where the HW supports it */
supported_rates |= wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif);
/* configure unicast TX rate classes */
for (i = 0; i < wl->conf.tx.ac_conf_count; i++) {
rc.enabled_rates = supported_rates;

View File

@ -320,46 +320,6 @@ static void wlcore_adjust_conf(struct wl1271 *wl)
}
}
static int wl1271_plt_init(struct wl1271 *wl)
{
int ret;
ret = wl->ops->hw_init(wl);
if (ret < 0)
return ret;
ret = wl1271_acx_init_mem_config(wl);
if (ret < 0)
return ret;
ret = wl12xx_acx_mem_cfg(wl);
if (ret < 0)
goto out_free_memmap;
/* Enable data path */
ret = wl1271_cmd_data_path(wl, 1);
if (ret < 0)
goto out_free_memmap;
/* Configure for CAM power saving (ie. always active) */
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
if (ret < 0)
goto out_free_memmap;
/* configure PM */
ret = wl1271_acx_pm_config(wl);
if (ret < 0)
goto out_free_memmap;
return 0;
out_free_memmap:
kfree(wl->target_mem_map);
wl->target_mem_map = NULL;
return ret;
}
static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
u8 hlid, u8 tx_pkts)
@ -387,7 +347,7 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
static void wl12xx_irq_update_links_status(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct wl_fw_status *status)
struct wl_fw_status_2 *status)
{
struct wl1271_link *lnk;
u32 cur_fw_ps_map;
@ -419,7 +379,8 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
}
static void wl12xx_fw_status(struct wl1271 *wl,
struct wl_fw_status *status)
struct wl_fw_status_1 *status_1,
struct wl_fw_status_2 *status_2)
{
struct wl12xx_vif *wlvif;
struct timespec ts;
@ -428,37 +389,38 @@ static void wl12xx_fw_status(struct wl1271 *wl,
int i;
size_t status_len;
status_len = sizeof(*status) + wl->fw_status_priv_len;
status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
sizeof(*status_2) + wl->fw_status_priv_len;
wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status,
wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status_1,
status_len, false);
wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
"drv_rx_counter = %d, tx_results_counter = %d)",
status->intr,
status->fw_rx_counter,
status->drv_rx_counter,
status->tx_results_counter);
status_1->intr,
status_1->fw_rx_counter,
status_1->drv_rx_counter,
status_1->tx_results_counter);
for (i = 0; i < NUM_TX_QUEUES; i++) {
/* prevent wrap-around in freed-packets counter */
wl->tx_allocated_pkts[i] -=
(status->counters.tx_released_pkts[i] -
(status_2->counters.tx_released_pkts[i] -
wl->tx_pkts_freed[i]) & 0xff;
wl->tx_pkts_freed[i] = status->counters.tx_released_pkts[i];
wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i];
}
/* prevent wrap-around in total blocks counter */
if (likely(wl->tx_blocks_freed <=
le32_to_cpu(status->total_released_blks)))
freed_blocks = le32_to_cpu(status->total_released_blks) -
le32_to_cpu(status_2->total_released_blks)))
freed_blocks = le32_to_cpu(status_2->total_released_blks) -
wl->tx_blocks_freed;
else
freed_blocks = 0x100000000LL - wl->tx_blocks_freed +
le32_to_cpu(status->total_released_blks);
le32_to_cpu(status_2->total_released_blks);
wl->tx_blocks_freed = le32_to_cpu(status->total_released_blks);
wl->tx_blocks_freed = le32_to_cpu(status_2->total_released_blks);
wl->tx_allocated_blocks -= freed_blocks;
@ -474,7 +436,7 @@ static void wl12xx_fw_status(struct wl1271 *wl,
cancel_delayed_work(&wl->tx_watchdog_work);
}
avail = le32_to_cpu(status->tx_total) - wl->tx_allocated_blocks;
avail = le32_to_cpu(status_2->tx_total) - wl->tx_allocated_blocks;
/*
* The FW might change the total number of TX memblocks before
@ -493,13 +455,13 @@ static void wl12xx_fw_status(struct wl1271 *wl,
/* for AP update num of allocated TX blocks per link and ps status */
wl12xx_for_each_wlvif_ap(wl, wlvif) {
wl12xx_irq_update_links_status(wl, wlvif, status);
wl12xx_irq_update_links_status(wl, wlvif, status_2);
}
/* update the host-chipset time offset */
getnstimeofday(&ts);
wl->time_offset = (timespec_to_ns(&ts) >> 10) -
(s64)le32_to_cpu(status->fw_localtime);
(s64)le32_to_cpu(status_2->fw_localtime);
}
static void wl1271_flush_deferred_work(struct wl1271 *wl)
@ -568,20 +530,30 @@ static irqreturn_t wl1271_irq(int irq, void *cookie)
clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
smp_mb__after_clear_bit();
wl12xx_fw_status(wl, wl->fw_status);
wl12xx_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
wlcore_hw_tx_immediate_compl(wl);
intr = le32_to_cpu(wl->fw_status->intr);
intr &= WL1271_INTR_MASK;
intr = le32_to_cpu(wl->fw_status_1->intr);
intr &= WLCORE_ALL_INTR_MASK;
if (!intr) {
done = true;
continue;
}
if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) {
wl1271_error("watchdog interrupt received! "
wl1271_error("HW watchdog interrupt received! starting recovery.");
wl->watchdog_recovery = true;
wl12xx_queue_recovery_work(wl);
/* restarting the chip. ignore any other interrupt. */
goto out;
}
if (unlikely(intr & WL1271_ACX_SW_INTR_WATCHDOG)) {
wl1271_error("SW watchdog interrupt received! "
"starting recovery.");
wl->watchdog_recovery = true;
wl12xx_queue_recovery_work(wl);
/* restarting the chip. ignore any other interrupt. */
@ -591,7 +563,7 @@ static irqreturn_t wl1271_irq(int irq, void *cookie)
if (likely(intr & WL1271_ACX_INTR_DATA)) {
wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
wl12xx_rx(wl, wl->fw_status);
wl12xx_rx(wl, wl->fw_status_1);
/* Check if any tx blocks were freed */
spin_lock_irqsave(&wl->wl_lock, flags);
@ -743,7 +715,7 @@ out:
return ret;
}
static int wl1271_fetch_nvs(struct wl1271 *wl)
static void wl1271_fetch_nvs(struct wl1271 *wl)
{
const struct firmware *fw;
int ret;
@ -751,16 +723,15 @@ static int wl1271_fetch_nvs(struct wl1271 *wl)
ret = request_firmware(&fw, WL12XX_NVS_NAME, wl->dev);
if (ret < 0) {
wl1271_error("could not get nvs file %s: %d", WL12XX_NVS_NAME,
ret);
return ret;
wl1271_debug(DEBUG_BOOT, "could not get nvs file %s: %d",
WL12XX_NVS_NAME, ret);
return;
}
wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
if (!wl->nvs) {
wl1271_error("could not allocate memory for the nvs file");
ret = -ENOMEM;
goto out;
}
@ -768,8 +739,6 @@ static int wl1271_fetch_nvs(struct wl1271 *wl)
out:
release_firmware(fw);
return ret;
}
void wl12xx_queue_recovery_work(struct wl1271 *wl)
@ -820,14 +789,16 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
/*
* Make sure the chip is awake and the logger isn't active.
* This might fail if the firmware hanged.
* Do not send a stop fwlog command if the fw is hanged.
*/
if (!wl1271_ps_elp_wakeup(wl))
if (!wl1271_ps_elp_wakeup(wl) && !wl->watchdog_recovery)
wl12xx_cmd_stop_fwlog(wl);
else
goto out;
/* Read the first memory block address */
wl12xx_fw_status(wl, wl->fw_status);
first_addr = le32_to_cpu(wl->fw_status->log_start_addr);
wl12xx_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
first_addr = le32_to_cpu(wl->fw_status_2->log_start_addr);
if (!first_addr)
goto out;
@ -872,9 +843,14 @@ static void wl1271_recovery_work(struct work_struct *work)
wl12xx_read_fwlog_panic(wl);
wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x",
/* change partitions momentarily so we can read the FW pc */
wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x "
"hint_sts: 0x%08x",
wl->chip.fw_ver_str,
wlcore_read_reg(wl, REG_PC_ON_RECOVERY));
wlcore_read_reg(wl, REG_PC_ON_RECOVERY),
wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR));
wlcore_set_partition(wl, &wl->ptable[PART_WORK]);
BUG_ON(bug_on_recovery &&
!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
@ -885,8 +861,6 @@ static void wl1271_recovery_work(struct work_struct *work)
goto out_unlock;
}
BUG_ON(bug_on_recovery);
/*
* Advance security sequence number to overcome potential progress
* in the firmware during recovery. This doens't hurt if the network is
@ -900,7 +874,7 @@ static void wl1271_recovery_work(struct work_struct *work)
}
/* Prevent spurious TX during FW restart */
ieee80211_stop_queues(wl->hw);
wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
if (wl->sched_scanning) {
ieee80211_sched_scan_stopped(wl->hw);
@ -914,6 +888,7 @@ static void wl1271_recovery_work(struct work_struct *work)
vif = wl12xx_wlvif_to_vif(wlvif);
__wl1271_op_remove_interface(wl, vif, false);
}
wl->watchdog_recovery = false;
mutex_unlock(&wl->mutex);
wl1271_op_stop(wl->hw);
@ -925,9 +900,10 @@ static void wl1271_recovery_work(struct work_struct *work)
* Its safe to enable TX now - the queues are stopped after a request
* to restart the HW.
*/
ieee80211_wake_queues(wl->hw);
wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
return;
out_unlock:
wl->watchdog_recovery = false;
mutex_unlock(&wl->mutex);
}
@ -938,13 +914,19 @@ static void wl1271_fw_wakeup(struct wl1271 *wl)
static int wl1271_setup(struct wl1271 *wl)
{
wl->fw_status = kmalloc(sizeof(*wl->fw_status), GFP_KERNEL);
if (!wl->fw_status)
wl->fw_status_1 = kmalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
sizeof(*wl->fw_status_2) +
wl->fw_status_priv_len, GFP_KERNEL);
if (!wl->fw_status_1)
return -ENOMEM;
wl->fw_status_2 = (struct wl_fw_status_2 *)
(((u8 *) wl->fw_status_1) +
WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc));
wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
if (!wl->tx_res_if) {
kfree(wl->fw_status);
kfree(wl->fw_status_1);
return -ENOMEM;
}
@ -987,13 +969,12 @@ static int wl12xx_chip_wakeup(struct wl1271 *wl, bool plt)
* simplify the code and since the performance impact is
* negligible, we use the same block size for all different
* chip types.
*
* Check if the bus supports blocksize alignment and, if it
* doesn't, make sure we don't have the quirk.
*/
if (wl1271_set_block_size(wl))
wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
ret = wl->ops->identify_chip(wl);
if (ret < 0)
goto out;
if (!wl1271_set_block_size(wl))
wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;
/* TODO: make sure the lower driver has set things up correctly */
@ -1005,13 +986,6 @@ static int wl12xx_chip_wakeup(struct wl1271 *wl, bool plt)
if (ret < 0)
goto out;
/* No NVS from netlink, try to get it from the filesystem */
if (wl->nvs == NULL) {
ret = wl1271_fetch_nvs(wl);
if (ret < 0)
goto out;
}
out:
return ret;
}
@ -1039,14 +1013,10 @@ int wl1271_plt_start(struct wl1271 *wl)
if (ret < 0)
goto power_off;
ret = wl->ops->boot(wl);
ret = wl->ops->plt_init(wl);
if (ret < 0)
goto power_off;
ret = wl1271_plt_init(wl);
if (ret < 0)
goto irq_disable;
wl->plt = true;
wl->state = WL1271_STATE_ON;
wl1271_notice("firmware booted in PLT mode (%s)",
@ -1059,19 +1029,6 @@ int wl1271_plt_start(struct wl1271 *wl)
goto out;
irq_disable:
mutex_unlock(&wl->mutex);
/* Unlocking the mutex in the middle of handling is
inherently unsafe. In this case we deem it safe to do,
because we need to let any possibly pending IRQ out of
the system (and while we are WL1271_STATE_OFF the IRQ
work function will not do anything.) Also, any other
possible concurrent operations will fail due to the
current state, hence the wl1271 struct should be safe. */
wlcore_disable_interrupts(wl);
wl1271_flush_deferred_work(wl);
cancel_work_sync(&wl->netstack_work);
mutex_lock(&wl->mutex);
power_off:
wl1271_power_off(wl);
}
@ -1154,9 +1111,16 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
spin_lock_irqsave(&wl->wl_lock, flags);
/* queue the packet */
/*
* drop the packet if the link is invalid or the queue is stopped
* for any reason but watermark. Watermark is a "soft"-stop so we
* allow these packets through.
*/
if (hlid == WL12XX_INVALID_LINK_ID ||
(wlvif && !test_bit(hlid, wlvif->links_map))) {
(wlvif && !test_bit(hlid, wlvif->links_map)) ||
(wlcore_is_queue_stopped(wl, q) &&
!wlcore_is_queue_stopped_by_reason(wl, q,
WLCORE_QUEUE_STOP_REASON_WATERMARK))) {
wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q);
ieee80211_free_txskb(hw, skb);
goto out;
@ -1174,8 +1138,8 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
*/
if (wl->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q);
ieee80211_stop_queue(wl->hw, mapping);
set_bit(q, &wl->stopped_queues_map);
wlcore_stop_queue_locked(wl, q,
WLCORE_QUEUE_STOP_REASON_WATERMARK);
}
/*
@ -1758,7 +1722,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
cancel_delayed_work_sync(&wl->connection_loss_work);
/* let's notify MAC80211 about the remaining pending TX frames */
wl12xx_tx_reset(wl, true);
wl12xx_tx_reset(wl);
mutex_lock(&wl->mutex);
wl1271_power_off(wl);
@ -1767,6 +1731,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
wl->rx_counter = 0;
wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
wl->channel_type = NL80211_CHAN_NO_HT;
wl->tx_blocks_available = 0;
wl->tx_allocated_blocks = 0;
wl->tx_results_count = 0;
@ -1799,8 +1764,9 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
wl1271_debugfs_reset(wl);
kfree(wl->fw_status);
wl->fw_status = NULL;
kfree(wl->fw_status_1);
wl->fw_status_1 = NULL;
wl->fw_status_2 = NULL;
kfree(wl->tx_res_if);
wl->tx_res_if = NULL;
kfree(wl->target_mem_map);
@ -1894,6 +1860,9 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
wl12xx_allocate_rate_policy(wl, &wlvif->sta.basic_rate_idx);
wl12xx_allocate_rate_policy(wl, &wlvif->sta.ap_rate_idx);
wl12xx_allocate_rate_policy(wl, &wlvif->sta.p2p_rate_idx);
wlvif->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
wlvif->basic_rate = CONF_TX_RATE_MASK_BASIC;
wlvif->rate_set = CONF_TX_RATE_MASK_BASIC;
} else {
/* init ap data */
wlvif->ap.bcast_hlid = WL12XX_INVALID_LINK_ID;
@ -1903,13 +1872,19 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
wl12xx_allocate_rate_policy(wl,
&wlvif->ap.ucast_rate_idx[i]);
wlvif->basic_rate_set = CONF_TX_AP_ENABLED_RATES;
/*
* TODO: check if basic_rate shouldn't be
* wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
* instead (the same thing for STA above).
*/
wlvif->basic_rate = CONF_TX_AP_ENABLED_RATES;
/* TODO: this seems to be used only for STA, check it */
wlvif->rate_set = CONF_TX_AP_ENABLED_RATES;
}
wlvif->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate;
wlvif->bitrate_masks[IEEE80211_BAND_5GHZ] = wl->conf.tx.basic_rate_5;
wlvif->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
wlvif->basic_rate = CONF_TX_RATE_MASK_BASIC;
wlvif->rate_set = CONF_TX_RATE_MASK_BASIC;
wlvif->beacon_int = WL1271_DEFAULT_BEACON_INT;
/*
@ -1919,6 +1894,7 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
wlvif->band = wl->band;
wlvif->channel = wl->channel;
wlvif->power_level = wl->power_level;
wlvif->channel_type = wl->channel_type;
INIT_WORK(&wlvif->rx_streaming_enable_work,
wl1271_rx_streaming_enable_work);
@ -2444,7 +2420,7 @@ static int wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif,
} else {
/* The current firmware only supports sched_scan in idle */
if (wl->sched_scanning) {
wl1271_scan_sched_scan_stop(wl);
wl1271_scan_sched_scan_stop(wl, wlvif);
ieee80211_sched_scan_stopped(wl->hw);
}
@ -2469,13 +2445,20 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
/* if the channel changes while joined, join again */
if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
((wlvif->band != conf->channel->band) ||
(wlvif->channel != channel))) {
(wlvif->channel != channel) ||
(wlvif->channel_type != conf->channel_type))) {
/* send all pending packets */
wl1271_tx_work_locked(wl);
wlvif->band = conf->channel->band;
wlvif->channel = channel;
wlvif->channel_type = conf->channel_type;
if (!is_ap) {
if (is_ap) {
ret = wl1271_init_ap_rates(wl, wlvif);
if (ret < 0)
wl1271_error("AP rate policy change failed %d",
ret);
} else {
/*
* FIXME: the mac80211 should really provide a fixed
* rate to use here. for now, just use the smallest
@ -2583,8 +2566,9 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
* frames, such as the deauth. To make sure those frames reach the air,
* wait here until the TX queue is fully flushed.
*/
if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
(conf->flags & IEEE80211_CONF_IDLE))
if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) ||
((changed & IEEE80211_CONF_CHANGE_IDLE) &&
(conf->flags & IEEE80211_CONF_IDLE)))
wl1271_tx_flush(wl);
mutex_lock(&wl->mutex);
@ -2593,6 +2577,7 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
wl->band = conf->channel->band;
wl->channel = channel;
wl->channel_type = conf->channel_type;
}
if (changed & IEEE80211_CONF_CHANGE_POWER)
@ -2825,17 +2810,6 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
int ret;
bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
/*
* A role set to GEM cipher requires different Tx settings (namely
* spare blocks). Note when we are in this mode so the HW can adjust.
*/
if (key_type == KEY_GEM) {
if (action == KEY_ADD_OR_REPLACE)
wlvif->is_gem = true;
else if (action == KEY_REMOVE)
wlvif->is_gem = false;
}
if (is_ap) {
struct wl1271_station *wl_sta;
u8 hlid;
@ -2913,12 +2887,21 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
return 0;
}
static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
static int wlcore_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key_conf)
{
struct wl1271 *wl = hw->priv;
return wlcore_hw_set_key(wl, cmd, vif, sta, key_conf);
}
int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key_conf)
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
int ret;
u32 tx_seq_32 = 0;
@ -3029,6 +3012,7 @@ out_unlock:
return ret;
}
EXPORT_SYMBOL_GPL(wlcore_set_key);
static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
@ -3167,6 +3151,7 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
int ret;
wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_stop");
@ -3180,7 +3165,7 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
if (ret < 0)
goto out;
wl1271_scan_sched_scan_stop(wl);
wl1271_scan_sched_scan_stop(wl, wlvif);
wl1271_ps_elp_sleep(wl);
out:
@ -3316,8 +3301,15 @@ static int wl1271_ap_set_probe_resp_tmpl(struct wl1271 *wl, u32 rates,
skb->data,
skb->len, 0,
rates);
dev_kfree_skb(skb);
if (ret < 0)
goto out;
wl1271_debug(DEBUG_AP, "probe response updated");
set_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags);
out:
return ret;
}
@ -3422,6 +3414,87 @@ out:
return ret;
}
static int wlcore_set_beacon_template(struct wl1271 *wl,
struct ieee80211_vif *vif,
bool is_ap)
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
struct ieee80211_hdr *hdr;
u32 min_rate;
int ret;
int ieoffset = offsetof(struct ieee80211_mgmt,
u.beacon.variable);
struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
u16 tmpl_id;
if (!beacon) {
ret = -EINVAL;
goto out;
}
wl1271_debug(DEBUG_MASTER, "beacon updated");
ret = wl1271_ssid_set(vif, beacon, ieoffset);
if (ret < 0) {
dev_kfree_skb(beacon);
goto out;
}
min_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON :
CMD_TEMPL_BEACON;
ret = wl1271_cmd_template_set(wl, wlvif->role_id, tmpl_id,
beacon->data,
beacon->len, 0,
min_rate);
if (ret < 0) {
dev_kfree_skb(beacon);
goto out;
}
/*
* In case we already have a probe-resp beacon set explicitly
* by usermode, don't use the beacon data.
*/
if (test_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags))
goto end_bcn;
/* remove TIM ie from probe response */
wl12xx_remove_ie(beacon, WLAN_EID_TIM, ieoffset);
/*
* remove p2p ie from probe response.
* the fw reponds to probe requests that don't include
* the p2p ie. probe requests with p2p ie will be passed,
* and will be responded by the supplicant (the spec
* forbids including the p2p ie when responding to probe
* requests that didn't include it).
*/
wl12xx_remove_vendor_ie(beacon, WLAN_OUI_WFA,
WLAN_OUI_TYPE_WFA_P2P, ieoffset);
hdr = (struct ieee80211_hdr *) beacon->data;
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_PROBE_RESP);
if (is_ap)
ret = wl1271_ap_set_probe_resp_tmpl_legacy(wl, vif,
beacon->data,
beacon->len,
min_rate);
else
ret = wl1271_cmd_template_set(wl, wlvif->role_id,
CMD_TEMPL_PROBE_RESPONSE,
beacon->data,
beacon->len, 0,
min_rate);
end_bcn:
dev_kfree_skb(beacon);
if (ret < 0)
goto out;
out:
return ret;
}
static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,
@ -3440,81 +3513,12 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl,
if ((changed & BSS_CHANGED_AP_PROBE_RESP) && is_ap) {
u32 rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
if (!wl1271_ap_set_probe_resp_tmpl(wl, rate, vif)) {
wl1271_debug(DEBUG_AP, "probe response updated");
set_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags);
}
wl1271_ap_set_probe_resp_tmpl(wl, rate, vif);
}
if ((changed & BSS_CHANGED_BEACON)) {
struct ieee80211_hdr *hdr;
u32 min_rate;
int ieoffset = offsetof(struct ieee80211_mgmt,
u.beacon.variable);
struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif);
u16 tmpl_id;
if (!beacon) {
ret = -EINVAL;
goto out;
}
wl1271_debug(DEBUG_MASTER, "beacon updated");
ret = wl1271_ssid_set(vif, beacon, ieoffset);
if (ret < 0) {
dev_kfree_skb(beacon);
goto out;
}
min_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON :
CMD_TEMPL_BEACON;
ret = wl1271_cmd_template_set(wl, wlvif->role_id, tmpl_id,
beacon->data,
beacon->len, 0,
min_rate);
if (ret < 0) {
dev_kfree_skb(beacon);
goto out;
}
/*
* In case we already have a probe-resp beacon set explicitly
* by usermode, don't use the beacon data.
*/
if (test_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags))
goto end_bcn;
/* remove TIM ie from probe response */
wl12xx_remove_ie(beacon, WLAN_EID_TIM, ieoffset);
/*
* remove p2p ie from probe response.
* the fw reponds to probe requests that don't include
* the p2p ie. probe requests with p2p ie will be passed,
* and will be responded by the supplicant (the spec
* forbids including the p2p ie when responding to probe
* requests that didn't include it).
*/
wl12xx_remove_vendor_ie(beacon, WLAN_OUI_WFA,
WLAN_OUI_TYPE_WFA_P2P, ieoffset);
hdr = (struct ieee80211_hdr *) beacon->data;
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_PROBE_RESP);
if (is_ap)
ret = wl1271_ap_set_probe_resp_tmpl_legacy(wl, vif,
beacon->data,
beacon->len,
min_rate);
else
ret = wl1271_cmd_template_set(wl, wlvif->role_id,
CMD_TEMPL_PROBE_RESPONSE,
beacon->data,
beacon->len, 0,
min_rate);
end_bcn:
dev_kfree_skb(beacon);
ret = wlcore_set_beacon_template(wl, vif, is_ap);
if (ret < 0)
goto out;
}
@ -3551,6 +3555,14 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
ret = wl1271_ap_init_templates(wl, vif);
if (ret < 0)
goto out;
ret = wl1271_ap_set_probe_resp_tmpl(wl, wlvif->basic_rate, vif);
if (ret < 0)
goto out;
ret = wlcore_set_beacon_template(wl, vif, true);
if (ret < 0)
goto out;
}
ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed);
@ -3691,7 +3703,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
sta_rate_set = sta->supp_rates[wl->hw->conf.channel->band];
if (sta->ht_cap.ht_supported)
sta_rate_set |=
(sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET);
(sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) |
(sta->ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET);
sta_ht_cap = sta->ht_cap;
sta_exists = true;
@ -3704,13 +3717,11 @@ sta_not_found:
u32 rates;
int ieoffset;
wlvif->aid = bss_conf->aid;
wlvif->channel_type = bss_conf->channel_type;
wlvif->beacon_int = bss_conf->beacon_int;
do_join = true;
set_assoc = true;
/* Cancel connection_loss_work */
cancel_delayed_work_sync(&wl->connection_loss_work);
/*
* use basic rates from AP, and determine lowest rate
* to use with control frames.
@ -3960,6 +3971,17 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x",
(int)changed);
/*
* make sure to cancel pending disconnections if our association
* state changed
*/
if (!is_ap && (changed & BSS_CHANGED_ASSOC))
cancel_delayed_work_sync(&wl->connection_loss_work);
if (is_ap && (changed & BSS_CHANGED_BEACON_ENABLED) &&
!bss_conf->enable_beacon)
wl1271_tx_flush(wl);
mutex_lock(&wl->mutex);
if (unlikely(wl->state == WL1271_STATE_OFF))
@ -4636,7 +4658,7 @@ static const struct ieee80211_ops wl1271_ops = {
.prepare_multicast = wl1271_op_prepare_multicast,
.configure_filter = wl1271_op_configure_filter,
.tx = wl1271_op_tx,
.set_key = wl1271_op_set_key,
.set_key = wlcore_op_set_key,
.hw_scan = wl1271_op_hw_scan,
.cancel_hw_scan = wl1271_op_cancel_hw_scan,
.sched_scan_start = wl1271_op_sched_scan_start,
@ -4905,14 +4927,8 @@ static int wl1271_register_hw(struct wl1271 *wl)
if (wl->mac80211_registered)
return 0;
ret = wl12xx_get_hw_info(wl);
if (ret < 0) {
wl1271_error("couldn't get hw info");
goto out;
}
ret = wl1271_fetch_nvs(wl);
if (ret == 0) {
wl1271_fetch_nvs(wl);
if (wl->nvs != NULL) {
/* NOTE: The wl->nvs->nvs element must be first, in
* order to simplify the casting, we assume it is at
* the beginning of the wl->nvs structure.
@ -4970,9 +4986,11 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
WL1271_CIPHER_SUITE_GEM,
};
/* The tx descriptor buffer and the TKIP space. */
wl->hw->extra_tx_headroom = WL1271_EXTRA_SPACE_TKIP +
sizeof(struct wl1271_tx_hw_descr);
/* The tx descriptor buffer */
wl->hw->extra_tx_headroom = sizeof(struct wl1271_tx_hw_descr);
if (wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE)
wl->hw->extra_tx_headroom += WL1271_EXTRA_SPACE_TKIP;
/* unit us */
/* FIXME: find a proper value */
@ -5025,12 +5043,14 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
*/
memcpy(&wl->bands[IEEE80211_BAND_2GHZ], &wl1271_band_2ghz,
sizeof(wl1271_band_2ghz));
memcpy(&wl->bands[IEEE80211_BAND_2GHZ].ht_cap, &wl->ht_cap,
sizeof(wl->ht_cap));
memcpy(&wl->bands[IEEE80211_BAND_2GHZ].ht_cap,
&wl->ht_cap[IEEE80211_BAND_2GHZ],
sizeof(*wl->ht_cap));
memcpy(&wl->bands[IEEE80211_BAND_5GHZ], &wl1271_band_5ghz,
sizeof(wl1271_band_5ghz));
memcpy(&wl->bands[IEEE80211_BAND_5GHZ].ht_cap, &wl->ht_cap,
sizeof(wl->ht_cap));
memcpy(&wl->bands[IEEE80211_BAND_5GHZ].ht_cap,
&wl->ht_cap[IEEE80211_BAND_5GHZ],
sizeof(*wl->ht_cap));
wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
&wl->bands[IEEE80211_BAND_2GHZ];
@ -5117,6 +5137,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size)
wl->rx_counter = 0;
wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
wl->band = IEEE80211_BAND_2GHZ;
wl->channel_type = NL80211_CHAN_NO_HT;
wl->flags = 0;
wl->sg_enabled = true;
wl->hw_pg_ver = -1;
@ -5142,6 +5163,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size)
wl->state = WL1271_STATE_OFF;
wl->fw_type = WL12XX_FW_TYPE_NONE;
mutex_init(&wl->mutex);
mutex_init(&wl->flush_mutex);
order = get_order(WL1271_AGGR_BUFFER_SIZE);
wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
@ -5222,7 +5244,7 @@ int wlcore_free_hw(struct wl1271 *wl)
kfree(wl->nvs);
wl->nvs = NULL;
kfree(wl->fw_status);
kfree(wl->fw_status_1);
kfree(wl->tx_res_if);
destroy_workqueue(wl->freezable_wq);
@ -5279,8 +5301,6 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
wlcore_adjust_conf(wl);
wl->irq = platform_get_irq(pdev, 0);
wl->ref_clock = pdata->board_ref_clock;
wl->tcxo_clock = pdata->board_tcxo_clock;
wl->platform_quirks = pdata->platform_quirks;
wl->set_power = pdata->set_power;
wl->dev = &pdev->dev;
@ -5316,6 +5336,16 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
}
disable_irq(wl->irq);
ret = wl12xx_get_hw_info(wl);
if (ret < 0) {
wl1271_error("couldn't get hw info");
goto out;
}
ret = wl->ops->identify_chip(wl);
if (ret < 0)
goto out;
ret = wl1271_init_ieee80211(wl);
if (ret)
goto out_irq;

View File

@ -28,6 +28,8 @@
#define WL1271_WAKEUP_TIMEOUT 500
#define ELP_ENTRY_DELAY 5
void wl1271_elp_work(struct work_struct *work)
{
struct delayed_work *dwork;
@ -72,6 +74,7 @@ out:
void wl1271_ps_elp_sleep(struct wl1271 *wl)
{
struct wl12xx_vif *wlvif;
u32 timeout;
if (wl->quirks & WLCORE_QUIRK_NO_ELP)
return;
@ -89,8 +92,13 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl)
return;
}
if (wl->conf.conn.forced_ps)
timeout = ELP_ENTRY_DELAY;
else
timeout = wl->conf.conn.dynamic_ps_timeout;
ieee80211_queue_delayed_work(wl->hw, &wl->elp_work,
msecs_to_jiffies(wl->conf.conn.dynamic_ps_timeout));
msecs_to_jiffies(timeout));
}
int wl1271_ps_elp_wakeup(struct wl1271 *wl)
@ -185,8 +193,12 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
set_bit(WLVIF_FLAG_IN_PS, &wlvif->flags);
/* enable beacon early termination. Not relevant for 5GHz */
if (wlvif->band == IEEE80211_BAND_2GHZ) {
/*
* enable beacon early termination.
* Not relevant for 5GHz and for high rates.
*/
if ((wlvif->band == IEEE80211_BAND_2GHZ) &&
(wlvif->basic_rate < CONF_HW_BIT_RATE_9MBPS)) {
ret = wl1271_acx_bet_enable(wl, wlvif, true);
if (ret < 0)
return ret;
@ -196,7 +208,8 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif,
wl1271_debug(DEBUG_PSM, "leaving psm");
/* disable beacon early termination */
if (wlvif->band == IEEE80211_BAND_2GHZ) {
if ((wlvif->band == IEEE80211_BAND_2GHZ) &&
(wlvif->basic_rate < CONF_HW_BIT_RATE_9MBPS)) {
ret = wl1271_acx_bet_enable(wl, wlvif, false);
if (ret < 0)
return ret;

View File

@ -186,6 +186,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
is_data = 1;
wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon);
wlcore_hw_set_rx_csum(wl, desc, skb);
seq_num = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s seq %d hlid %d", skb,
@ -199,12 +200,12 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
return is_data;
}
void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status)
void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status_1 *status)
{
unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
u32 buf_size;
u32 fw_rx_counter = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK;
u32 fw_rx_counter = status->fw_rx_counter % wl->num_rx_desc;
u32 drv_rx_counter = wl->rx_counter % wl->num_rx_desc;
u32 rx_counter;
u32 pkt_len, align_pkt_len;
u32 pkt_offset, des;
@ -223,7 +224,7 @@ void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status)
break;
buf_size += align_pkt_len;
rx_counter++;
rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
rx_counter %= wl->num_rx_desc;
}
if (buf_size == 0) {
@ -263,7 +264,7 @@ void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status)
wl->rx_counter++;
drv_rx_counter++;
drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK;
drv_rx_counter %= wl->num_rx_desc;
pkt_offset += wlcore_rx_get_align_buf_size(wl, pkt_len);
}
}

View File

@ -38,8 +38,6 @@
#define RX_DESC_PACKETID_SHIFT 11
#define RX_MAX_PACKET_ID 3
#define NUM_RX_PKT_DESC_MOD_MASK 7
#define RX_DESC_VALID_FCS 0x0001
#define RX_DESC_MATCH_RXADDR1 0x0002
#define RX_DESC_MCAST 0x0004
@ -102,6 +100,9 @@
/* If set, the start of IP payload is not 4 bytes aligned */
#define RX_BUF_UNALIGNED_PAYLOAD BIT(20)
/* If set, the buffer was padded by the FW to be 4 bytes aligned */
#define RX_BUF_PADDED_PAYLOAD BIT(30)
/* Describes the alignment state of a Rx buffer */
enum wl_rx_buf_align {
WLCORE_RX_BUF_ALIGNED,
@ -136,7 +137,7 @@ struct wl1271_rx_descriptor {
u8 reserved;
} __packed;
void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status);
void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status_1 *status);
u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band);
int wl1271_rx_filter_enable(struct wl1271 *wl,
int index, bool enable,

View File

@ -411,7 +411,8 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
struct cfg80211_sched_scan_request *req,
struct conn_scan_ch_params *channels,
u32 band, bool radar, bool passive,
int start, int max_channels)
int start, int max_channels,
u8 *n_pactive_ch)
{
struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
int i, j;
@ -479,6 +480,23 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
channels[j].tx_power_att = req->channels[i]->max_power;
channels[j].channel = req->channels[i]->hw_value;
if ((band == IEEE80211_BAND_2GHZ) &&
(channels[j].channel >= 12) &&
(channels[j].channel <= 14) &&
(flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
!force_passive) {
/* pactive channels treated as DFS */
channels[j].flags = SCAN_CHANNEL_FLAGS_DFS;
/*
* n_pactive_ch is counted down from the end of
* the passive channel list
*/
(*n_pactive_ch)++;
wl1271_debug(DEBUG_SCAN, "n_pactive_ch = %d",
*n_pactive_ch);
}
j++;
}
}
@ -491,38 +509,47 @@ wl1271_scan_sched_scan_channels(struct wl1271 *wl,
struct cfg80211_sched_scan_request *req,
struct wl1271_cmd_sched_scan_config *cfg)
{
u8 n_pactive_ch = 0;
cfg->passive[0] =
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
IEEE80211_BAND_2GHZ,
false, true, 0,
MAX_CHANNELS_2GHZ);
MAX_CHANNELS_2GHZ,
&n_pactive_ch);
cfg->active[0] =
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
IEEE80211_BAND_2GHZ,
false, false,
cfg->passive[0],
MAX_CHANNELS_2GHZ);
MAX_CHANNELS_2GHZ,
&n_pactive_ch);
cfg->passive[1] =
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
IEEE80211_BAND_5GHZ,
false, true, 0,
MAX_CHANNELS_5GHZ);
MAX_CHANNELS_5GHZ,
&n_pactive_ch);
cfg->dfs =
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
IEEE80211_BAND_5GHZ,
true, true,
cfg->passive[1],
MAX_CHANNELS_5GHZ);
MAX_CHANNELS_5GHZ,
&n_pactive_ch);
cfg->active[1] =
wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
IEEE80211_BAND_5GHZ,
false, false,
cfg->passive[1] + cfg->dfs,
MAX_CHANNELS_5GHZ);
MAX_CHANNELS_5GHZ,
&n_pactive_ch);
/* 802.11j channels are not supported yet */
cfg->passive[2] = 0;
cfg->active[2] = 0;
cfg->n_pactive_ch = n_pactive_ch;
wl1271_debug(DEBUG_SCAN, " 2.4GHz: active %d passive %d",
cfg->active[0], cfg->passive[0]);
wl1271_debug(DEBUG_SCAN, " 5GHz: active %d passive %d",
@ -537,6 +564,7 @@ wl1271_scan_sched_scan_channels(struct wl1271 *wl,
/* Returns the scan type to be used or a negative value on error */
static int
wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req)
{
struct wl1271_cmd_sched_scan_ssid_list *cmd = NULL;
@ -565,6 +593,7 @@ wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl,
goto out;
}
cmd->role_id = wlvif->dev_role_id;
if (!n_match_ssids) {
/* No filter, with ssids */
type = SCAN_SSID_FILTER_DISABLED;
@ -603,7 +632,9 @@ wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl,
continue;
for (j = 0; j < cmd->n_ssids; j++)
if (!memcmp(req->ssids[i].ssid,
if ((req->ssids[i].ssid_len ==
req->ssids[j].ssid_len) &&
!memcmp(req->ssids[i].ssid,
cmd->ssids[j].ssid,
req->ssids[i].ssid_len)) {
cmd->ssids[j].type =
@ -652,6 +683,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
if (!cfg)
return -ENOMEM;
cfg->role_id = wlvif->dev_role_id;
cfg->rssi_threshold = c->rssi_threshold;
cfg->snr_threshold = c->snr_threshold;
cfg->n_probe_reqs = c->num_probe_reqs;
@ -669,7 +701,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
cfg->intervals[i] = cpu_to_le32(req->interval);
cfg->ssid_len = 0;
ret = wl12xx_scan_sched_scan_ssid_list(wl, req);
ret = wl12xx_scan_sched_scan_ssid_list(wl, wlvif, req);
if (ret < 0)
goto out;
@ -741,6 +773,7 @@ int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif)
if (!start)
return -ENOMEM;
start->role_id = wlvif->dev_role_id;
start->tag = WL1271_SCAN_DEFAULT_TAG;
ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start,
@ -762,7 +795,7 @@ void wl1271_scan_sched_scan_results(struct wl1271 *wl)
ieee80211_sched_scan_results(wl->hw);
}
void wl1271_scan_sched_scan_stop(struct wl1271 *wl)
void wl1271_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct wl1271_cmd_sched_scan_stop *stop;
int ret = 0;
@ -776,6 +809,7 @@ void wl1271_scan_sched_scan_stop(struct wl1271 *wl)
return;
}
stop->role_id = wlvif->dev_role_id;
stop->tag = WL1271_SCAN_DEFAULT_TAG;
ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop,

View File

@ -40,7 +40,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
struct cfg80211_sched_scan_request *req,
struct ieee80211_sched_scan_ies *ies);
int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif);
void wl1271_scan_sched_scan_stop(struct wl1271 *wl);
void wl1271_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
void wl1271_scan_sched_scan_results(struct wl1271 *wl);
#define WL1271_SCAN_MAX_CHANNELS 24
@ -142,7 +142,8 @@ enum {
SCAN_BSS_TYPE_ANY,
};
#define SCAN_CHANNEL_FLAGS_DFS BIT(0)
#define SCAN_CHANNEL_FLAGS_DFS BIT(0) /* channel is passive until an
activity is detected on it */
#define SCAN_CHANNEL_FLAGS_DFS_ENABLED BIT(1)
struct conn_scan_ch_params {
@ -185,7 +186,10 @@ struct wl1271_cmd_sched_scan_config {
u8 dfs;
u8 padding[3];
u8 n_pactive_ch; /* number of pactive (passive until fw detects energy)
channels in BG band */
u8 role_id;
u8 padding[1];
struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ];
struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ];
@ -212,21 +216,24 @@ struct wl1271_cmd_sched_scan_ssid_list {
u8 n_ssids;
struct wl1271_ssid ssids[SCHED_SCAN_MAX_SSIDS];
u8 padding[3];
u8 role_id;
u8 padding[2];
} __packed;
struct wl1271_cmd_sched_scan_start {
struct wl1271_cmd_header header;
u8 tag;
u8 padding[3];
u8 role_id;
u8 padding[2];
} __packed;
struct wl1271_cmd_sched_scan_stop {
struct wl1271_cmd_header header;
u8 tag;
u8 padding[3];
u8 role_id;
u8 padding[2];
} __packed;

View File

@ -25,6 +25,7 @@
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/platform_device.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/card.h>
@ -32,6 +33,7 @@
#include <linux/gpio.h>
#include <linux/wl12xx.h>
#include <linux/pm_runtime.h>
#include <linux/printk.h>
#include "wlcore.h"
#include "wl12xx_80211.h"
@ -45,6 +47,8 @@
#define SDIO_DEVICE_ID_TI_WL1271 0x4076
#endif
static bool dump = false;
struct wl12xx_sdio_glue {
struct device *dev;
struct platform_device *core;
@ -76,6 +80,13 @@ static void wl12xx_sdio_raw_read(struct device *child, int addr, void *buf,
sdio_claim_host(func);
if (unlikely(dump)) {
printk(KERN_DEBUG "wlcore_sdio: READ from 0x%04x\n", addr);
print_hex_dump(KERN_DEBUG, "wlcore_sdio: READ ",
DUMP_PREFIX_OFFSET, 16, 1,
buf, len, false);
}
if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) {
((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
dev_dbg(child->parent, "sdio read 52 addr 0x%x, byte 0x%02x\n",
@ -105,6 +116,13 @@ static void wl12xx_sdio_raw_write(struct device *child, int addr, void *buf,
sdio_claim_host(func);
if (unlikely(dump)) {
printk(KERN_DEBUG "wlcore_sdio: WRITE to 0x%04x\n", addr);
print_hex_dump(KERN_DEBUG, "wlcore_sdio: WRITE ",
DUMP_PREFIX_OFFSET, 16, 1,
buf, len, false);
}
if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) {
sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret);
dev_dbg(child->parent, "sdio write 52 addr 0x%x, byte 0x%02x\n",
@ -196,6 +214,7 @@ static int __devinit wl1271_probe(struct sdio_func *func,
struct resource res[1];
mmc_pm_flag_t mmcflags;
int ret = -ENOMEM;
const char *chip_family;
/* We are only able to handle the wlan function */
if (func->num != 0x02)
@ -236,7 +255,18 @@ static int __devinit wl1271_probe(struct sdio_func *func,
/* Tell PM core that we don't need the card to be powered now */
pm_runtime_put_noidle(&func->dev);
glue->core = platform_device_alloc("wl12xx", -1);
/*
* Due to a hardware bug, we can't differentiate wl18xx from
* wl12xx, because both report the same device ID. The only
* way to differentiate is by checking the SDIO revision,
* which is 3.00 on the wl18xx chips.
*/
if (func->card->cccr.sdio_vsn == SDIO_SDIO_REV_3_00)
chip_family = "wl18xx";
else
chip_family = "wl12xx";
glue->core = platform_device_alloc(chip_family, -1);
if (!glue->core) {
dev_err(glue->dev, "can't allocate platform_device");
ret = -ENOMEM;
@ -367,6 +397,9 @@ static void __exit wl1271_exit(void)
module_init(wl1271_init);
module_exit(wl1271_exit);
module_param(dump, bool, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(dump, "Enable sdio read/write dumps.");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");

View File

@ -72,7 +72,7 @@ static int wl1271_alloc_tx_id(struct wl1271 *wl, struct sk_buff *skb)
return id;
}
static void wl1271_free_tx_id(struct wl1271 *wl, int id)
void wl1271_free_tx_id(struct wl1271 *wl, int id)
{
if (__test_and_clear_bit(id, wl->tx_frames_map)) {
if (unlikely(wl->tx_frames_cnt == wl->num_tx_desc))
@ -82,6 +82,7 @@ static void wl1271_free_tx_id(struct wl1271 *wl, int id)
wl->tx_frames_cnt--;
}
}
EXPORT_SYMBOL(wl1271_free_tx_id);
static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl,
struct sk_buff *skb)
@ -127,6 +128,7 @@ bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb)
{
return wl->dummy_packet == skb;
}
EXPORT_SYMBOL(wl12xx_is_dummy_packet);
u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct sk_buff *skb)
@ -146,10 +148,10 @@ u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif,
return wl->system_hlid;
hdr = (struct ieee80211_hdr *)skb->data;
if (ieee80211_is_mgmt(hdr->frame_control))
return wlvif->ap.global_hlid;
else
if (is_multicast_ether_addr(ieee80211_get_DA(hdr)))
return wlvif->ap.bcast_hlid;
else
return wlvif->ap.global_hlid;
}
}
@ -176,37 +178,34 @@ u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl,
unsigned int packet_length)
{
if (wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN)
return ALIGN(packet_length, WL12XX_BUS_BLOCK_SIZE);
else
if ((wl->quirks & WLCORE_QUIRK_TX_PAD_LAST_FRAME) ||
!(wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN))
return ALIGN(packet_length, WL1271_TX_ALIGN_TO);
else
return ALIGN(packet_length, WL12XX_BUS_BLOCK_SIZE);
}
EXPORT_SYMBOL(wlcore_calc_packet_alignment);
static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct sk_buff *skb, u32 extra, u32 buf_offset,
u8 hlid)
u8 hlid, bool is_gem)
{
struct wl1271_tx_hw_descr *desc;
u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra;
u32 total_blocks;
int id, ret = -EBUSY, ac;
u32 spare_blocks = wl->normal_tx_spare;
bool is_dummy = false;
u32 spare_blocks;
if (buf_offset + total_len > WL1271_AGGR_BUFFER_SIZE)
return -EAGAIN;
spare_blocks = wlcore_hw_get_spare_blocks(wl, is_gem);
/* allocate free identifier for the packet */
id = wl1271_alloc_tx_id(wl, skb);
if (id < 0)
return id;
if (unlikely(wl12xx_is_dummy_packet(wl, skb)))
is_dummy = true;
else if (wlvif->is_gem)
spare_blocks = wl->gem_tx_spare;
total_blocks = wlcore_hw_calc_tx_blocks(wl, total_len, spare_blocks);
if (total_blocks <= wl->tx_blocks_available) {
@ -228,7 +227,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
wl->tx_allocated_pkts[ac]++;
if (!is_dummy && wlvif &&
if (!wl12xx_is_dummy_packet(wl, skb) && wlvif &&
wlvif->bss_type == BSS_TYPE_AP_BSS &&
test_bit(hlid, wlvif->ap.sta_hlid_map))
wl->links[hlid].allocated_pkts++;
@ -268,6 +267,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
if (extra) {
int hdrlen = ieee80211_hdrlen(frame_control);
memmove(frame_start, hdr, hdrlen);
skb_set_network_header(skb, skb_network_offset(skb) + extra);
}
/* configure packet life time */
@ -330,9 +330,9 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
ieee80211_has_protected(frame_control))
tx_attr |= TX_HW_ATTR_HOST_ENCRYPT;
desc->reserved = 0;
desc->tx_attr = cpu_to_le16(tx_attr);
wlcore_hw_set_tx_desc_csum(wl, desc, skb);
wlcore_hw_set_tx_desc_data_len(wl, desc, skb);
}
@ -346,6 +346,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u32 total_len;
u8 hlid;
bool is_dummy;
bool is_gem = false;
if (!skb)
return -EINVAL;
@ -355,7 +356,8 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
/* TODO: handle dummy packets on multi-vifs */
is_dummy = wl12xx_is_dummy_packet(wl, skb);
if (info->control.hw_key &&
if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
info->control.hw_key &&
info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
extra = WL1271_EXTRA_SPACE_TKIP;
@ -373,6 +375,8 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
return ret;
wlvif->default_key = idx;
}
is_gem = (cipher == WL1271_CIPHER_SUITE_GEM);
}
hlid = wl12xx_tx_get_hlid(wl, wlvif, skb);
if (hlid == WL12XX_INVALID_LINK_ID) {
@ -380,7 +384,8 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
return -EINVAL;
}
ret = wl1271_tx_allocate(wl, wlvif, skb, extra, buf_offset, hlid);
ret = wl1271_tx_allocate(wl, wlvif, skb, extra, buf_offset, hlid,
is_gem);
if (ret < 0)
return ret;
@ -425,10 +430,10 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set,
rate_set >>= 1;
}
/* MCS rates indication are on bits 16 - 23 */
/* MCS rates indication are on bits 16 - 31 */
rate_set >>= HW_HT_RATES_OFFSET - band->n_bitrates;
for (bit = 0; bit < 8; bit++) {
for (bit = 0; bit < 16; bit++) {
if (rate_set & 0x1)
enabled_rates |= (CONF_HW_BIT_RATE_MCS_0 << bit);
rate_set >>= 1;
@ -439,18 +444,15 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set,
void wl1271_handle_tx_low_watermark(struct wl1271 *wl)
{
unsigned long flags;
int i;
for (i = 0; i < NUM_TX_QUEUES; i++) {
if (test_bit(i, &wl->stopped_queues_map) &&
if (wlcore_is_queue_stopped_by_reason(wl, i,
WLCORE_QUEUE_STOP_REASON_WATERMARK) &&
wl->tx_queue_count[i] <= WL1271_TX_QUEUE_LOW_WATERMARK) {
/* firmware buffer has space, restart queues */
spin_lock_irqsave(&wl->wl_lock, flags);
ieee80211_wake_queue(wl->hw,
wl1271_tx_get_mac80211_queue(i));
clear_bit(i, &wl->stopped_queues_map);
spin_unlock_irqrestore(&wl->wl_lock, flags);
wlcore_wake_queue(wl, i,
WLCORE_QUEUE_STOP_REASON_WATERMARK);
}
}
}
@ -661,7 +663,7 @@ void wl1271_tx_work_locked(struct wl1271 *wl)
struct wl12xx_vif *wlvif;
struct sk_buff *skb;
struct wl1271_tx_hw_descr *desc;
u32 buf_offset = 0;
u32 buf_offset = 0, last_len = 0;
bool sent_packets = false;
unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
int ret;
@ -685,6 +687,9 @@ void wl1271_tx_work_locked(struct wl1271 *wl)
* Flush buffer and try again.
*/
wl1271_skb_queue_head(wl, wlvif, skb);
buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset,
last_len);
wlcore_write_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf,
buf_offset, true);
sent_packets = true;
@ -710,7 +715,8 @@ void wl1271_tx_work_locked(struct wl1271 *wl)
ieee80211_free_txskb(wl->hw, skb);
goto out_ack;
}
buf_offset += ret;
last_len = ret;
buf_offset += last_len;
wl->tx_packets_count++;
if (has_data) {
desc = (struct wl1271_tx_hw_descr *) skb->data;
@ -720,6 +726,7 @@ void wl1271_tx_work_locked(struct wl1271 *wl)
out_ack:
if (buf_offset) {
buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset, last_len);
wlcore_write_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf,
buf_offset, true);
sent_packets = true;
@ -849,7 +856,8 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));
/* remove TKIP header space if present */
if (info->control.hw_key &&
if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
info->control.hw_key &&
info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data,
@ -958,7 +966,7 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
}
/* caller must hold wl->mutex and TX must be stopped */
void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
void wl12xx_tx_reset(struct wl1271 *wl)
{
int i;
struct sk_buff *skb;
@ -973,15 +981,12 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
wl->tx_queue_count[i] = 0;
}
wl->stopped_queues_map = 0;
/*
* Make sure the driver is at a consistent state, in case this
* function is called from a context other than interface removal.
* This call will always wake the TX queues.
*/
if (reset_tx_queues)
wl1271_handle_tx_low_watermark(wl);
wl1271_handle_tx_low_watermark(wl);
for (i = 0; i < wl->num_tx_desc; i++) {
if (wl->tx_frames[i] == NULL)
@ -998,7 +1003,8 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
*/
info = IEEE80211_SKB_CB(skb);
skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));
if (info->control.hw_key &&
if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
info->control.hw_key &&
info->control.hw_key->cipher ==
WLAN_CIPHER_SUITE_TKIP) {
int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
@ -1024,6 +1030,11 @@ void wl1271_tx_flush(struct wl1271 *wl)
int i;
timeout = jiffies + usecs_to_jiffies(WL1271_TX_FLUSH_TIMEOUT);
/* only one flush should be in progress, for consistent queue state */
mutex_lock(&wl->flush_mutex);
wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FLUSH);
while (!time_after(jiffies, timeout)) {
mutex_lock(&wl->mutex);
wl1271_debug(DEBUG_TX, "flushing tx buffer: %d %d",
@ -1032,7 +1043,7 @@ void wl1271_tx_flush(struct wl1271 *wl)
if ((wl->tx_frames_cnt == 0) &&
(wl1271_tx_total_queue_count(wl) == 0)) {
mutex_unlock(&wl->mutex);
return;
goto out;
}
mutex_unlock(&wl->mutex);
msleep(1);
@ -1045,7 +1056,12 @@ void wl1271_tx_flush(struct wl1271 *wl)
for (i = 0; i < WL12XX_MAX_LINKS; i++)
wl1271_tx_reset_link_queues(wl, i);
mutex_unlock(&wl->mutex);
out:
wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FLUSH);
mutex_unlock(&wl->flush_mutex);
}
EXPORT_SYMBOL_GPL(wl1271_tx_flush);
u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set)
{
@ -1054,3 +1070,96 @@ u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set)
return BIT(__ffs(rate_set));
}
void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue,
enum wlcore_queue_stop_reason reason)
{
bool stopped = !!wl->queue_stop_reasons[queue];
/* queue should not be stopped for this reason */
WARN_ON(test_and_set_bit(reason, &wl->queue_stop_reasons[queue]));
if (stopped)
return;
ieee80211_stop_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue));
}
void wlcore_stop_queue(struct wl1271 *wl, u8 queue,
enum wlcore_queue_stop_reason reason)
{
unsigned long flags;
spin_lock_irqsave(&wl->wl_lock, flags);
wlcore_stop_queue_locked(wl, queue, reason);
spin_unlock_irqrestore(&wl->wl_lock, flags);
}
void wlcore_wake_queue(struct wl1271 *wl, u8 queue,
enum wlcore_queue_stop_reason reason)
{
unsigned long flags;
spin_lock_irqsave(&wl->wl_lock, flags);
/* queue should not be clear for this reason */
WARN_ON(!test_and_clear_bit(reason, &wl->queue_stop_reasons[queue]));
if (wl->queue_stop_reasons[queue])
goto out;
ieee80211_wake_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue));
out:
spin_unlock_irqrestore(&wl->wl_lock, flags);
}
void wlcore_stop_queues(struct wl1271 *wl,
enum wlcore_queue_stop_reason reason)
{
int i;
for (i = 0; i < NUM_TX_QUEUES; i++)
wlcore_stop_queue(wl, i, reason);
}
EXPORT_SYMBOL_GPL(wlcore_stop_queues);
void wlcore_wake_queues(struct wl1271 *wl,
enum wlcore_queue_stop_reason reason)
{
int i;
for (i = 0; i < NUM_TX_QUEUES; i++)
wlcore_wake_queue(wl, i, reason);
}
EXPORT_SYMBOL_GPL(wlcore_wake_queues);
void wlcore_reset_stopped_queues(struct wl1271 *wl)
{
int i;
unsigned long flags;
spin_lock_irqsave(&wl->wl_lock, flags);
for (i = 0; i < NUM_TX_QUEUES; i++) {
if (!wl->queue_stop_reasons[i])
continue;
wl->queue_stop_reasons[i] = 0;
ieee80211_wake_queue(wl->hw,
wl1271_tx_get_mac80211_queue(i));
}
spin_unlock_irqrestore(&wl->wl_lock, flags);
}
bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue,
enum wlcore_queue_stop_reason reason)
{
return test_bit(reason, &wl->queue_stop_reasons[queue]);
}
bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue)
{
return !!wl->queue_stop_reasons[queue];
}

View File

@ -85,6 +85,19 @@ struct wl128x_tx_mem {
u8 extra_bytes;
} __packed;
struct wl18xx_tx_mem {
/*
* Total number of memory blocks allocated by the host for
* this packet.
*/
u8 total_mem_blocks;
/*
* control bits
*/
u8 ctrl;
} __packed;
/*
* On wl128x based devices, when TX packets are aggregated, each packet
* size must be aligned to the SDIO block size. The maximum block size
@ -100,6 +113,7 @@ struct wl1271_tx_hw_descr {
union {
struct wl127x_tx_mem wl127x_mem;
struct wl128x_tx_mem wl128x_mem;
struct wl18xx_tx_mem wl18xx_mem;
} __packed;
/* Device time (in us) when the packet arrived to the driver */
__le32 start_time;
@ -116,7 +130,16 @@ struct wl1271_tx_hw_descr {
u8 tid;
/* host link ID (HLID) */
u8 hlid;
u8 reserved;
union {
u8 wl12xx_reserved;
/*
* bit 0 -> 0 = udp, 1 = tcp
* bit 1:7 -> IP header offset
*/
u8 wl18xx_checksum_data;
} __packed;
} __packed;
enum wl1271_tx_hw_res_status {
@ -161,6 +184,13 @@ struct wl1271_tx_hw_res_if {
struct wl1271_tx_hw_res_descr tx_results_queue[TX_HW_RESULT_QUEUE_LEN];
} __packed;
enum wlcore_queue_stop_reason {
WLCORE_QUEUE_STOP_REASON_WATERMARK,
WLCORE_QUEUE_STOP_REASON_FW_RESTART,
WLCORE_QUEUE_STOP_REASON_FLUSH,
WLCORE_QUEUE_STOP_REASON_SPARE_BLK, /* 18xx specific */
};
static inline int wl1271_tx_get_queue(int queue)
{
switch (queue) {
@ -207,7 +237,7 @@ void wl1271_tx_work(struct work_struct *work);
void wl1271_tx_work_locked(struct wl1271 *wl);
void wl1271_tx_complete(struct wl1271 *wl);
void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif);
void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues);
void wl12xx_tx_reset(struct wl1271 *wl);
void wl1271_tx_flush(struct wl1271 *wl);
u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum ieee80211_band band);
u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set,
@ -223,6 +253,21 @@ bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb);
void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids);
unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl,
unsigned int packet_length);
void wl1271_free_tx_id(struct wl1271 *wl, int id);
void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue,
enum wlcore_queue_stop_reason reason);
void wlcore_stop_queue(struct wl1271 *wl, u8 queue,
enum wlcore_queue_stop_reason reason);
void wlcore_wake_queue(struct wl1271 *wl, u8 queue,
enum wlcore_queue_stop_reason reason);
void wlcore_stop_queues(struct wl1271 *wl,
enum wlcore_queue_stop_reason reason);
void wlcore_wake_queues(struct wl1271 *wl,
enum wlcore_queue_stop_reason reason);
void wlcore_reset_stopped_queues(struct wl1271 *wl);
bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue,
enum wlcore_queue_stop_reason reason);
bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue);
/* from main.c */
void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid);

View File

@ -24,8 +24,9 @@
#include <linux/platform_device.h>
#include "wl12xx.h"
#include "wlcore_i.h"
#include "event.h"
#include "boot.h"
/* The maximum number of Tx descriptors in all chip families */
#define WLCORE_MAX_TX_DESCRIPTORS 32
@ -33,11 +34,13 @@
/* forward declaration */
struct wl1271_tx_hw_descr;
enum wl_rx_buf_align;
struct wl1271_rx_descriptor;
struct wlcore_ops {
int (*identify_chip)(struct wl1271 *wl);
int (*identify_fw)(struct wl1271 *wl);
int (*boot)(struct wl1271 *wl);
int (*plt_init)(struct wl1271 *wl);
void (*trigger_cmd)(struct wl1271 *wl, int cmd_box_addr,
void *buf, size_t len);
void (*ack_event)(struct wl1271 *wl);
@ -61,6 +64,23 @@ struct wlcore_ops {
struct wl12xx_vif *wlvif);
s8 (*get_pg_ver)(struct wl1271 *wl);
void (*get_mac)(struct wl1271 *wl);
void (*set_tx_desc_csum)(struct wl1271 *wl,
struct wl1271_tx_hw_descr *desc,
struct sk_buff *skb);
void (*set_rx_csum)(struct wl1271 *wl,
struct wl1271_rx_descriptor *desc,
struct sk_buff *skb);
u32 (*ap_get_mimo_wide_rate_mask)(struct wl1271 *wl,
struct wl12xx_vif *wlvif);
int (*debugfs_init)(struct wl1271 *wl, struct dentry *rootdir);
int (*handle_static_data)(struct wl1271 *wl,
struct wl1271_static_data *static_data);
int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem);
int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key_conf);
u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len);
};
enum wlcore_partitions {
@ -109,6 +129,15 @@ enum wlcore_registers {
REG_TABLE_LEN,
};
struct wl1271_stats {
void *fw_stats;
unsigned long fw_stats_update;
size_t fw_stats_len;
unsigned int retry_count;
unsigned int excessive_retries;
};
struct wl1271 {
struct ieee80211_hw *hw;
bool mac80211_registered;
@ -121,7 +150,6 @@ struct wl1271 {
void (*set_power)(bool enable);
int irq;
int ref_clock;
spinlock_t wl_lock;
@ -186,7 +214,7 @@ struct wl1271 {
/* Frames scheduled for transmission, not handled yet */
int tx_queue_count[NUM_TX_QUEUES];
long stopped_queues_map;
unsigned long queue_stop_reasons[NUM_TX_QUEUES];
/* Frames received, not handled yet by mac80211 */
struct sk_buff_head deferred_rx_queue;
@ -205,9 +233,6 @@ struct wl1271 {
/* FW Rx counter */
u32 rx_counter;
/* Rx memory pool address */
struct wl1271_rx_mem_pool_addr rx_mem_pool_addr;
/* Intermediate buffer, used for packet aggregation */
u8 *aggr_buf;
@ -228,6 +253,7 @@ struct wl1271 {
/* Hardware recovery work */
struct work_struct recovery_work;
bool watchdog_recovery;
/* Pointer that holds DMA-friendly block for the mailbox */
struct event_mailbox *mbox;
@ -263,7 +289,8 @@ struct wl1271 {
u32 buffer_cmd;
u32 buffer_busyword[WL1271_BUSY_WORD_CNT];
struct wl_fw_status *fw_status;
struct wl_fw_status_1 *fw_status_1;
struct wl_fw_status_2 *fw_status_2;
struct wl1271_tx_hw_res_if *tx_res_if;
/* Current chipset configuration */
@ -279,8 +306,6 @@ struct wl1271 {
/* bands supported by this instance of wl12xx */
struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS];
int tcxo_clock;
/*
* wowlan trigger was configured during suspend.
* (currently, only "ANY" trigger is supported)
@ -333,10 +358,8 @@ struct wl1271 {
/* number of TX descriptors the HW supports. */
u32 num_tx_desc;
/* spare Tx blocks for normal/GEM operating modes */
u32 normal_tx_spare;
u32 gem_tx_spare;
/* number of RX descriptors the HW supports. */
u32 num_rx_desc;
/* translate HW Tx rates to standard rate-indices */
const u8 **band_rate_to_idx;
@ -348,19 +371,32 @@ struct wl1271 {
u8 hw_min_ht_rate;
/* HW HT (11n) capabilities */
struct ieee80211_sta_ht_cap ht_cap;
struct ieee80211_sta_ht_cap ht_cap[IEEE80211_NUM_BANDS];
/* size of the private FW status data */
size_t fw_status_priv_len;
/* RX Data filter rule state - enabled/disabled */
bool rx_filter_enabled[WL1271_MAX_RX_FILTERS];
/* size of the private static data */
size_t static_data_priv_len;
/* the current channel type */
enum nl80211_channel_type channel_type;
/* mutex for protecting the tx_flush function */
struct mutex flush_mutex;
};
int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev);
int __devexit wlcore_remove(struct platform_device *pdev);
struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size);
int wlcore_free_hw(struct wl1271 *wl);
int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key_conf);
/* Firmware image load chunk size */
#define CHUNK_SIZE 16384
@ -385,6 +421,12 @@ int wlcore_free_hw(struct wl1271 *wl);
/* Some firmwares may not support ELP */
#define WLCORE_QUIRK_NO_ELP BIT(6)
/* pad only the last frame in the aggregate buffer */
#define WLCORE_QUIRK_TX_PAD_LAST_FRAME BIT(7)
/* extra header space is required for TKIP */
#define WLCORE_QUIRK_TKIP_HEADER_SPACE BIT(8)
/* TODO: move to the lower drivers when all usages are abstracted */
#define CHIP_ID_1271_PG10 (0x4030101)
#define CHIP_ID_1271_PG20 (0x4030111)

View File

@ -22,8 +22,8 @@
*
*/
#ifndef __WL12XX_H__
#define __WL12XX_H__
#ifndef __WLCORE_I_H__
#define __WLCORE_I_H__
#include <linux/mutex.h>
#include <linux/completion.h>
@ -89,7 +89,7 @@
#define WL1271_AP_BSS_INDEX 0
#define WL1271_AP_DEF_BEACON_EXP 20
#define WL1271_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
#define WL1271_AGGR_BUFFER_SIZE (5 * PAGE_SIZE)
enum wl1271_state {
WL1271_STATE_OFF,
@ -132,16 +132,7 @@ struct wl1271_chip {
unsigned int fw_ver[NUM_FW_VER];
};
struct wl1271_stats {
struct acx_statistics *fw_stats;
unsigned long fw_stats_update;
unsigned int retry_count;
unsigned int excessive_retries;
};
#define NUM_TX_QUEUES 4
#define NUM_RX_PKT_DESC 8
#define AP_MAX_STATIONS 8
@ -159,13 +150,26 @@ struct wl_fw_packet_counters {
} __packed;
/* FW status registers */
struct wl_fw_status {
struct wl_fw_status_1 {
__le32 intr;
u8 fw_rx_counter;
u8 drv_rx_counter;
u8 reserved;
u8 tx_results_counter;
__le32 rx_pkt_descs[NUM_RX_PKT_DESC];
__le32 rx_pkt_descs[0];
} __packed;
/*
* Each HW arch has a different number of Rx descriptors.
* The length of the status depends on it, since it holds an array
* of descriptors.
*/
#define WLCORE_FW_STATUS_1_LEN(num_rx_desc) \
(sizeof(struct wl_fw_status_1) + \
(sizeof(((struct wl_fw_status_1 *)0)->rx_pkt_descs[0])) * \
num_rx_desc)
struct wl_fw_status_2 {
__le32 fw_localtime;
/*
@ -194,11 +198,6 @@ struct wl_fw_status {
u8 priv[0];
} __packed;
struct wl1271_rx_mem_pool_addr {
u32 addr;
u32 addr_extra;
};
#define WL1271_MAX_CHANNELS 64
struct wl1271_scan {
struct cfg80211_scan_request *req;
@ -367,6 +366,7 @@ struct wl12xx_vif {
/* The current band */
enum ieee80211_band band;
int channel;
enum nl80211_channel_type channel_type;
u32 bitrate_masks[IEEE80211_NUM_BANDS];
u32 basic_rate_set;
@ -417,9 +417,6 @@ struct wl12xx_vif {
struct work_struct rx_streaming_disable_work;
struct timer_list rx_streaming_timer;
/* does the current role use GEM for encryption (AP or STA) */
bool is_gem;
/*
* This struct must be last!
* data that has to be saved acrossed reconfigs (e.g. recovery)
@ -501,7 +498,8 @@ void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter,
/* Macros to handle wl1271.sta_rate_set */
#define HW_BG_RATES_MASK 0xffff
#define HW_HT_RATES_OFFSET 16
#define HW_MIMO_RATES_OFFSET 24
#define WL12XX_HW_BLOCK_SIZE 256
#endif
#endif /* __WLCORE_I_H__ */