From 32ec78901ddef7cd53342b2dbd96bc0b25ba7bd0 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 21 Oct 2011 10:35:01 +0200 Subject: WLAN: Initial commit of the STM CW1200 WLAN driver. LL IO API is defined and implemented. TX queue is designed and implemented. WSM API subset is defined. mac80211 API is implemented partly. FW downloading is working. BH is _not_ implemented. RX BH is implemented. 1. RX WSM API is defined and stubs are created. 2. RX BH is implemented (not verifiyed yet). Reference WSM API implementation. wsm_configuration / wsm_configuration_confirm. Not veryfied yet. BH and WSM TX, basic WSM notification. Device startup is finished. TODO at mac80211 layer: - start - add_interface - config - config_tx - config_filter - hw_scan - remove_interface - stop TODO in general: - proper (random?) MAC address TODO in WSM: - datapath Basic mac80211 STA interface is implemented (not verified yet, but should be working). Random MAC address is implemented. Defined and implemented: - cw1200_start - cw1200_stop - cw1200_add_interface - cw1200_remove_interface - cw1200_config - cw1200_conf_tx FW code is moved to fwio.[hc] STA code is moved to sta.[hc] AP code is moved to ap.[hc] TODO: - configure_filter() - hw_scan() - datapath Some bugfix. Piggyback RX-ing. Bugbix: wrong buf_id_rx calculation. Finaly! RX datapath is implemented. TX datapath is implemented. HW scan is implemented. TODO: - hw_scan: little refactoring. I hate gotos. - Join - Unjoin - SKB pool - BH monitor / chip restarter Development. + Proper handling of scan IEs Join. TX/RX should be working now. Development. + Proper handling of scan IEs + Ping is working!!! (with some hacks) Development. + TX rate policy cache is implemented. + TX rate policy table uploading is implemented. + TX queues stop/resume. + WSM STA API (except MIBs) is 100% finished. + Direct TX-ing of probe requests is converted to start-scan WSM cmd (workaround against FW "feature"). + All work moved to an own workqueue. + Scan timeout is implemented (workaround against a known bug in FW). + set_key is implemented. TODO: - Full WSM API support. - Join refactoring - Assoc status updates. Verification and bugfixing. + Direct probe is verified. Some bugs fixed. + TX timeout is implemented... and rolled back: no way to cancel TX. + TX hang is fixed: FW hangs if tx->more is set but no more data available. + TX rate table uploading is moved into a separate workitem: can't schedule in datapath. + TX status reporting is enabled. + Removed hacks from Join: should be able to connect to any AP now. + TX rate cache bugfix: incorrect handling of policies like 100000000008900000000000. + TX rate cache optimization: upload only changed policies. + Unjoin is implemented for TX and RX. + Join is triggered for auth. frames only. + Join timeout is implemented. + wsm_set_bss_params is called from assoc notification. + wsm_set_association_mode is called from assoc notification. + set_beacon_wakeup_period is called from assoc notification. + HT information is now available in priv->ht_cap, priv->association_mode + CTS protection is set. + RTS threshold is set and verified. + Warning cleanup. + TODOs for compat-wireless-2010-07-16 API change. + Very basic RSSI subscription. However reporting is NIY. + Some bugfix for minestrel. F.e. policy like 10:9 11:9 0:1 0:1 -1:0 should be handled ok. + MaxTxCount (short/long) is implemented and verified. + Channel switch is protected now. + OverrideInternalTxRate is implemented.. and rolled back: internal rate controlselection is better. TODO: - Security is not validated yet - Set HT parameters of TX data according to priv->ht_cap, priv->association_mode. - Strange: set_beacon_wakeup_period is called with DTIM 1 always. Why??? - RSSI subscription events. - How to do unjoin? Style changes. Modifications to make the code compatible with checkpath. Not finished yet. 1. // -> /* */ 2. whitespaces 3. formatting 4. warning- & error fix No functional changes in the commit. Verification. + RSSI reporting is working. + WEP is working. Dynamic WEP key switching is implemented to select TX WEP key on-fly + WPA/CCMP and WPA2/CCMP are working. + WPA/TKIP and WPA2/TKIP are working. + MIC failure event is implemented. + Unjoin is implemented as a reset. Why not? + wsm_flush_tx() added to flush TX port after wsm_lock_tx_async(). + race condition in wsm_lock_tx()/wsm_flush_tx() is fixed. + RSSI subscription is under research. + Event reporting is implemented. + Beacon loss event is prepared (but not sent) + Link loss is implemented. TODO: On target.. + Bugfix: DMA from non-DMAble memory in FW downloading. + RSSI subscription is finished. + RSSI event reporting is implemented. + Beacon loss event is reimplemented with respect to scan. + Memory leak in WSM event processing is fixed. + Code is verified on target. Set CW1200_U8500_PLATFORM to build for target. + Scan turns on powersave to prevent AP from TXing. + Spinning of direct_probe work is fixed. + Workaround is implemented for "FIXME: we can't use 'more' at all: we don't know future." TODO: Verification on device. + Correct deinitialization of async work. + New WSM API. + WMM is verified and fixed (queue prioritization). + Bug in TX is fixed (freezing of TCP traffic). + STE CQM extensions are implemented. + HW revision detection is implemented. + A crash/hangs in scan is fixed. + 802.11 slot time is set + 11n: first steps: Mixed mode is working. + 11n: Block ACK config. + Keep-alive work is implemented (but it is not doing anything yet). TODO: - Copyright statements and COPYING - STE API for power-save - Need to deinit bt_thread, isn't it? Copyright statements and bt_thread deinitialization. + Copyright statements + bt_thread deinitialization (NOT TESTED ON DEVICE AT ALL!!!) Cumulative TODO: - COPYING - STE API for power-save - Set HT parameters of TX data. 11n verification. - SKB pool - reuse previously allocated SKBs - BH monitor / chip restarter - IBSS - AP mode - U-APSD configuration - Memory leak verification, coverity, checkpatch = It looks like that's it. Mainline to compat-wireless-20010-12-03 No functional changes in this commit. + Changes in compat-wireless API. + Changes in kerner API. + Changes in kernel header structures. TODO: - New code in cw1200_bss_info_changed() needs to be verified. - ht_operation_mode needs to be cached in priv. Some HT refactoring. + ht_operation_mode is cached in priv + channel_type is cached in priv + New header for HT-related code: ht.h + Greenfield mode is used for TX if possible. + HT flags are reported back to rate control correctly. TODO: - Verify A-MPDU factor settings in cw1200_band_2ghz. SKB cache is implemented. Firmware loading refactoring. + CUT 2.0 detection is implemented. + Standard firmware loading API is used. + Firmware loading path is changed to /lib/firmware/cw1200/ + Firmware is put to the project: ./firmware/ TODO: - Firmware copying at build time. Bringup after mainlining. + FW downloading is working now. TODO: - BH thread can't read data block from device. Init: removing GPIO toggling. Not needed anymore. WLAN: Fix for missing GPIO_HIGH, GPIO_LOW The patch defines CW1200_GPIO_{HIGH|LOW} as replacement of GPIO_{HIGH|LOW}, which might be absent on the target platform. Also it allows build without compat wireless tree. Added cw1200 to drivers/staging Kconfig and Makefile. Added two CONFIG_ flags: + CONFIG_CW1200 - Enables the generic cw1200 module + CONFIG_CW1200_U8500_PLATFORM - U8500 platform integration Removed binary firmware files, they do not belong in the kernel tree. Added TODO file for cw1200 staging driver. Added more help text to Kconfig Removed checkpatch warnings. There is still one left. Removed all checkpatch warnings for cw1200.h Removed checkpatch warnings from cw1200_stdio.c. There is still one left. Removed checkpatch warnings for fwio.h Removed checkpatch warning for hwio.c Removed checkpatch warnings for main.c There are still some warninges left. Removed checkpatch warnings from queue.c There are still some warnings left Removed checkpatch warnings for queue.h Removed checkpatch warnings for sbus.h Removed checkpatch warnings for scan.c Removed checkpatch warnings for scan.h Removed checkpatch warnings for sta.c Removed checkpatch warnings for txrc.c Removed checkpatch warnings for txrx.h Removed checkpatch warnings from wsm.c Lots of "line over 80 char..." warnings left. Minor checkpatch warning cleanup on wsm.c WLAN: CW1200: Development: SoftAP mode WLAN: CW1200: AP development. + Basic open AP is woking (prototyping done). TODO: - Check AP scan & assoc with sniffer - WSM API: TxPowerLevel in scan requests. - WSM API: Frame in TX and RX indication. - WSM API: Flags in set_bss_params. - WSM API: IGTKGroupKey in key info. - WSM API: MultiTX confirm - WSM API: Config block ACK - WSM API: BA timeout indication. CW1200: WLAN: Fix for a merge error. This commit fixes build error introduced by WLAN: CW1200: AP development. WLAN: CW1200: Fix for a warning in net/mac80211/rx.c WLAN: CW1200: Add / remove key refactoring for AP mode. Keys are cached in the driver and loaded after wsm_start() is called. Verified (AP mode): + Open security: OK + WEP40: OK + WEP104: FAIL (WSM_STATUS_DECRYPTFAILURE) + CCMP: FAIL (MIC failure detected by STA. STA->AP decrypted OK) + TKIP: FAIL (MIC failure detected by STA. STA->AP decrypted OK) WLAN: CW1200: Proper handling of stop request in the AP mode. WLAN: CW1200: Firmware-driven keep-alive is implemented. CW1200: SDIO card detection is implemented. Removed dependency to u8500_sdio_detect_card(). New moule parameter "device" is implemented to point to the SDIO interface device is connected to. WLAN: CW1200: Checkpatch cleanup. + wsm.c is cleared. + sta.c is cleared. + ap.c is cleared. + bh.c is cleared. + cw1200_sdio.c is cleared. + main.c is cleared. + queue.c is cleared. + scan.c is cleared. + txrx.c is cleared. + cw1200.h is cleared. + hwio.h is cleared. + ht.h is cleared. + wsm_get_tx is (finally!) redesigned. TODO: - wsm.h WLAN: CW1200: Checkpatch cleanup. + wsm.h is cleared. Code is ready for submission. WLAN: CW1200: Module parameter for MAC address. New module parameter for cw1200_core.ko: "macaddr". Usage: modprobe cw1200_core macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 Random MAC address by default. WLAN: CW1200: Fix for a crash in skb_release_data(). WLAN: CW1200: Fix for a too short WSM timeout in stressed condition. WLAN: CW1200: Bugfix after regression testing. Datapath is verified (several gigabytes of downloaded data). WLAN: STA mode development. + AP API sta_add/sta_semove is implemented. + wsm_suspend_resume_indication is implemented. + Unique link ID is allocated for STAs in AP mode as WSM requires. + wsm_map_link is implemented. + wsm_reset is modified to support multiple link IDs. + cw1200_suspend_resume handler is implemented in the ap.c. + no more hardcoded SSIDs + Debug logs functionality is exposed to the kernel config. + Suspend / resume is supported by queue. + TKIP multicast TX (AP mode) is fixed. TODO: - AP mode with powersave is not verified. WLAN: Support for non-power-of-two SDIO transfer. WLAN: AP mode verification and bugfixing. + Fix for broken broadcasts. + Redesign of PS implementation in AP mode. + set_tim() is implemented. + sta_notify() is implemented. WLAN: IV/ICV injection fixed. Fixed: skb was modified prior to sanity check. There is also a workaround against a bug in WSM_A21.05.0288 firmware in this commit. In AP mode FW calculates FCS incorrectly when DA is FF:FF:FF:FF:FF:FF (was seen with TKIP and CCMP security). Workaround checks DA and replaces FF:FF:FF:FF:FF:FF with multicast address 01:00:5E:00:00:16. Workaround is disabled by #if 0 (can be used only for verification, should not be enabled in production). WLAN: Disabling debug printouts (STA/AP). WLAN: Updating u8500_defconfig with CW1200 driver. WLAN: AP mode fine-tuning. + Explicitly set broadcast bit in TIM when broadcasts are available. + Split handling of management frames (link ID = 0) and buffered multicasts (link ID = CW1200_LINK_ID_AFTER_DTIM) Signed-off-by: Dmitry Tarnyagin --- drivers/staging/cw1200/.gitignore | 10 + drivers/staging/cw1200/Kconfig | 66 ++ drivers/staging/cw1200/Makefile | 45 + drivers/staging/cw1200/TODO | 10 + drivers/staging/cw1200/ap.c | 595 +++++++++++++ drivers/staging/cw1200/ap.h | 36 + drivers/staging/cw1200/bh.c | 419 ++++++++++ drivers/staging/cw1200/bh.h | 26 + drivers/staging/cw1200/cw1200.h | 228 +++++ drivers/staging/cw1200/cw1200_sdio.c | 322 +++++++ drivers/staging/cw1200/fwio.c | 560 +++++++++++++ drivers/staging/cw1200/fwio.h | 33 + drivers/staging/cw1200/ht.h | 43 + drivers/staging/cw1200/hwio.c | 268 ++++++ drivers/staging/cw1200/hwio.h | 236 ++++++ drivers/staging/cw1200/main.c | 458 ++++++++++ drivers/staging/cw1200/queue.c | 409 +++++++++ drivers/staging/cw1200/queue.h | 72 ++ drivers/staging/cw1200/sbus.h | 37 + drivers/staging/cw1200/scan.c | 374 +++++++++ drivers/staging/cw1200/scan.h | 56 ++ drivers/staging/cw1200/sta.c | 1026 +++++++++++++++++++++++ drivers/staging/cw1200/sta.h | 72 ++ drivers/staging/cw1200/txrx.c | 586 +++++++++++++ drivers/staging/cw1200/txrx.h | 84 ++ drivers/staging/cw1200/wsm.c | 1530 ++++++++++++++++++++++++++++++++++ drivers/staging/cw1200/wsm.h | 1512 +++++++++++++++++++++++++++++++++ 27 files changed, 9113 insertions(+) create mode 100644 drivers/staging/cw1200/.gitignore create mode 100644 drivers/staging/cw1200/Kconfig create mode 100644 drivers/staging/cw1200/Makefile create mode 100644 drivers/staging/cw1200/TODO create mode 100644 drivers/staging/cw1200/ap.c create mode 100644 drivers/staging/cw1200/ap.h create mode 100644 drivers/staging/cw1200/bh.c create mode 100644 drivers/staging/cw1200/bh.h create mode 100644 drivers/staging/cw1200/cw1200.h create mode 100644 drivers/staging/cw1200/cw1200_sdio.c create mode 100644 drivers/staging/cw1200/fwio.c create mode 100644 drivers/staging/cw1200/fwio.h create mode 100644 drivers/staging/cw1200/ht.h create mode 100644 drivers/staging/cw1200/hwio.c create mode 100644 drivers/staging/cw1200/hwio.h create mode 100644 drivers/staging/cw1200/main.c create mode 100644 drivers/staging/cw1200/queue.c create mode 100644 drivers/staging/cw1200/queue.h create mode 100644 drivers/staging/cw1200/sbus.h create mode 100644 drivers/staging/cw1200/scan.c create mode 100644 drivers/staging/cw1200/scan.h create mode 100644 drivers/staging/cw1200/sta.c create mode 100644 drivers/staging/cw1200/sta.h create mode 100644 drivers/staging/cw1200/txrx.c create mode 100644 drivers/staging/cw1200/txrx.h create mode 100644 drivers/staging/cw1200/wsm.c create mode 100644 drivers/staging/cw1200/wsm.h diff --git a/drivers/staging/cw1200/.gitignore b/drivers/staging/cw1200/.gitignore new file mode 100644 index 00000000000..6ad0d1ec58e --- /dev/null +++ b/drivers/staging/cw1200/.gitignore @@ -0,0 +1,10 @@ +*.o +*.ko +*.ko.cmd +.tmp_versions +modules.order +Module.symvers +Module.markers +*.o.cmd +*.mod.c +*.swp diff --git a/drivers/staging/cw1200/Kconfig b/drivers/staging/cw1200/Kconfig new file mode 100644 index 00000000000..19ac553b745 --- /dev/null +++ b/drivers/staging/cw1200/Kconfig @@ -0,0 +1,66 @@ +config CW1200 + tristate "CW1200 WLAN support" + select MAC80211 + select CFG80211 + help + + This is an experimental driver for the cw1200 chip-set. + Enabling this option enables the generic driver without + any platform support. + + Please select the appropriate platform below. + +if CW1200 + +config CW1200_U8500_PLATFORM + bool "U8500 platform support" + depends on CW1200 + select CW1200_NON_POWER_OF_TWO_BLOCKSIZES + help + Say Y if you want to include support for the u8500 platform. + This will add u8500 specific initializing code for cw1200. + +config CW1200_NON_POWER_OF_TWO_BLOCKSIZES + bool "Platform supports non-power-of-two SDIO transfer" + depends on CW1200 + help + Say N here only if you are running the driver on a platform + which does not have support for non-power-of-two SDIO transfer. + If unsure, say Y. + + +config CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE + bool "Software keep-alive (DEVELOPMENT)" + depends on CW1200 + help + Say Y if your firmware does not support keep-alive functionality + or you suspect problems with the implementation. + Please finalize software the software keep-alive functionality in + that case. + + If unsure, say N. + +menu "Driver debug features" + depends on CW1200 + +config CW1200_BH_DEBUG + bool "Enable low-level device communication logs (DEVELOPMENT)" + +config CW1200_WSM_DEBUG + bool "Enable WSM API debug messages (DEVELOPMENT)" + +config CW1200_WSM_DUMPS + bool "Verbose WSM API logging (DEVELOPMENT)" + +config CW1200_TXRX_DEBUG + bool "Enable TX/RX debug messages (DEVELOPMENT)" + +config CW1200_TX_POLICY_DEBUG + bool "Enable TX policy debug (DEVELOPMENT)" + +config CW1200_STA_DEBUG + bool "Enable STA/AP debug (DEVELOPMENT)" + +endmenu + +endif diff --git a/drivers/staging/cw1200/Makefile b/drivers/staging/cw1200/Makefile new file mode 100644 index 00000000000..f14637ed297 --- /dev/null +++ b/drivers/staging/cw1200/Makefile @@ -0,0 +1,45 @@ +ifeq ($(CONFIG_CW1200_U8500_PLATFORM),y) + EXTRA_CFLAGS += -DCW1200_U8500_PLATFORM=1 +endif + +ifeq ($(CONFIG_CW1200_NON_POWER_OF_TWO_BLOCKSIZES),y) + EXTRA_CFLAGS += -DCW1200_NON_POWER_OF_TWO_BLOCKSIZES=1 +endif + +ifeq ($(CONFIG_CW1200_USE_STE_EXTENSIONS),y) + EXTRA_CFLAGS += -DUSE_STE_EXTENSIONS=1 +endif + +ifeq ($(CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE),y) + EXTRA_CFLAGS += -DCW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE=1 +endif + +ifeq ($(CONFIG_CW1200_BH_DEBUG),y) + EXTRA_CFLAGS += -DCW1200_DEBUG_ENABLE_BH_LOGS=1 +endif + +ifeq ($(CONFIG_CW1200_WSM_DEBUG),y) + EXTRA_CFLAGS += -DCW1200_DEBUG_ENABLE_WSM_LOGS=1 +endif + +ifeq ($(CONFIG_CW1200_WSM_DUMPS),y) + EXTRA_CFLAGS += -DCW1200_DEBUG_ENABLE_WSM_DUMPS=1 +endif + +ifeq ($(CONFIG_CW1200_TXRX_DEBUG),y) + EXTRA_CFLAGS += -DCW1200_TXRX_DEBUG=1 +endif + +ifeq ($(CONFIG_CW1200_TX_POLICY_DEBUG),y) + EXTRA_CFLAGS += -DCW1200_TX_POLICY_DEBUG=1 +endif + +ifeq ($(CONFIG_CW1200_STA_DEBUG),y) + EXTRA_CFLAGS += -DCW1200_DEBUG_ENABLE_STA_LOGS +endif + +cw1200_core-objs := fwio.o txrx.o main.o queue.o hwio.o bh.o wsm.o sta.o ap.o scan.o +cw1200_wlan-objs := cw1200_sdio.o + +obj-$(CONFIG_CW1200) += cw1200_core.o +obj-$(CONFIG_CW1200) += cw1200_wlan.o diff --git a/drivers/staging/cw1200/TODO b/drivers/staging/cw1200/TODO new file mode 100644 index 00000000000..0d2be40e1f4 --- /dev/null +++ b/drivers/staging/cw1200/TODO @@ -0,0 +1,10 @@ +TODO: + - IBSS: Not implemented (3-10 m*d). + - 11n: Almost done. WSM API upgrade is required fo finish implementation. (2-3 m*d). + - 11n: verification (??? m*d Resources? WLAN RF lab? 11n sniffers + availability? Bring up of the test equipment?). + - memory leakage verification and proper cleanup: not done (1-3 m*d). + - AP (hot-spot) mode: Implemented, some problems with WEP104/WPA/WPA2 security. + FW bug? To be investigated. + - U-APSD configuration (0.5-1 m*d). + - Cleanup of debug printouts (1 m*d). diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c new file mode 100644 index 00000000000..93aee42ab6c --- /dev/null +++ b/drivers/staging/cw1200/ap.c @@ -0,0 +1,595 @@ +/* + * mac80211 STA and AP API for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#include "cw1200.h" +#include "sta.h" +#include "ap.h" +#include "bh.h" + +#ifdef CW1200_DEBUG_ENABLE_STA_LOGS +#define ap_printk(...) printk(__VA_ARGS__) +#else +#define ap_printk(...) +#endif + +static int cw1200_upload_beacon(struct cw1200_common *priv); +static int cw1200_start_ap(struct cw1200_common *priv); +static int cw1200_update_beaconing(struct cw1200_common *priv); + + +/* ******************************************************************** */ +/* AP API */ + +int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_sta_priv *sta_priv = + (struct cw1200_sta_priv *)&sta->drv_priv; + struct wsm_map_link map_link = { + .link_id = 0, + }; + + + /* Link ID mapping works fine in STA mode as well. + * It's better to keep same handling for both STA ans AP modes */ +#if 0 + if (priv->mode != NL80211_IFTYPE_AP) + return 0; +#endif + + map_link.link_id = ffs(~(priv->link_id_map | 1)) - 1; + if (map_link.link_id > CW1200_MAX_STA_IN_AP_MODE) { + sta_priv->link_id = 0; + printk(KERN_INFO "[AP] No more link ID available.\n"); + return -ENOENT; + } + + memcpy(map_link.mac_addr, sta->addr, ETH_ALEN); + if (!WARN_ON(wsm_map_link(priv, &map_link))) { + sta_priv->link_id = map_link.link_id; + priv->link_id_map |= 1 << map_link.link_id; + ap_printk(KERN_DEBUG "[AP] STA added, link_id: %d\n", + map_link.link_id); + } + return 0; +} + +int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_sta_priv *sta_priv = + (struct cw1200_sta_priv *)&sta->drv_priv; + struct wsm_reset reset = { + .link_id = 0, + .reset_statistics = false, + }; + + if (sta_priv->link_id) { + ap_printk(KERN_DEBUG "[AP] STA removed, link_id: %d\n", + sta_priv->link_id); + reset.link_id = sta_priv->link_id; + priv->link_id_map &= ~(1 << sta_priv->link_id); + sta_priv->link_id = 0; + WARN_ON(wsm_reset(priv, &reset)); + } + return 0; +} + +void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta) +{ + struct cw1200_common *priv = dev->priv; + struct cw1200_sta_priv *sta_priv = + (struct cw1200_sta_priv *)&sta->drv_priv; + u32 bit = 1 << sta_priv->link_id; + + switch (notify_cmd) { + case STA_NOTIFY_SLEEP: + priv->sta_asleep_mask |= bit; + break; + case STA_NOTIFY_AWAKE: + priv->sta_asleep_mask &= ~bit; + cw1200_bh_wakeup(priv); + break; + } +} + +static int cw1200_set_tim_impl(struct cw1200_common *priv, bool multicast) +{ + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_BEACON, + }; + u16 tim_offset, tim_length; + + ap_printk(KERN_DEBUG "[AP] %s.\n", __func__); + + frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, + &tim_offset, &tim_length); + if (WARN_ON(!frame.skb)) + return -ENOMEM; + + if (tim_offset && tim_length >= 6) { + /* Ignore DTIM count from mac80211: + * firmware handles DTIM internally. */ + frame.skb->data[tim_offset + 2] = 0; + + /* Set/reset aid0 bit */ + if (multicast) + frame.skb->data[tim_offset + 4] |= 1; + else + frame.skb->data[tim_offset + 4] &= ~1; + } + + WARN_ON(wsm_set_template_frame(priv, &frame)); + + dev_kfree_skb(frame.skb); + + return 0; +} + +void cw1200_set_tim_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, set_tim_work); + (void)cw1200_set_tim_impl(priv, !priv->suspend_multicast); +} + +int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, + bool set) +{ + struct cw1200_common *priv = dev->priv; + queue_work(priv->workqueue, &priv->set_tim_work); + return 0; +} + +void cw1200_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct cw1200_common *priv = dev->priv; + struct ieee80211_conf *conf = &dev->conf; + + mutex_lock(&priv->conf_mutex); + if (changed & BSS_CHANGED_BSSID) { + memcpy(priv->bssid, info->bssid, ETH_ALEN); + cw1200_setup_mac(priv); + } + + /* TODO: BSS_CHANGED_IBSS */ + /* TODO: BSS_CHANGED_ARP_FILTER */ + + if (changed & BSS_CHANGED_BEACON_ENABLED) + priv->enable_beacon = info->enable_beacon; + + if (changed & BSS_CHANGED_BEACON) + WARN_ON(cw1200_upload_beacon(priv)); + + if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_INT)) + WARN_ON(cw1200_update_beaconing(priv)); + + if (changed & BSS_CHANGED_ASSOC) { + wsm_lock_tx(priv); + priv->wep_default_key_id = -1; + wsm_unlock_tx(priv); + + if (!info->assoc /* && !info->ibss_joined */) { + priv->cqm_link_loss_count = 60; + priv->cqm_beacon_loss_count = 20; + priv->cqm_tx_failure_thold = 0; + } + priv->cqm_tx_failure_count = 0; + } + + if (changed & + (BSS_CHANGED_ASSOC | + BSS_CHANGED_BASIC_RATES | + BSS_CHANGED_ERP_PREAMBLE | + BSS_CHANGED_HT | + BSS_CHANGED_ERP_SLOT)) { + ap_printk(KERN_DEBUG "BSS_CHANGED_ASSOC.\n"); + if (info->assoc) { /* TODO: ibss_joined */ + int dtim_interval = conf->ps_dtim_period; + int listen_interval = conf->listen_interval; + struct ieee80211_sta *sta = NULL; + + /* Associated: kill join timeout */ + cancel_delayed_work_sync(&priv->join_timeout); + + /* TODO: This code is not verified {{{ */ + rcu_read_lock(); + if (info->bssid) + sta = ieee80211_find_sta(vif, info->bssid); + if (sta) { + BUG_ON(!priv->channel); + priv->ht_info.ht_cap = sta->ht_cap; + priv->bss_params.operationalRateSet = + __cpu_to_le32( + cw1200_rate_mask_to_wsm(priv, + sta->supp_rates[priv->channel->band])); + priv->ht_info.channel_type = + info->channel_type; + priv->ht_info.operation_mode = + info->ht_operation_mode; + } else { + memset(&priv->ht_info, 0, + sizeof(priv->ht_info)); + priv->bss_params.operationalRateSet = -1; + } + rcu_read_unlock(); + /* }}} */ + + priv->association_mode.greenfieldMode = + cw1200_ht_greenfield(&priv->ht_info); + priv->association_mode.flags = + WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES | + WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE | + WSM_ASSOCIATION_MODE_USE_HT_MODE | + WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET | + WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING; + priv->association_mode.preambleType = + info->use_short_preamble ? + WSM_JOIN_PREAMBLE_SHORT : + WSM_JOIN_PREAMBLE_LONG; + priv->association_mode.basicRateSet = __cpu_to_le32( + cw1200_rate_mask_to_wsm(priv, + info->basic_rates)); + priv->association_mode.mpduStartSpacing = + cw1200_ht_ampdu_density(&priv->ht_info); + +#ifdef USE_STE_EXTENSIONS + priv->cqm_beacon_loss_count = + info->cqm_beacon_miss_thold; + priv->cqm_tx_failure_thold = + info->cqm_tx_fail_thold; + priv->cqm_tx_failure_count = 0; +#endif /* USE_STE_EXTENSIONS */ + + priv->bss_params.beaconLostCount = + priv->cqm_beacon_loss_count ? + priv->cqm_beacon_loss_count : + priv->cqm_link_loss_count; + + priv->bss_params.aid = info->aid; + + if (dtim_interval < 1) + dtim_interval = 1; + if (dtim_interval < priv->join_dtim_period) + dtim_interval = priv->join_dtim_period; + if (listen_interval < dtim_interval) + listen_interval = 0; + + ap_printk(KERN_DEBUG "[STA] DTIM %d, listen %d\n", + dtim_interval, listen_interval); + ap_printk(KERN_DEBUG "[STA] Preamble: %d, " \ + "Greenfield: %d, Aid: %d, " \ + "Rates: 0x%.8X, Basic: 0x%.8X\n", + priv->association_mode.preambleType, + priv->association_mode.greenfieldMode, + priv->bss_params.aid, + priv->bss_params.operationalRateSet, + priv->association_mode.basicRateSet); + WARN_ON(wsm_set_association_mode(priv, + &priv->association_mode)); + WARN_ON(wsm_set_bss_params(priv, &priv->bss_params)); + WARN_ON(wsm_set_beacon_wakeup_period(priv, + dtim_interval, listen_interval)); +#if 0 + /* It's better to override internal TX rete; otherwise + * device sends RTS at too high rate. However device + * can't receive CTS at 1 and 2 Mbps. Well, 5.5 is a + * good choice for RTS/CTS, but that means PS poll + * will be sent at the same rate - impact on link + * budget. Not sure what is better.. */ + + /* Update: internal rate selection algorythm is not + * bad: if device is not receiving CTS at high rate, + * it drops RTS rate. + * So, conclusion: if-0 the code. Keep code just for + * information: + * Do not touch WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE! */ + + /* ~3 is a bug in device: RTS/CTS is not working at + * low rates */ + + __le32 internal_tx_rate = __cpu_to_le32(__ffs( + priv->association_mode.basicRateSet & ~3)); + WARN_ON(wsm_write_mib(priv, + WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, + &internal_tx_rate, + sizeof(internal_tx_rate))); +#endif + } else { + memset(&priv->association_mode, 0, + sizeof(priv->association_mode)); + memset(&priv->bss_params, 0, sizeof(priv->bss_params)); + } + } + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT)) { + __le32 use_cts_prot = info->use_cts_prot ? + __cpu_to_le32(1) : 0; + + ap_printk(KERN_DEBUG "[STA] CTS protection %d\n", + info->use_cts_prot); + WARN_ON(wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION, + &use_cts_prot, sizeof(use_cts_prot))); + } + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) { + __le32 slot_time = info->use_short_slot ? + __cpu_to_le32(9) : __cpu_to_le32(20); + ap_printk(KERN_DEBUG "[STA] Slot time :%d us.\n", + __le32_to_cpu(slot_time)); + WARN_ON(wsm_write_mib(priv, WSM_MIB_ID_DOT11_SLOT_TIME, + &slot_time, sizeof(slot_time))); + } + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) { + struct wsm_rcpi_rssi_threshold threshold = { + .rssiRcpiMode = WSM_RCPI_RSSI_USE_RSSI, + .rollingAverageCount = 1, + }; + +#if 0 + /* For verification purposes */ + info->cqm_rssi_thold = -50; + info->cqm_rssi_hyst = 4; +#endif /* 0 */ + + ap_printk(KERN_DEBUG "[CQM] RSSI threshold subscribe: %d +- %d\n", + info->cqm_rssi_thold, info->cqm_rssi_hyst); +#ifdef USE_STE_EXTENSIONS + ap_printk(KERN_DEBUG "[CQM] Beacon loss subscribe: %d\n", + info->cqm_beacon_miss_thold); + ap_printk(KERN_DEBUG "[CQM] TX failure subscribe: %d\n", + info->cqm_tx_fail_thold); + priv->cqm_rssi_thold = info->cqm_rssi_thold; + priv->cqm_rssi_hyst = info->cqm_rssi_hyst; +#endif /* USE_STE_EXTENSIONS */ + if (info->cqm_rssi_thold || info->cqm_rssi_hyst) { + /* RSSI subscription enabled */ + /* TODO: It's not a correct way of setting threshold. + * Upper and lower must be set equal here and adjusted + * in callback. However current implementation is much + * more relaible and stable. */ + threshold.upperThreshold = + info->cqm_rssi_thold + info->cqm_rssi_hyst; + threshold.lowerThreshold = + info->cqm_rssi_thold; + threshold.rssiRcpiMode |= + WSM_RCPI_RSSI_THRESHOLD_ENABLE; + } else { + /* There is a bug in FW, see sta.c. We have to enable + * dummy subscription to get correct RSSI values. */ + threshold.rssiRcpiMode |= + WSM_RCPI_RSSI_THRESHOLD_ENABLE | + WSM_RCPI_RSSI_DONT_USE_UPPER | + WSM_RCPI_RSSI_DONT_USE_LOWER; + } + WARN_ON(wsm_set_rcpi_rssi_threshold(priv, &threshold)); + +#ifdef USE_STE_EXTENSIONS + priv->cqm_tx_failure_thold = info->cqm_tx_fail_thold; + priv->cqm_tx_failure_count = 0; + + if (priv->cqm_beacon_loss_count != + info->cqm_beacon_miss_thold) { + priv->cqm_beacon_loss_count = + info->cqm_beacon_miss_thold; + priv->bss_params.beaconLostCount = + priv->cqm_beacon_loss_count ? + priv->cqm_beacon_loss_count : + priv->cqm_link_loss_count; + WARN_ON(wsm_set_bss_params(priv, &priv->bss_params)); + } +#endif /* USE_STE_EXTENSIONS */ + } + mutex_unlock(&priv->conf_mutex); +} + +void cw1200_multicast_start_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, multicast_start_work); + + (void)cw1200_set_tim_impl(priv, true); + wsm_lock_tx(priv); + priv->suspend_multicast = false; + wsm_unlock_tx(priv); + cw1200_bh_wakeup(priv); +} + +void cw1200_multicast_stop_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, multicast_stop_work); + + /* Lock flushes send queue in device. Just to make sure DTIM beacom + * and frames are sent. */ + wsm_lock_tx(priv); + priv->suspend_multicast = true; + (void)cw1200_set_tim_impl(priv, false); + wsm_unlock_tx(priv); +} + +/* ******************************************************************** */ +/* WSM callback */ +void cw1200_suspend_resume(struct cw1200_common *priv, + struct wsm_suspend_resume *arg) +{ + int queue = 1 << wsm_queue_id_to_linux(arg->queue); + u32 unicast = 1 << arg->link_id; + u32 after_dtim = 1 << CW1200_LINK_ID_AFTER_DTIM; + u32 wakeup_required = 0; + u32 set = 0; + u32 clear; + u32 tx_suspend_mask; + int i; + + if (!arg->link_id) /* For all links */ + unicast = (1 << (CW1200_MAX_STA_IN_AP_MODE + 1)) - 2; + + ap_printk(KERN_DEBUG "[AP] %s: %s\n", + arg->stop ? "stop" : "start", + arg->multicast ? "broadcast" : "unicast"); + + if (arg->multicast) { + if (arg->stop) + queue_work(priv->workqueue, + &priv->multicast_stop_work); + else { + /* Handle only if there is data to be sent */ + for (i = 0; i < 4; ++i) { + if (cw1200_queue_get_num_queued( + &priv->tx_queue[i], + after_dtim)) { + queue_work(priv->workqueue, + &priv->multicast_start_work); + break; + } + } + } + } else { + if (arg->stop) + set = unicast; + else + set = 0; + + clear = set ^ unicast; + + /* TODO: if (!priv->uapsd) */ + queue = 0x0F; + + for (i = 0; i < 4; ++i) { + if (!(queue & (1 << i))) + continue; + + tx_suspend_mask = priv->tx_suspend_mask[i]; + priv->tx_suspend_mask[i] = + (tx_suspend_mask & ~clear) | set; + + wakeup_required = wakeup_required || + cw1200_queue_get_num_queued( + &priv->tx_queue[i], + tx_suspend_mask & clear); + } + } + if (wakeup_required) + cw1200_bh_wakeup(priv); + return; +} + + +/* ******************************************************************** */ +/* AP privates */ + +static int cw1200_upload_beacon(struct cw1200_common *priv) +{ + int ret = 0; + const u8 *ssidie; + const struct ieee80211_mgmt *mgmt; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_BEACON, + }; + + ap_printk(KERN_DEBUG "[AP] %s.\n", __func__); + + frame.skb = ieee80211_beacon_get(priv->hw, priv->vif); + if (WARN_ON(!frame.skb)) + return -ENOMEM; + + mgmt = (struct ieee80211_mgmt *)frame.skb->data; + ssidie = cfg80211_find_ie(WLAN_EID_SSID, + mgmt->u.beacon.variable, + frame.skb->len - (mgmt->u.beacon.variable - frame.skb->data)); + memset(priv->ssid, 0, sizeof(priv->ssid)); + if (ssidie) { + priv->ssid_length = ssidie[1]; + if (WARN_ON(priv->ssid_length > sizeof(priv->ssid))) + priv->ssid_length = sizeof(priv->ssid); + memcpy(priv->ssid, &ssidie[2], priv->ssid_length); + } else { + priv->ssid_length = 0; + } + + ret = wsm_set_template_frame(priv, &frame); + if (!ret) { + /* TODO: Distille probe resp; remove TIM + * and other beacon-specific IEs */ + *(__le16 *)frame.skb->data = + __cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_RESP); + frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE; + ret = wsm_set_template_frame(priv, &frame); + } + dev_kfree_skb(frame.skb); + + return ret; +} + +static int cw1200_start_ap(struct cw1200_common *priv) +{ + int ret; + struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; + struct wsm_start start = { + .mode = WSM_START_MODE_AP, + .band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G, + .channelNumber = priv->channel->hw_value, + .beaconInterval = conf->beacon_int, + .DTIMPeriod = conf->dtim_period, + .preambleType = conf->use_short_preamble ? + WSM_JOIN_PREAMBLE_SHORT : + WSM_JOIN_PREAMBLE_LONG, + .probeDelay = 100, + .basicRateSet = cw1200_rate_mask_to_wsm(priv, + conf->basic_rates), + .ssidLength = priv->ssid_length, + }; + struct wsm_beacon_transmit transmit = { + .enableBeaconing = priv->enable_beacon, + }; + + memcpy(&start.ssid[0], priv->ssid, start.ssidLength); + + ap_printk(KERN_DEBUG "[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s %s.\n", + start.channelNumber, start.band, + start.beaconInterval, start.DTIMPeriod, + start.basicRateSet, + start.ssidLength, start.ssid, + transmit.enableBeaconing ? "ena" : "dis"); + ret = WARN_ON(wsm_start(priv, &start)); + if (!ret) + ret = WARN_ON(cw1200_upload_keys(priv)); + if (!ret) + ret = WARN_ON(wsm_beacon_transmit(priv, &transmit)); + if (!ret) + priv->join_status = CW1200_JOIN_STATUS_AP; + return ret; +} + +static int cw1200_update_beaconing(struct cw1200_common *priv) +{ + struct wsm_reset reset = { + .link_id = 0, + .reset_statistics = true, + }; + + if (priv->mode == NL80211_IFTYPE_AP) { + ap_printk(KERN_DEBUG "[AP] %s.\n", __func__); + WARN_ON(wsm_reset(priv, &reset)); + WARN_ON(cw1200_start_ap(priv)); + } + return 0; +} + diff --git a/drivers/staging/cw1200/ap.h b/drivers/staging/cw1200/ap.h new file mode 100644 index 00000000000..77c364dfc2d --- /dev/null +++ b/drivers/staging/cw1200/ap.h @@ -0,0 +1,36 @@ +/* + * mac80211 STA and AP API for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#ifndef AP_H_INCLUDED +#define AP_H_INCLUDED + +int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, + bool set); +int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta); +void cw1200_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed); + +void cw1200_suspend_resume(struct cw1200_common *priv, + struct wsm_suspend_resume *arg); +void cw1200_set_tim_work(struct work_struct *work); +void cw1200_multicast_start_work(struct work_struct *work); +void cw1200_multicast_stop_work(struct work_struct *work); + + +#endif diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c new file mode 100644 index 00000000000..94711af1ea3 --- /dev/null +++ b/drivers/staging/cw1200/bh.c @@ -0,0 +1,419 @@ +/* + * Device handling thread implementation for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on: + * ST-Ericsson UMAC CW1200 driver, which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh + * + * 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. + */ + +#include +#include + +#include "cw1200.h" +#include "bh.h" +#include "hwio.h" +#include "wsm.h" +#include "sbus.h" + +#ifdef CW1200_DEBUG_ENABLE_BH_LOGS +#define bh_printk(...) printk(__VA_ARGS__) +#else +#define bh_printk(...) +#endif + +static int cw1200_bh(void *arg); + +/* TODO: Werify these numbers with WSM specification. */ +#define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4) +/* an SPI message cannot be bigger than (2"12-1)*2 bytes + * "*2" to cvt to bytes */ +#define MAX_SZ_RD_WR_BUFFERS (DOWNLOAD_BLOCK_SIZE_WR*2) +#define PIGGYBACK_CTRL_REG (2) +#define EFFECTIVE_BUF_SIZE (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG) + +typedef int (*cw1200_wsm_handler)(struct cw1200_common *priv, + u8 *data, size_t size); + + +int cw1200_register_bh(struct cw1200_common *priv) +{ + int err = 0; + struct sched_param param = { .sched_priority = 1 }; + bh_printk(KERN_DEBUG "[BH] register.\n"); + BUG_ON(priv->bh_thread); + atomic_set(&priv->bh_rx, 0); + atomic_set(&priv->bh_tx, 0); + atomic_set(&priv->bh_term, 0); + priv->buf_id_tx = 0; + priv->buf_id_rx = 0; + init_waitqueue_head(&priv->bh_wq); + init_waitqueue_head(&priv->hw_bufs_used_wq); + priv->bh_thread = kthread_create(&cw1200_bh, priv, "cw1200_bh"); + if (IS_ERR(priv->bh_thread)) { + err = PTR_ERR(priv->bh_thread); + priv->bh_thread = NULL; + } else { + WARN_ON(sched_setscheduler(priv->bh_thread, + SCHED_FIFO, ¶m)); +#ifdef HAS_PUT_TASK_STRUCT + get_task_struct(priv->bh_thread); +#endif + wake_up_process(priv->bh_thread); + } + return err; +} + +void cw1200_unregister_bh(struct cw1200_common *priv) +{ + struct task_struct *thread = priv->bh_thread; + if (WARN_ON(!thread)) + return; + + priv->bh_thread = NULL; + bh_printk(KERN_DEBUG "[BH] unregister.\n"); + atomic_add(1, &priv->bh_term); + wake_up_interruptible(&priv->bh_wq); + kthread_stop(thread); +#ifdef HAS_PUT_TASK_STRUCT + put_task_struct(thread); +#endif +} + +void cw1200_irq_handler(struct cw1200_common *priv) +{ + bh_printk(KERN_DEBUG "[BH] irq.\n"); + if (/* WARN_ON */(priv->bh_error)) + return; + + if (atomic_add_return(1, &priv->bh_rx) == 1) + wake_up_interruptible(&priv->bh_wq); +} + +void cw1200_bh_wakeup(struct cw1200_common *priv) +{ + bh_printk(KERN_DEBUG "[BH] wakeup.\n"); + if (WARN_ON(priv->bh_error)) + return; + + if (atomic_add_return(1, &priv->bh_tx) == 1) + wake_up_interruptible(&priv->bh_wq); +} + +static inline void wsm_alloc_tx_buffer(struct cw1200_common *priv) +{ + ++priv->hw_bufs_used; +} + +static int wsm_release_tx_buffer(struct cw1200_common *priv) +{ + int ret = 0; + int hw_bufs_used = priv->hw_bufs_used--; + if (WARN_ON(!hw_bufs_used)) + ret = -1; + else if (hw_bufs_used >= priv->wsm_caps.numInpChBufs - 1) + ret = 1; + else if (hw_bufs_used == 1) + wake_up_interruptible(&priv->hw_bufs_used_wq); + return ret; +} + +static struct sk_buff *cw1200_get_skb(struct cw1200_common *priv, size_t len) +{ + struct sk_buff *skb; + size_t alloc_len = (len > SDIO_BLOCK_SIZE) ? len : SDIO_BLOCK_SIZE; + + if (len > SDIO_BLOCK_SIZE || !priv->skb_cache) { + skb = dev_alloc_skb(alloc_len + + WSM_TX_EXTRA_HEADROOM + + 8 /* TKIP IV */ + + 12 /* TKIP ICV + MIC */ + - 2 /* Piggyback */); + /* In AP mode RXed SKB can be looped back as a broadcast. + * Here we reserve enough space for headers. */ + skb_reserve(skb, WSM_TX_EXTRA_HEADROOM + + 8 /* TKIP IV */ + - WSM_RX_EXTRA_HEADROOM); + } else { + skb = priv->skb_cache; + priv->skb_cache = NULL; + } + return skb; +} + +static void cw1200_put_skb(struct cw1200_common *priv, struct sk_buff *skb) +{ + if (priv->skb_cache) + dev_kfree_skb(skb); + else + priv->skb_cache = skb; +} + +static inline int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv, + u16 *ctrl_reg) +{ + int ret; + + ret = cw1200_reg_read_16(priv, + ST90TDS_CONTROL_REG_ID, ctrl_reg); + if (ret) { + ret = cw1200_reg_read_16(priv, + ST90TDS_CONTROL_REG_ID, ctrl_reg); + if (ret) + printk(KERN_ERR + "[BH] Failed to read control register.\n"); + else + printk(KERN_WARNING + "[BH] Second attempt to read control " + "register passed. This is a firmware bug.\n"); + } + + return ret; +} + +static int cw1200_bh(void *arg) +{ + struct cw1200_common *priv = arg; + struct sk_buff *skb_rx = NULL; + size_t read_len = 0; + int rx, tx, term; + struct wsm_hdr *wsm; + size_t wsm_len; + int wsm_id; + u8 wsm_seq; + int rx_resync = 1; + u16 ctrl_reg = 0; + int tx_allowed; + + for (;;) { + int status = wait_event_interruptible(priv->bh_wq, ({ + rx = atomic_xchg(&priv->bh_rx, 0); + tx = atomic_xchg(&priv->bh_tx, 0); + term = atomic_xchg(&priv->bh_term, 0); + (rx || tx || term); + })); + + if (status || term) + break; + + if (rx) { + size_t alloc_len; + u8 *data; + + if (WARN_ON(cw1200_bh_read_ctrl_reg( + priv, &ctrl_reg))) + break; +rx: + read_len = (ctrl_reg & 0xFFF) * 2; + if (!read_len) + goto tx; + + if (WARN_ON((read_len < sizeof(struct wsm_hdr)) || + (read_len > EFFECTIVE_BUF_SIZE))) { + printk(KERN_DEBUG "Invalid read len: %d", + read_len); + break; + } + + /* Add SIZE of PIGGYBACK reg (CONTROL Reg) + * to the NEXT Message length + 2 Bytes for SKB */ + read_len = read_len + 2; + + BUG_ON(SDIO_BLOCK_SIZE & (SDIO_BLOCK_SIZE - 1)); + +#ifdef CW1200_NON_POWER_OF_TWO_BLOCKSIZES + alloc_len = priv->sbus_ops->align_size( + priv->sbus_priv, read_len); +#else + /* Platform's SDIO workaround */ + alloc_len = read_len & ~(SDIO_BLOCK_SIZE - 1); + if (read_len & (SDIO_BLOCK_SIZE - 1)) + alloc_len += SDIO_BLOCK_SIZE; +#endif + + skb_rx = cw1200_get_skb(priv, alloc_len); + if (WARN_ON(!skb_rx)) + break; + + skb_trim(skb_rx, 0); + skb_put(skb_rx, read_len); + data = skb_rx->data; + if (WARN_ON(!data)) + break; + + if (WARN_ON(cw1200_data_read(priv, data, alloc_len))) + break; + + /* Piggyback */ + ctrl_reg = __le16_to_cpu( + ((__le16 *)data)[alloc_len / 2 - 1]); + + wsm = (struct wsm_hdr *)data; + wsm_len = __le32_to_cpu(wsm->len); + if (WARN_ON(wsm_len > read_len)) + break; + +#ifdef CW1200_DEBUG_ENABLE_WSM_DUMPS + print_hex_dump_bytes("<-- ", DUMP_PREFIX_NONE, + data, wsm_len); +#endif /* CW1200_DEBUG_ENABLE_WSM_DUMPS */ + + wsm_id = __le32_to_cpu(wsm->id) & 0xFFF; + wsm_seq = (__le32_to_cpu(wsm->id) >> 13) & 7; + + skb_trim(skb_rx, wsm_len); + + if (unlikely(wsm_id == 0x0800)) { + wsm_handle_exception(priv, + &data[sizeof(*wsm)], + wsm_len - sizeof(*wsm)); + break; + } else if (unlikely(!rx_resync)) { + if (WARN_ON(wsm_seq != priv->wsm_rx_seq)) + break; + } + priv->wsm_rx_seq = (wsm_seq + 1) & 7; + rx_resync = 0; + + if (wsm_id & 0x0400) { + int rc = wsm_release_tx_buffer(priv); + /* TODO: 3.60 Multi-transmit confirmation + * requires special handling. + * Not supported yet. */ + BUG_ON((wsm_id & 0x3F) == 0x1E); + if (WARN_ON(rc < 0)) + break; + else if (rc > 0) + tx = 1; + } + + /* cw1200_wsm_rx takes care on SKB livetime */ + if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb_rx))) + break; + + if (skb_rx) { + cw1200_put_skb(priv, skb_rx); + skb_rx = NULL; + } + + read_len = 0; + + { + /* HACK!!! */ + /* Read CONFIG Register Value - HW BUG */ + u32 val32; + WARN_ON(cw1200_reg_read_32(priv, + ST90TDS_CONFIG_REG_ID, &val32)); + } + } + +tx: + /* HACK! One buffer is reserved for control path */ + BUG_ON(priv->hw_bufs_used > priv->wsm_caps.numInpChBufs); + tx_allowed = + priv->hw_bufs_used + 1 < priv->wsm_caps.numInpChBufs; + if (unlikely(!tx_allowed && priv->wsm_cmd.ptr)) + tx_allowed = 1; + + if (tx && tx_allowed) { + size_t tx_len; + u8 *data; + int ret; + + wsm_alloc_tx_buffer(priv); + ret = wsm_get_tx(priv, &data, &tx_len); + if (ret <= 0) { + wsm_release_tx_buffer(priv); + if (WARN_ON(ret < 0)) + break; + } else { + wsm = (struct wsm_hdr *)data; + BUG_ON(tx_len < sizeof(*wsm)); + BUG_ON(__le32_to_cpu(wsm->len) != tx_len); + +#if 0 /* count is not implemented */ + if (ret > 1) + atomic_add(1, &priv->bh_tx); +#else + atomic_add(1, &priv->bh_tx); +#endif + + +#ifdef CW1200_NON_POWER_OF_TWO_BLOCKSIZES + tx_len = priv->sbus_ops->align_size( + priv->sbus_priv, tx_len); +#else + /* HACK!!! Platform limitation. + * It is also supported by upper layer: + * there is always enough space at the + * end of the buffer. */ + if (tx_len & (SDIO_BLOCK_SIZE - 1)) { + tx_len &= ~(SDIO_BLOCK_SIZE - 1); + tx_len += SDIO_BLOCK_SIZE; + } +#endif + + wsm->id |= __cpu_to_le32( + priv->wsm_tx_seq << 13); + + if (WARN_ON(cw1200_data_write(priv, + data, tx_len))) { + wsm_release_tx_buffer(priv); + break; + } + +#ifdef CW1200_DEBUG_ENABLE_WSM_DUMPS + print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE, + data, __le32_to_cpu(wsm->len)); +#endif /* CW1200_DEBUG_ENABLE_WSM_DUMPS */ + + wsm_txed(priv, data); + priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & 7; + } + } + + /* HACK!!! Device tends not to send interrupt + * if this extra check is missing */ + if (!(ctrl_reg & 0xFFF)) { + if (WARN_ON(cw1200_bh_read_ctrl_reg( + priv, &ctrl_reg))) + break; + } + + if (ctrl_reg & 0xFFF) + goto rx; + } + + if (skb_rx) { + cw1200_put_skb(priv, skb_rx); + skb_rx = NULL; + } + + + if (!term) { + cw1200_dbg(CW1200_DBG_ERROR, "[BH] Fatal error, exitting.\n"); + priv->bh_error = 1; + /* TODO: schedule_work(recovery) */ +#ifndef HAS_PUT_TASK_STRUCT + /* The only reason of having this stupid code here is + * that __put_task_struct is not exported by kernel. */ + for (;;) { + int status = wait_event_interruptible(priv->bh_wq, ({ + term = atomic_xchg(&priv->bh_term, 0); + (term); + })); + + if (status || term) + break; + } +#endif + } + return 0; +} diff --git a/drivers/staging/cw1200/bh.h b/drivers/staging/cw1200/bh.h new file mode 100644 index 00000000000..b44388e164b --- /dev/null +++ b/drivers/staging/cw1200/bh.h @@ -0,0 +1,26 @@ +/* + * Device handling thread interface for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#ifndef CW1200_BH_H +#define CW1200_BH_H + +/* extern */ struct cw1200_common; + +/* TODO: 512, actually. Was increased to 1024 + * for compatibility with particular FW. */ +#define SDIO_BLOCK_SIZE (1024) + +int cw1200_register_bh(struct cw1200_common *priv); +void cw1200_unregister_bh(struct cw1200_common *priv); +void cw1200_irq_handler(struct cw1200_common *priv); +void cw1200_bh_wakeup(struct cw1200_common *priv); + +#endif /* CW1200_BH_H */ diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h new file mode 100644 index 00000000000..2251f1ca756 --- /dev/null +++ b/drivers/staging/cw1200/cw1200.h @@ -0,0 +1,228 @@ +/* + * Common private data for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on the mac80211 Prism54 code, which is + * Copyright (c) 2006, Michael Wu + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * 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. + */ + +#ifndef CW1200_H +#define CW1200_H + +#include +#include +#include +#include +#include + +#include "queue.h" +#include "wsm.h" +#include "scan.h" +#include "txrx.h" +#include "ht.h" + +/* Debug switches */ + +/* Dump all WSM (HI) messages. Quite a lot of logs. */ +/* #define CW1200_DEBUG_ENABLE_WSM_DUMPS */ + +/* Enable WSM logs. Useful for low-level debugging (WSM HI control flow). */ +/* #define CW1200_DEBUG_ENABLE_WSM_LOGS */ + +/* Enable BH logs. Useful for ultra-level debugging (interrupts and so on). */ +/* #define CW1200_DEBUG_ENABLE_BH_LOGS */ + +/* Enable TX/RX logs. */ +/* #define CW1200_TXRX_DEBUG */ + +/* extern */ struct sbus_ops; +/* extern */ struct task_struct; + +#ifdef CW1200_TXRX_DEBUG +#define txrx_printk(...) printk(__VA_ARGS__) +#else +#define txrx_printk(...) +#endif + +#define CW1200_MAX_CTRL_FRAME_LEN (0x1000) + +#define CW1200_MAX_STA_IN_AP_MODE (5) +#define CW1200_LINK_ID_AFTER_DTIM (CW1200_MAX_STA_IN_AP_MODE + 1) + +enum cw1200_join_status { + CW1200_JOIN_STATUS_MONITOR = 0, + CW1200_JOIN_STATUS_STA, + CW1200_JOIN_STATUS_AP, +}; + +struct cw1200_common { + struct cw1200_queue tx_queue[4]; + + struct ieee80211_hw *hw; + struct ieee80211_vif *vif; + struct device *pdev; + struct workqueue_struct *workqueue; + + struct mutex conf_mutex; + + const struct sbus_ops *sbus_ops; + struct sbus_priv *sbus_priv; + + /* HW type (HIF_...) */ + int hw_type; + int hw_revision; + + /* firmware/hardware info */ + unsigned int tx_hdr_len; + + /* Radio data */ + int output_power; + int noise; + + /* calibration, output power limit and rssi<->dBm conversation data */ + + /* BBP/MAC state */ + struct ieee80211_rate *rates; + u8 mac_addr[ETH_ALEN]; + struct ieee80211_channel *channel; + u8 bssid[ETH_ALEN]; + struct wsm_edca_params edca; + struct wsm_association_mode association_mode; + struct wsm_set_bss_params bss_params; + struct cw1200_ht_info ht_info; + struct wsm_set_pm powersave_mode; + int cqm_rssi_thold; + unsigned cqm_rssi_hyst; + unsigned cqm_tx_failure_thold; + unsigned cqm_tx_failure_count; + int cqm_link_loss_count; + int cqm_beacon_loss_count; + int channel_switch_in_progress; + wait_queue_head_t channel_switch_done; + u8 long_frame_max_tx_count; + u8 short_frame_max_tx_count; + int mode; + bool enable_beacon; + size_t ssid_length; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + + /* BH */ + atomic_t bh_rx; + atomic_t bh_tx; + atomic_t bh_term; + struct task_struct *bh_thread; + int bh_error; + wait_queue_head_t bh_wq; + int buf_id_tx; /* byte */ + int buf_id_rx; /* byte */ + int wsm_rx_seq; /* byte */ + int wsm_tx_seq; /* byte */ + int hw_bufs_used; + wait_queue_head_t hw_bufs_used_wq; + struct sk_buff *skb_cache; + + /* WSM */ + struct wsm_caps wsm_caps; + struct mutex wsm_cmd_mux; + struct wsm_buf wsm_cmd_buf; + struct wsm_cmd wsm_cmd; + wait_queue_head_t wsm_cmd_wq; + wait_queue_head_t wsm_startup_done; + struct wsm_cbc wsm_cbc; + atomic_t tx_lock; + + /* Scan status */ + struct cw1200_scan scan; + + /* WSM Join */ + enum cw1200_join_status join_status; + u8 join_bssid[ETH_ALEN]; + const struct wsm_tx *join_pending_frame; + struct work_struct join_work; + struct delayed_work join_timeout; + struct work_struct unjoin_work; + int join_dtim_period; + + /* TX/RX and security */ + s8 wep_default_key_id; + struct work_struct wep_key_work; + u32 key_map; + struct wsm_add_key keys[WSM_KEY_MAX_INDEX + 1]; + unsigned long rx_timestamp; + + /* AP powersave */ + u32 link_id_map; + u32 tx_suspend_mask[4]; + u32 sta_asleep_mask; + bool suspend_multicast; + struct work_struct set_tim_work; + struct work_struct multicast_start_work; + struct work_struct multicast_stop_work; + + + /* WSM events and CQM implementation */ + spinlock_t event_queue_lock; + struct list_head event_queue; + struct work_struct event_handler; + struct delayed_work bss_loss_work; + struct delayed_work connection_loss_work; +#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE + struct delayed_work keep_alive_work; + unsigned long last_activity_time; +#endif + struct work_struct tx_failure_work; + int delayed_link_loss; + + /* TX rate policy cache */ + struct tx_policy_cache tx_policy_cache; + struct work_struct tx_policy_upload_work; + + /* cryptographic engine information */ + + /* bit field of glowing LEDs */ + u16 softled_state; + + /* statistics */ + struct ieee80211_low_level_stats stats; +}; + +struct cw1200_sta_priv { + int link_id; +}; + +/* interfaces for the drivers */ +int cw1200_probe(const struct sbus_ops *sbus_ops, + struct sbus_priv *sbus, + struct device *pdev, + struct cw1200_common **pself); +void cw1200_release(struct cw1200_common *self); + +#define CW1200_DBG_MSG 0x00000001 +#define CW1200_DBG_NIY 0x00000002 +#define CW1200_DBG_SBUS 0x00000004 +#define CW1200_DBG_INIT 0x00000008 +#define CW1200_DBG_ERROR 0x00000010 +#define CW1200_DBG_LEVEL 0xFFFFFFFF + +#define cw1200_dbg(level, ...) \ + do { \ + if ((level) & CW1200_DBG_LEVEL) \ + printk(KERN_DEBUG __VA_ARGS__); \ + } while (0) + +#define STUB() \ + do { \ + cw1200_dbg(CW1200_DBG_NIY, "%s: STUB at line %d.\n", \ + __func__, __LINE__); \ + } while (0) + +#endif /* CW1200_H */ diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c new file mode 100644 index 00000000000..0864984436c --- /dev/null +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -0,0 +1,322 @@ +/* + * Mac80211 SDIO driver for ST-Ericsson CW1200 device + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "cw1200.h" +#include "sbus.h" + +MODULE_AUTHOR("Dmitry Tarnyagin "); +MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SDIO driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("cw1200_wlan"); + +/* Module parameter for MMC interface to probe the driver on. */ +static char device_name[10] = "mmc3"; +module_param_string(device, device_name, sizeof(device_name), S_IRUGO); +MODULE_PARM_DESC(device, "SDIO interface device is connected to"); + +#define CW1200_GPIO_LOW (0) +#define CW1200_GPIO_HIGH (1) + +struct sbus_priv { + struct sdio_func *func; + struct cw1200_common *core; + spinlock_t lock; + sbus_irq_handler irq_handler; + void *irq_priv; +}; + +static const struct sdio_device_id if_sdio_ids[] = { + { SDIO_DEVICE(SDIO_ANY_ID, SDIO_ANY_ID) }, + { /* end: all zeroes */ }, +}; + +/* sbus_ops implemetation */ + +static int cw1200_sdio_memcpy_fromio(struct sbus_priv *self, + unsigned int addr, + void *dst, int count) +{ + int ret = sdio_memcpy_fromio(self->func, dst, addr, count); + if (ret) { + printk(KERN_ERR "!!! Can't read %d bytes from 0x%.8X. Err %d.\n", + count, addr, ret); + } + return ret; +} + +static int cw1200_sdio_memcpy_toio(struct sbus_priv *self, + unsigned int addr, + const void *src, int count) +{ + return sdio_memcpy_toio(self->func, addr, (void *)src, count); +} + +static void cw1200_sdio_lock(struct sbus_priv *self) +{ + sdio_claim_host(self->func); +} + +static void cw1200_sdio_unlock(struct sbus_priv *self) +{ + sdio_release_host(self->func); +} + +static void cw1200_sdio_irq_handler(struct sdio_func *func) +{ + struct sbus_priv *self = sdio_get_drvdata(func); + unsigned long flags; + + BUG_ON(!self); + spin_lock_irqsave(&self->lock, flags); + if (self->irq_handler) + self->irq_handler(self->irq_priv); + spin_unlock_irqrestore(&self->lock, flags); +} + +static int cw1200_sdio_irq_subscribe(struct sbus_priv *self, + sbus_irq_handler handler, + void *priv) +{ + int ret; + unsigned long flags; + + if (!handler) + return -EINVAL; + + spin_lock_irqsave(&self->lock, flags); + self->irq_priv = priv; + self->irq_handler = handler; + spin_unlock_irqrestore(&self->lock, flags); + + printk(KERN_DEBUG "SW IRQ subscribe\n"); + sdio_claim_host(self->func); + ret = sdio_claim_irq(self->func, cw1200_sdio_irq_handler); + sdio_release_host(self->func); + return ret; +} + +static int cw1200_sdio_irq_unsubscribe(struct sbus_priv *self) +{ + int ret; + unsigned long flags; + + WARN_ON(!self->irq_handler); + if (!self->irq_handler) + return 0; + + printk(KERN_DEBUG "SW IRQ unsubscribe\n"); + sdio_claim_host(self->func); + ret = sdio_release_irq(self->func); + sdio_release_host(self->func); + + spin_lock_irqsave(&self->lock, flags); + self->irq_priv = NULL; + self->irq_handler = NULL; + spin_unlock_irqrestore(&self->lock, flags); + + return ret; +} + +#ifdef CW1200_U8500_PLATFORM +static int cw1200_detect_card(void) +{ + /* HACK!!! + * Rely on mmc->class_dev.class set in mmc_alloc_host + * Tricky part: a new mmc hook is being (temporary) created + * to discover mmc_host class. + * Do you know more elegant way how to enumerate mmc_hosts? + */ + + struct mmc_host *mmc = NULL; + struct class_dev_iter iter; + struct device *dev; + + mmc = mmc_alloc_host(0, NULL); + if (!mmc) + return -ENOMEM; + + BUG_ON(!mmc->class_dev.class); + class_dev_iter_init(&iter, mmc->class_dev.class, NULL, NULL); + for (;;) { + dev = class_dev_iter_next(&iter); + if (!dev) { + printk(KERN_ERR "CW1200: %s is not found.\n", + device_name); + break; + } else { + struct mmc_host *host = container_of(dev, + struct mmc_host, class_dev); + + if (dev_name(&host->class_dev) && + strcmp(dev_name(&host->class_dev), + device_name)) + continue; + + mmc_detect_change(host, 10); + break; + } + } + mmc_free_host(mmc); + return 0; +} + +static int cw1200_sdio_off(void) +{ + gpio_set_value(215, CW1200_GPIO_LOW); + cw1200_detect_card(); + gpio_free(215); + return 0; +} + +static int cw1200_sdio_on(void) +{ + gpio_request(215, "cw1200_sdio"); + gpio_direction_output(215, 1); + gpio_set_value(215, CW1200_GPIO_HIGH); + cw1200_detect_card(); + return 0; +} + +static int cw1200_sdio_reset(struct sbus_priv *self) +{ + cw1200_sdio_off(); + mdelay(1000); + cw1200_sdio_on(); + return 0; +} + +#else /* CW1200_U8500_PLATFORM */ + +static int cw1200_sdio_off(void) +{ + return 0; +} + +static int cw1200_sdio_on(void) +{ + return 0; +} + +static int cw1200_sdio_reset(struct sbus_priv *self) +{ + return 0; +} + +#endif /* CW1200_U8500_PLATFORM */ + +static size_t cw1200_align_size(struct sbus_priv *self, size_t size) +{ + return sdio_align_size(self->func, size); +} + +static struct sbus_ops cw1200_sdio_sbus_ops = { + .sbus_memcpy_fromio = cw1200_sdio_memcpy_fromio, + .sbus_memcpy_toio = cw1200_sdio_memcpy_toio, + .lock = cw1200_sdio_lock, + .unlock = cw1200_sdio_unlock, + .irq_subscribe = cw1200_sdio_irq_subscribe, + .irq_unsubscribe = cw1200_sdio_irq_unsubscribe, + .reset = cw1200_sdio_reset, + .align_size = cw1200_align_size, +}; + +/* Probe Function to be called by SDIO stack when device is discovered */ +static int cw1200_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct sbus_priv *self; + int status; + cw1200_dbg(CW1200_DBG_INIT, "Probe called\n"); + + self = kzalloc(sizeof(*self), GFP_KERNEL); + if (!self) { + cw1200_dbg(CW1200_DBG_ERROR, "Can't allocate SDIO sbus_priv."); + return -ENOMEM; + } + + spin_lock_init(&self->lock); + self->func = func; + sdio_set_drvdata(func, self); + sdio_claim_host(func); + sdio_enable_func(func); + sdio_release_host(func); + + status = cw1200_probe(&cw1200_sdio_sbus_ops, + self, &func->dev, &self->core); + if (status) { + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + sdio_set_drvdata(func, NULL); + kfree(self); + } + + return status; +} + +/* Disconnect Function to be called by SDIO stack when device is disconnected */ +static void cw1200_sdio_disconnect(struct sdio_func *func) +{ + struct sbus_priv *self = sdio_get_drvdata(func); + + if (self) { + if (self->core) { + cw1200_release(self->core); + self->core = NULL; + } + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + sdio_set_drvdata(func, NULL); + kfree(self); + } +} + +static struct sdio_driver sdio_driver = { + .name = "cw1200_wlan", + .id_table = if_sdio_ids, + .probe = cw1200_sdio_probe, + .remove = cw1200_sdio_disconnect, +}; + +/* Init Module function -> Called by insmod */ +static int __init cw1200_sdio_init(void) +{ + int ret = sdio_register_driver(&sdio_driver); + if (ret) + return ret; + ret = cw1200_sdio_on(); + if (ret) + sdio_unregister_driver(&sdio_driver); + return ret; +} + +/* Called at Driver Unloading */ +static void __exit cw1200_sdio_exit(void) +{ + sdio_unregister_driver(&sdio_driver); + cw1200_sdio_off(); +} + + +module_init(cw1200_sdio_init); +module_exit(cw1200_sdio_exit); diff --git a/drivers/staging/cw1200/fwio.c b/drivers/staging/cw1200/fwio.c new file mode 100644 index 00000000000..a85062e2e3a --- /dev/null +++ b/drivers/staging/cw1200/fwio.c @@ -0,0 +1,560 @@ +/* + * Firmware I/O code for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on: + * ST-Ericsson UMAC CW1200 driver which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh + * + * 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. + */ + +#include +#include +#include +#include + +#include "cw1200.h" +#include "fwio.h" +#include "hwio.h" +#include "sbus.h" +#include "bh.h" + +static int cw1200_get_hw_type(u32 config_reg_val, int *major_revision) +{ + int hw_type = -1; + u32 silicon_type = (config_reg_val >> 24) & 0x3; + u32 silicon_vers = (config_reg_val >> 31) & 0x1; + + /* Check if we have CW1200 or STLC9000 */ + if ((silicon_type == 0x1) || (silicon_type == 0x2)) { + *major_revision = silicon_type; + if (silicon_vers) + hw_type = HIF_8601_VERSATILE; + else + hw_type = HIF_8601_SILICON; + } else { + *major_revision = 1; + hw_type = HIF_9000_SILICON_VERSTAILE; + } + + return hw_type; +} + +static int config_reg_read_stlc9000(struct cw1200_common *priv, + u16 reg, u32 *val) +{ + u16 val16; + int ret = cw1200_reg_read_16(priv, reg, &val16); + if (ret < 0) + return ret; + *val = val16; + return 0; +} + +static int config_reg_write_stlc9000(struct cw1200_common *priv, + u16 reg, u32 val) +{ + return cw1200_reg_write_16(priv, reg, (u16)val); +} + +static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) +{ + int ret, block, num_blocks; + unsigned i; + u32 val32; + u32 put = 0, get = 0; + u8 *buf = NULL; + const char *fw_path; + const struct firmware *firmware = NULL; + + /* Macroses are local. */ +#define APB_WRITE(reg, val) \ + do { \ + ret = cw1200_apb_write_32(priv, CW12000_APB(reg), (val)); \ + if (ret < 0) { \ + cw1200_dbg(CW1200_DBG_ERROR, \ + "%s: can't write %s at line %d.\n", \ + __func__, #reg, __LINE__); \ + goto error; \ + } \ + } while (0) +#define APB_READ(reg, val) \ + do { \ + ret = cw1200_apb_read_32(priv, CW12000_APB(reg), &(val)); \ + if (ret < 0) { \ + cw1200_dbg(CW1200_DBG_ERROR, \ + "%s: can't read %s at line %d.\n", \ + __func__, #reg, __LINE__); \ + goto error; \ + } \ + } while (0) +#define REG_WRITE(reg, val) \ + do { \ + ret = cw1200_reg_write_32(priv, (reg), (val)); \ + if (ret < 0) { \ + cw1200_dbg(CW1200_DBG_ERROR, \ + "%s: can't write %s at line %d.\n", \ + __func__, #reg, __LINE__); \ + goto error; \ + } \ + } while (0) +#define REG_READ(reg, val) \ + do { \ + ret = cw1200_reg_read_32(priv, (reg), &(val)); \ + if (ret < 0) { \ + cw1200_dbg(CW1200_DBG_ERROR, \ + "%s: can't read %s at line %d.\n", \ + __func__, #reg, __LINE__); \ + goto error; \ + } \ + } while (0) + + switch (priv->hw_revision) { + case CW1200_HW_REV_CUT10: + fw_path = FIRMWARE_CUT10; + break; + case CW1200_HW_REV_CUT11: + fw_path = FIRMWARE_CUT11; + break; + case CW1200_HW_REV_CUT20: + fw_path = FIRMWARE_CUT20; + break; + default: + cw1200_dbg(CW1200_DBG_ERROR, + "%s: invalid silicon revision %d.\n", + __func__, priv->hw_revision); + return -EINVAL; + } + + /* Initialize common registers */ + APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE); + APB_WRITE(DOWNLOAD_PUT_REG, 0); + APB_WRITE(DOWNLOAD_GET_REG, 0); + APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING); + APB_WRITE(DOWNLOAD_FLAGS_REG, 0); + + /* Write the NOP Instruction */ + REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000); + REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE); + + /* Release CPU from RESET */ + REG_READ(ST90TDS_CONFIG_REG_ID, val32); + val32 &= ~ST90TDS_CONFIG_CPU_RESET_BIT; + REG_WRITE(ST90TDS_CONFIG_REG_ID, val32); + + /* Enable Clock */ + val32 &= ~ST90TDS_CONFIG_CPU_CLK_DIS_BIT; + REG_WRITE(ST90TDS_CONFIG_REG_ID, val32); + + /* Load a firmware file */ + ret = request_firmware(&firmware, fw_path, priv->pdev); + if (ret) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't load firmware file %s.\n", + __func__, fw_path); + goto error; + } + BUG_ON(!firmware->data); + + buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA); + if (!buf) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't allocate firmware buffer.\n", __func__); + ret = -ENOMEM; + goto error; + } + + /* Check if the bootloader is ready */ + for (i = 0; i < 100; i += 1 + i / 2) { + APB_READ(DOWNLOAD_IMAGE_SIZE_REG, val32); + if (val32 == DOWNLOAD_I_AM_HERE) + break; + mdelay(i); + } /* End of for loop */ + + if (val32 != DOWNLOAD_I_AM_HERE) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: bootloader is not ready.\n", __func__); + ret = -ETIMEDOUT; + goto error; + } + + /* Calculcate number of download blocks */ + num_blocks = (firmware->size - 1) / DOWNLOAD_BLOCK_SIZE + 1; + + /* Updating the length in Download Ctrl Area */ + val32 = firmware->size; /* Explicit cast from size_t to u32 */ + APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, val32); + + /* Firmware downloading loop */ + for (block = 0; block < num_blocks ; block++) { + size_t tx_size; + size_t block_size; + + /* check the download status */ + APB_READ(DOWNLOAD_STATUS_REG, val32); + if (val32 != DOWNLOAD_PENDING) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: bootloader reported error %d.\n", + __func__, val32); + ret = -EIO; + goto error; + } + + /* loop until put - get <= 24K */ + for (i = 0; i < 100; i++) { + APB_READ(DOWNLOAD_GET_REG, get); + if ((put - get) <= + (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) + break; + mdelay(i); + } + + if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Timeout waiting for FIFO.\n", + __func__); + return -ETIMEDOUT; + } + + /* calculate the block size */ + tx_size = block_size = min((size_t)(firmware->size - put), + (size_t)DOWNLOAD_BLOCK_SIZE); + + memcpy(buf, &firmware->data[put], block_size); +#ifdef BUG_POWER_OF_TWO_BLOCKSIZE + if (block_size < DOWNLOAD_BLOCK_SIZE) { + memset(&buf[block_size], + 0, DOWNLOAD_BLOCK_SIZE - block_size); + tx_size = DOWNLOAD_BLOCK_SIZE; + } +#endif /* BUG_POWER_OF_TWO_BLOCKSIZE */ + + /* send the block to sram */ + ret = cw1200_apb_write(priv, + CW12000_APB(DOWNLOAD_FIFO_OFFSET + + (put & (DOWNLOAD_FIFO_SIZE - 1))), + buf, tx_size); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't write block at line %d.\n", + __func__, __LINE__); + goto error; + } + + /* update the put register */ + put += block_size; + APB_WRITE(DOWNLOAD_PUT_REG, put); + } /* End of firmware download loop */ + + /* Wait for the download completion */ + for (i = 0; i < 300; i += 1 + i / 2) { + APB_READ(DOWNLOAD_STATUS_REG, val32); + if (val32 != DOWNLOAD_PENDING) + break; + mdelay(i); + } + if (val32 != DOWNLOAD_SUCCESS) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: wait for download completion failed. " \ + "Read: 0x%.8X\n", __func__, val32); + ret = -ETIMEDOUT; + goto error; + } else { + cw1200_dbg(CW1200_DBG_MSG, + "Firmware download completed.\n"); + ret = 0; + } + +error: + kfree(buf); + if (firmware) + release_firmware(firmware); + return ret; + +#undef APB_WRITE +#undef APB_READ +#undef REG_WRITE +#undef REG_READ +} + +int cw1200_load_firmware(struct cw1200_common *priv) +{ + int ret; + int i; + u32 val32; + u16 val16; + u32 dpll = 0; + int major_revision; + int (*config_reg_read)(struct cw1200_common *priv, u16 reg, u32 *val); + int (*config_reg_write)(struct cw1200_common *priv, u16 reg, u32 val); + + BUG_ON(!priv); + + /* Read CONFIG Register Value - We will read 32 bits */ + ret = cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't read config register.\n", __func__); + goto out; + } + + priv->hw_type = cw1200_get_hw_type(val32, &major_revision); + if (priv->hw_type < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't deduct hardware type.\n", __func__); + ret = -ENOTSUPP; + goto out; + } + + switch (priv->hw_type) { + case HIF_8601_VERSATILE: + case HIF_8601_SILICON: + dpll = DPLL_INIT_VAL_CW1200; + config_reg_read = cw1200_reg_read_32; + config_reg_write = cw1200_reg_write_32; + break; + case HIF_9000_SILICON_VERSTAILE: + dpll = DPLL_INIT_VAL_9000; + config_reg_read = config_reg_read_stlc9000; + config_reg_write = config_reg_write_stlc9000; + break; + default: + BUG_ON(1); + } + + ret = cw1200_reg_write_32(priv, ST90TDS_TSET_GEN_R_W_REG_ID, dpll); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't write DPLL register.\n", __func__); + goto out; + } + + msleep(20); + + /* Read DPLL Reg value and compare with value written */ + ret = cw1200_reg_read_32(priv, + ST90TDS_TSET_GEN_R_W_REG_ID, &val32); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't read DPLL register.\n", __func__); + goto out; + } + + if (val32 != dpll) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: unable to initialise " \ + "DPLL register. Wrote 0x%.8X, read 0x%.8X.\n", + __func__, dpll, val32); + ret = -EIO; + goto out; + } + + /* Set wakeup bit in device */ + ret = cw1200_reg_read_16(priv, ST90TDS_CONTROL_REG_ID, &val16); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: set_wakeup: can't read " \ + "control register.\n", __func__); + goto out; + } + + ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, + val16 | ST90TDS_CONT_WUP_BIT); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: set_wakeup: can't write " \ + "control register.\n", __func__); + goto out; + } + + /* Wait for wakeup */ + for (i = 0 ; i < 300 ; i += 1 + i / 2) { + ret = cw1200_reg_read_16(priv, + ST90TDS_CONTROL_REG_ID, &val16); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: wait_for_wakeup: can't read " \ + "control register.\n", __func__); + goto out; + } + + if (val16 & ST90TDS_CONT_RDY_BIT) { + cw1200_dbg(CW1200_DBG_MSG, + "WLAN device is ready.\n"); + break; + } + msleep(i); + } + + if ((val16 & ST90TDS_CONT_RDY_BIT) == 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: wait_for_wakeup: device is not responding.\n", + __func__); + ret = -ETIMEDOUT; + goto out; + } + + if (major_revision == 1) { + /* CW1200 Hardware detection logic : Check for CUT1.1 */ + ret = cw1200_ahb_read_32(priv, CW1200_CUT_ID_ADDR, &val32); + if (ret) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: HW detection: can't read CUT ID.\n", + __func__); + goto out; + } + + switch (val32) { + case CW1200_CUT_11_ID_STR: + cw1200_dbg(CW1200_DBG_MSG, + "Cut 1.1 silicon is detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT11; + break; + default: + cw1200_dbg(CW1200_DBG_MSG, + "Cut 1.0 silicon is detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT10; + break; + } + } else if (major_revision == 2) { + cw1200_dbg(CW1200_DBG_MSG, "Cut 2.0 silicon is detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT20; + } else { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: unsupported silicon major revision %d.\n", + __func__, major_revision); + ret = -ENOTSUPP; + goto out; + } + + /* Checking for access mode */ + ret = config_reg_read(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: check_access_mode: can't read " \ + "config register.\n", __func__); + goto out; + } + + if (val32 & ST90TDS_CONFIG_ACCESS_MODE_BIT) { + switch (priv->hw_type) { + case HIF_8601_SILICON: + cw1200_dbg(CW1200_DBG_MSG, + "%s: CW1200 detected.\n", __func__); + ret = cw1200_load_firmware_cw1200(priv); + break; + case HIF_8601_VERSATILE: + /* TODO: Not implemented yet! + ret = cw1200_load_firmware_cw1100(priv); + */ + ret = -ENOTSUPP; + goto out; + case HIF_9000_SILICON_VERSTAILE: + /* TODO: Not implemented yet! + ret = cw1200_load_firmware_stlc9000(priv); + */ + ret = -ENOTSUPP; + goto out; + default: + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Unknown hardware: %d.\n", + __func__, priv->hw_type); + } + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't download firmware.\n", __func__); + goto out; + } + } else { + cw1200_dbg(CW1200_DBG_MSG, + "%s: check_access_mode: device is already " \ + "in QUEUE mode.\n", __func__); + /* TODO: verify this branch. Do we need something to do? */ + } + + /* Register Interrupt Handler */ + ret = priv->sbus_ops->irq_subscribe(priv->sbus_priv, + (sbus_irq_handler)cw1200_irq_handler, priv); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't register IRQ handler.\n", __func__); + goto out; + } + + if (HIF_8601_SILICON == priv->hw_type) { + /* If device is CW1200 the IRQ enable/disable bits + * are in CONFIG register */ + ret = config_reg_read(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: enable_irq: can't read " \ + "config register.\n", __func__); + goto unsubscribe; + } + ret = config_reg_write(priv, ST90TDS_CONFIG_REG_ID, + val32 | ST90TDS_CONF_IRQ_RDY_ENABLE); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: enable_irq: can't write " \ + "config register.\n", __func__); + goto unsubscribe; + } + } else { + /* If device is STLC9000 the IRQ enable/disable bits + * are in CONTROL register */ + /* Enable device interrupts - Both DATA_RDY and WLAN_RDY */ + ret = cw1200_reg_read_16(priv, ST90TDS_CONFIG_REG_ID, &val16); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: enable_irq: can't read " \ + "control register.\n", __func__); + goto unsubscribe; + } + ret = cw1200_reg_write_16(priv, ST90TDS_CONFIG_REG_ID, + val16 | ST90TDS_CONT_IRQ_RDY_ENABLE); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: enable_irq: can't write " \ + "control register.\n", __func__); + goto unsubscribe; + } + + } + + /* Configure device for MESSSAGE MODE */ + ret = config_reg_read(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: set_mode: can't read config register.\n", + __func__); + goto unsubscribe; + } + ret = config_reg_write(priv, ST90TDS_CONFIG_REG_ID, + val32 & ~ST90TDS_CONFIG_ACCESS_MODE_BIT); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: set_mode: can't write config register.\n", + __func__); + goto unsubscribe; + } + + /* Unless we read the CONFIG Register we are + * not able to get an interrupt */ + mdelay(10); + config_reg_read(priv, ST90TDS_CONFIG_REG_ID, &val32); + +out: + return ret; + +unsubscribe: + priv->sbus_ops->irq_unsubscribe(priv->sbus_priv); + return ret; +} + diff --git a/drivers/staging/cw1200/fwio.h b/drivers/staging/cw1200/fwio.h new file mode 100644 index 00000000000..4f5e612cf10 --- /dev/null +++ b/drivers/staging/cw1200/fwio.h @@ -0,0 +1,33 @@ +/* + * Firmware API for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on: + * ST-Ericsson UMAC CW1200 driver which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh + * + * 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. + */ + +#ifndef FWIO_H_INCLUDED +#define FWIO_H_INCLUDED + +#define FIRMWARE_CUT20 ("cw1200/wsm_20.bin") +#define FIRMWARE_CUT11 ("cw1200/wsm_11.bin") +#define FIRMWARE_CUT10 ("cw1200/wsm_10.bin") +#define SDD_FILE_20 ("cw1200/sdd_20.bin") +#define SDD_FILE_11 ("cw1200/sdd_11.bin") +#define SDD_FILE_10 ("cw1200/sdd_10.bin") + +#define CW1200_HW_REV_CUT10 (10) +#define CW1200_HW_REV_CUT11 (11) +#define CW1200_HW_REV_CUT20 (20) + +int cw1200_load_firmware(struct cw1200_common *priv); + +#endif diff --git a/drivers/staging/cw1200/ht.h b/drivers/staging/cw1200/ht.h new file mode 100644 index 00000000000..5c486a634c7 --- /dev/null +++ b/drivers/staging/cw1200/ht.h @@ -0,0 +1,43 @@ +/* + * HT-related code for ST-Ericsson CW1200 driver + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#ifndef CW1200_HT_H_INCLUDED +#define CW1200_HT_H_INCLUDED + +#include + +struct cw1200_ht_info { + struct ieee80211_sta_ht_cap ht_cap; + enum nl80211_channel_type channel_type; + u16 operation_mode; +}; + +static inline int cw1200_is_ht(const struct cw1200_ht_info *ht_info) +{ + return ht_info->channel_type != NL80211_CHAN_NO_HT; +} + +static inline int cw1200_ht_greenfield(const struct cw1200_ht_info *ht_info) +{ + return cw1200_is_ht(ht_info) && + (ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && + !(ht_info->operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); +} + +static inline int cw1200_ht_ampdu_density(const struct cw1200_ht_info *ht_info) +{ + if (!cw1200_is_ht(ht_info)) + return 0; + return ht_info->ht_cap.ampdu_density; +} + +#endif /* CW1200_HT_H_INCLUDED */ diff --git a/drivers/staging/cw1200/hwio.c b/drivers/staging/cw1200/hwio.c new file mode 100644 index 00000000000..094ce8234f0 --- /dev/null +++ b/drivers/staging/cw1200/hwio.c @@ -0,0 +1,268 @@ +/* + * Low-level device IO routines for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on: + * ST-Ericsson UMAC CW1200 driver, which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh + * + * 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. + */ + +#include + +#include "cw1200.h" +#include "hwio.h" +#include "sbus.h" + + /* Sdio addr is 4*spi_addr */ +#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2) +#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \ + ((((buf_id) & 0x1F) << 7) \ + | (((mpf) & 1) << 6) \ + | (((rfu) & 1) << 5) \ + | (((reg_id_ofs) & 0x1F) << 0)) + + +static int __cw1200_reg_read(struct cw1200_common *priv, u16 addr, + void *buf, size_t buf_len, int buf_id) +{ + u16 addr_sdio; + u32 sdio_reg_addr_17bit ; + + /* Check if buffer is aligned to 4 byte boundary */ + if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: buffer is not aligned.\n", __func__); + return -EINVAL; + } + + /* Convert to SDIO Register Address */ + addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); + sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); + + BUG_ON(!priv->sbus_ops); + return priv->sbus_ops->sbus_memcpy_fromio(priv->sbus_priv, + sdio_reg_addr_17bit, + buf, buf_len); +} + +static int __cw1200_reg_write(struct cw1200_common *priv, u16 addr, + const void *buf, size_t buf_len, int buf_id) +{ + u16 addr_sdio; + u32 sdio_reg_addr_17bit ; + +#if 0 + /* Check if buffer is aligned to 4 byte boundary */ + if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) { + cw1200_dbg(CW1200_DBG_ERROR, "%s: buffer is not aligned.\n", + __func__); + return -EINVAL; + } +#endif + + /* Convert to SDIO Register Address */ + addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); + sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); + + BUG_ON(!priv->sbus_ops); + return priv->sbus_ops->sbus_memcpy_toio(priv->sbus_priv, + sdio_reg_addr_17bit, + buf, buf_len); +} + +static inline int __cw1200_reg_read_32(struct cw1200_common *priv, + u16 addr, u32 *val) +{ + return __cw1200_reg_read(priv, addr, val, sizeof(val), 0); +} + +static inline int __cw1200_reg_write_32(struct cw1200_common *priv, + u16 addr, u32 val) +{ + return __cw1200_reg_write(priv, addr, &val, sizeof(val), 0); +} + +int cw1200_reg_read(struct cw1200_common *priv, u16 addr, void *buf, + size_t buf_len) +{ + int ret; + BUG_ON(!priv->sbus_ops); + priv->sbus_ops->lock(priv->sbus_priv); + ret = __cw1200_reg_read(priv, addr, buf, buf_len, 0); + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} + +int cw1200_reg_write(struct cw1200_common *priv, u16 addr, const void *buf, + size_t buf_len) +{ + int ret; + BUG_ON(!priv->sbus_ops); + priv->sbus_ops->lock(priv->sbus_priv); + ret = __cw1200_reg_write(priv, addr, buf, buf_len, 0); + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} + +int cw1200_data_read(struct cw1200_common *priv, void *buf, size_t buf_len) +{ + int ret; + BUG_ON(!priv->sbus_ops); + priv->sbus_ops->lock(priv->sbus_priv); + { + int buf_id_rx = priv->buf_id_rx; + ret = __cw1200_reg_read(priv, ST90TDS_IN_OUT_QUEUE_REG_ID, buf, + buf_len, buf_id_rx + 1); + if (!ret) { + buf_id_rx = (buf_id_rx + 1) & 3; + priv->buf_id_rx = buf_id_rx; + } + } + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} + +int cw1200_data_write(struct cw1200_common *priv, const void *buf, + size_t buf_len) +{ + int ret; + BUG_ON(!priv->sbus_ops); + priv->sbus_ops->lock(priv->sbus_priv); + { + int buf_id_tx = priv->buf_id_tx; + ret = __cw1200_reg_write(priv, ST90TDS_IN_OUT_QUEUE_REG_ID, buf, + buf_len, buf_id_tx); + if (!ret) { + buf_id_tx = (buf_id_tx + 1) & 31; + priv->buf_id_tx = buf_id_tx; + } + } + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} + +int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf, + size_t buf_len, u32 prefetch, u16 port_addr) +{ + u32 val32 = 0; + int i, ret; + + if ((buf_len / 2) >= 0x1000) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Can't read more than 0xfff words.\n", + __func__); + WARN_ON(1); + return -EINVAL; + goto out; + } + + priv->sbus_ops->lock(priv->sbus_priv); + /* Write address */ + ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Can't write address register.\n", + __func__); + goto out; + } + + /* Read CONFIG Register Value - We will read 32 bits */ + ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Can't read config register.\n", + __func__); + goto out; + } + + /* Set PREFETCH bit */ + ret = __cw1200_reg_write_32(priv, ST90TDS_CONFIG_REG_ID, + val32 | prefetch); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Can't write prefetch bit.\n", + __func__); + goto out; + } + + /* Check for PRE-FETCH bit to be cleared */ + for (i = 0; i < 20; i++) { + ret = __cw1200_reg_read_32(priv, ST90TDS_CONFIG_REG_ID, &val32); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Can't check prefetch bit.\n", + __func__); + goto out; + } + if (!(val32 & prefetch)) + break; + + mdelay(i); + } + + if (val32 & prefetch) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Prefetch bit is not cleared.\n", + __func__); + goto out; + } + + /* Read data port */ + ret = __cw1200_reg_read(priv, port_addr, buf, buf_len, 0); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Can't read data port.\n", + __func__); + goto out; + } + +out: + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} + +int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf, + size_t buf_len) +{ + int ret; + + if ((buf_len / 2) >= 0x1000) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Can't wrire more than 0xfff words.\n", + __func__); + WARN_ON(1); + return -EINVAL; + } + + priv->sbus_ops->lock(priv->sbus_priv); + + /* Write address */ + ret = __cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: Can't write address register.\n", + __func__); + goto out; + } + + /* Write data port */ + ret = __cw1200_reg_write(priv, ST90TDS_SRAM_DPORT_REG_ID, + buf, buf_len, 0); + if (ret < 0) { + cw1200_dbg(CW1200_DBG_ERROR, "%s: Can't write data port.\n", + __func__); + goto out; + } + +out: + priv->sbus_ops->unlock(priv->sbus_priv); + return ret; +} + diff --git a/drivers/staging/cw1200/hwio.h b/drivers/staging/cw1200/hwio.h new file mode 100644 index 00000000000..59f3ca94eb1 --- /dev/null +++ b/drivers/staging/cw1200/hwio.h @@ -0,0 +1,236 @@ +/* + * Low-level API for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on: + * ST-Ericsson UMAC CW1200 driver which is + * Copyright (c) 2010, ST-Ericsson + * Author: Ajitpal Singh + * + * 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. + */ + +#ifndef CW1200_HWIO_H_INCLUDED +#define CW1200_HWIO_H_INCLUDED + +/* extern */ struct cw1200_common; + +#define BUG_POWER_OF_TWO_BLOCKSIZE + +/* DPLL initial values */ +#define DPLL_INIT_VAL_9000 (0x00000191) +#define DPLL_INIT_VAL_CW1200 (0x0EC4F121) + +/* Hardware Type Definitions */ +#define HIF_8601_VERSATILE (0) +#define HIF_8601_SILICON (1) +#define HIF_9000_SILICON_VERSTAILE (2) + +#define CW1200_CUT_11_ID_STR (0x302E3830) +#define CW1200_CUT_ID_ADDR (0xFFF17F90) + +/* Download control area */ +/* boot loader start address in SRAM */ +#define DOWNLOAD_BOOT_LOADER_OFFSET (0x00000000) +/* 32K, 0x4000 to 0xDFFF */ +#define DOWNLOAD_FIFO_OFFSET (0x00004000) +/* 32K */ +#define DOWNLOAD_FIFO_SIZE (0x00008000) +/* 128 bytes, 0xFF80 to 0xFFFF */ +#define DOWNLOAD_CTRL_OFFSET (0x0000FF80) +#define DOWNLOAD_CTRL_DATA_DWORDS (32-6) + +struct download_cntl_t { + /* size of whole firmware file (including Cheksum), host init */ + u32 ImageSize; + /* downloading flags */ + u32 Flags; + /* No. of bytes put into the download, init & updated by host */ + u32 Put; + /* last traced program counter, last ARM reg_pc */ + u32 TracePc; + /* No. of bytes read from the download, host init, device updates */ + u32 Get; + /* r0, boot losader status, host init to pending, device updates */ + u32 Status; + /* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */ + u32 DebugData[DOWNLOAD_CTRL_DATA_DWORDS]; +}; + +#define DOWNLOAD_IMAGE_SIZE_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, ImageSize)) +#define DOWNLOAD_FLAGS_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Flags)) +#define DOWNLOAD_PUT_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Put)) +#define DOWNLOAD_TRACE_PC_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, TracePc)) +#define DOWNLOAD_GET_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Get)) +#define DOWNLOAD_STATUS_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Status)) +#define DOWNLOAD_DEBUG_DATA_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, DebugData)) +#define DOWNLOAD_DEBUG_DATA_LEN (108) + +#define DOWNLOAD_BLOCK_SIZE (1024) + +/* For boot loader detection */ +#define DOWNLOAD_ARE_YOU_HERE (0x87654321) +#define DOWNLOAD_I_AM_HERE (0x12345678) + +/* Download error code */ +#define DOWNLOAD_PENDING (0xFFFFFFFF) +#define DOWNLOAD_SUCCESS (0) +#define DOWNLOAD_EXCEPTION (1) +#define DOWNLOAD_ERR_MEM_1 (2) +#define DOWNLOAD_ERR_MEM_2 (3) +#define DOWNLOAD_ERR_SOFTWARE (4) +#define DOWNLOAD_ERR_FILE_SIZE (5) +#define DOWNLOAD_ERR_CHECKSUM (6) +#define DOWNLOAD_ERR_OVERFLOW (7) +#define DOWNLOAD_ERR_IMAGE (8) +#define DOWNLOAD_ERR_HOST (9) +#define DOWNLOAD_ERR_ABORT (10) + + +#define SYS_BASE_ADDR_SILICON (0) +#define PAC_BASE_ADDRESS_SILICON (SYS_BASE_ADDR_SILICON + 0x09000000) +#define PAC_SHARED_MEMORY_SILICON (PAC_BASE_ADDRESS_SILICON) + +#define CW12000_APB(addr) (PAC_SHARED_MEMORY_SILICON + (addr)) + +/* *************************************************************** +*Device register definitions +*************************************************************** */ +/* WBF - SPI Register Addresses */ +#define ST90TDS_ADDR_ID_BASE (0x0000) +/* 16/32 bits */ +#define ST90TDS_CONFIG_REG_ID (0x0000) +/* 16/32 bits */ +#define ST90TDS_CONTROL_REG_ID (0x0001) +/* 16 bits, Q mode W/R */ +#define ST90TDS_IN_OUT_QUEUE_REG_ID (0x0002) +/* 32 bits, AHB bus R/W */ +#define ST90TDS_AHB_DPORT_REG_ID (0x0003) +/* 16/32 bits */ +#define ST90TDS_SRAM_BASE_ADDR_REG_ID (0x0004) +/* 32 bits, APB bus R/W */ +#define ST90TDS_SRAM_DPORT_REG_ID (0x0005) +/* 32 bits, t_settle/general */ +#define ST90TDS_TSET_GEN_R_W_REG_ID (0x0006) +/* 16 bits, Q mode read, no length */ +#define ST90TDS_FRAME_OUT_REG_ID (0x0007) +#define ST90TDS_ADDR_ID_MAX (ST90TDS_FRAME_OUT_REG_ID) + +/* WBF - Control register bit set */ +/* next o/p length, bit 11 to 0 */ +#define ST90TDS_CONT_NEXT_LEN_MASK (0x0FFF) +#define ST90TDS_CONT_WUP_BIT (BIT(12)) +#define ST90TDS_CONT_RDY_BIT (BIT(13)) +#define ST90TDS_CONT_IRQ_ENABLE (BIT(14)) +#define ST90TDS_CONT_RDY_ENABLE (BIT(15)) +#define ST90TDS_CONT_IRQ_RDY_ENABLE (BIT(14)|BIT(15)) + +/* SPI Config register bit set */ +#define ST90TDS_CONFIG_FRAME_BIT (BIT(2)) +#define ST90TDS_CONFIG_WORD_MODE_BITS (BIT(3)|BIT(4)) +#define ST90TDS_CONFIG_WORD_MODE_1 (BIT(3)) +#define ST90TDS_CONFIG_WORD_MODE_2 (BIT(4)) +#define ST90TDS_CONFIG_ERROR_0_BIT (BIT(5)) +#define ST90TDS_CONFIG_ERROR_1_BIT (BIT(6)) +#define ST90TDS_CONFIG_ERROR_2_BIT (BIT(7)) +/* TBD: Sure??? */ +#define ST90TDS_CONFIG_CSN_FRAME_BIT (BIT(7)) +#define ST90TDS_CONFIG_ERROR_3_BIT (BIT(8)) +#define ST90TDS_CONFIG_ERROR_4_BIT (BIT(9)) +/* QueueM */ +#define ST90TDS_CONFIG_ACCESS_MODE_BIT (BIT(10)) +/* AHB bus */ +#define ST90TDS_CONFIG_AHB_PFETCH_BIT (BIT(11)) +#define ST90TDS_CONFIG_CPU_CLK_DIS_BIT (BIT(12)) +/* APB bus */ +#define ST90TDS_CONFIG_PFETCH_BIT (BIT(13)) +/* cpu reset */ +#define ST90TDS_CONFIG_CPU_RESET_BIT (BIT(14)) +#define ST90TDS_CONFIG_CLEAR_INT_BIT (BIT(15)) + +/* For CW1200 the IRQ Enable and Ready Bits are in CONFIG register */ +#define ST90TDS_CONF_IRQ_RDY_ENABLE (BIT(16)|BIT(17)) + +int cw1200_data_read(struct cw1200_common *priv, + void *buf, size_t buf_len); +int cw1200_data_write(struct cw1200_common *priv, + const void *buf, size_t buf_len); + +int cw1200_reg_read(struct cw1200_common *priv, u16 addr, + void *buf, size_t buf_len); +int cw1200_reg_write(struct cw1200_common *priv, u16 addr, + const void *buf, size_t buf_len); + +static inline int cw1200_reg_read_16(struct cw1200_common *priv, + u16 addr, u16 *val) +{ + return cw1200_reg_read(priv, addr, val, sizeof(val)); +} + +static inline int cw1200_reg_write_16(struct cw1200_common *priv, + u16 addr, u16 val) +{ + return cw1200_reg_write(priv, addr, &val, sizeof(val)); +} + +static inline int cw1200_reg_read_32(struct cw1200_common *priv, + u16 addr, u32 *val) +{ + return cw1200_reg_read(priv, addr, val, sizeof(val)); +} + +static inline int cw1200_reg_write_32(struct cw1200_common *priv, + u16 addr, u32 val) +{ + return cw1200_reg_write(priv, addr, &val, sizeof(val)); +} + +int cw1200_indirect_read(struct cw1200_common *priv, u32 addr, void *buf, + size_t buf_len, u32 prefetch, u16 port_addr); +int cw1200_apb_write(struct cw1200_common *priv, u32 addr, const void *buf, + size_t buf_len); + +static inline int cw1200_apb_read(struct cw1200_common *priv, u32 addr, + void *buf, size_t buf_len) +{ + return cw1200_indirect_read(priv, addr, buf, buf_len, + ST90TDS_CONFIG_PFETCH_BIT, ST90TDS_SRAM_DPORT_REG_ID); +} + +static inline int cw1200_ahb_read(struct cw1200_common *priv, u32 addr, + void *buf, size_t buf_len) +{ + return cw1200_indirect_read(priv, addr, buf, buf_len, + ST90TDS_CONFIG_AHB_PFETCH_BIT, ST90TDS_AHB_DPORT_REG_ID); +} + +static inline int cw1200_apb_read_32(struct cw1200_common *priv, + u32 addr, u32 *val) +{ + return cw1200_apb_read(priv, addr, val, sizeof(val)); +} + +static inline int cw1200_apb_write_32(struct cw1200_common *priv, + u32 addr, u32 val) +{ + return cw1200_apb_write(priv, addr, &val, sizeof(val)); +} + +static inline int cw1200_ahb_read_32(struct cw1200_common *priv, + u32 addr, u32 *val) +{ + return cw1200_ahb_read(priv, addr, val, sizeof(val)); +} + +#endif /* CW1200_HWIO_H_INCLUDED */ diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c new file mode 100644 index 00000000000..e1251b4e4e4 --- /dev/null +++ b/drivers/staging/cw1200/main.c @@ -0,0 +1,458 @@ +/* + * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on: + * Copyright (c) 2006, Michael Wu + * Copyright (c) 2007-2009, Christian Lamparter + * Copyright 2008, Johannes Berg + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * - stlc45xx driver + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "cw1200.h" +#include "txrx.h" +#include "sbus.h" +#include "fwio.h" +#include "hwio.h" +#include "bh.h" +#include "sta.h" +#include "ap.h" +#include "scan.h" + +MODULE_AUTHOR("Dmitry Tarnyagin "); +MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("cw1200_core"); + +/* Accept MAC address of the form macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 */ +static u8 cw1200_mac_template[ETH_ALEN] = {0x00, 0x80, 0xe1, 0x00, 0x00, 0x00}; +module_param_array_named(macaddr, cw1200_mac_template, byte, NULL, S_IRUGO); +MODULE_PARM_DESC(macaddr, "MAC address"); + +/* TODO: use rates and channels from the device */ +#define RATETAB_ENT(_rate, _rateid, _flags) \ + { \ + .bitrate = (_rate), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ + } + +static struct ieee80211_rate cw1200_rates[] = { + RATETAB_ENT(10, 0, 0), + RATETAB_ENT(20, 1, 0), + RATETAB_ENT(55, 2, 0), + RATETAB_ENT(110, 3, 0), + RATETAB_ENT(60, 6, 0), + RATETAB_ENT(90, 7, 0), + RATETAB_ENT(120, 8, 0), + RATETAB_ENT(180, 9, 0), + RATETAB_ENT(240, 10, 0), + RATETAB_ENT(360, 11, 0), + RATETAB_ENT(480, 12, 0), + RATETAB_ENT(540, 13, 0), + RATETAB_ENT(65, 14, 0), + RATETAB_ENT(130, 15, 0), + RATETAB_ENT(195, 16, 0), + RATETAB_ENT(260, 17, 0), + RATETAB_ENT(390, 18, 0), + RATETAB_ENT(520, 19, 0), + RATETAB_ENT(585, 20, 0), + RATETAB_ENT(650, 21, 0), +}; + +#define cw1200_a_rates (cw1200_rates + 4) +#define cw1200_a_rates_size (ARRAY_SIZE(cw1200_rates) - 4) +#define cw1200_g_rates (cw1200_rates + 0) +#define cw1200_g_rates_size (ARRAY_SIZE(cw1200_rates)) + + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel, _flags) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = 5000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel cw1200_2ghz_chantable[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +static struct ieee80211_channel cw1200_5ghz_chantable[] = { + CHAN5G(34, 0), CHAN5G(36, 0), + CHAN5G(38, 0), CHAN5G(40, 0), + CHAN5G(42, 0), CHAN5G(44, 0), + CHAN5G(46, 0), CHAN5G(48, 0), + CHAN5G(52, 0), CHAN5G(56, 0), + CHAN5G(60, 0), CHAN5G(64, 0), + CHAN5G(100, 0), CHAN5G(104, 0), + CHAN5G(108, 0), CHAN5G(112, 0), + CHAN5G(116, 0), CHAN5G(120, 0), + CHAN5G(124, 0), CHAN5G(128, 0), + CHAN5G(132, 0), CHAN5G(136, 0), + CHAN5G(140, 0), CHAN5G(149, 0), + CHAN5G(153, 0), CHAN5G(157, 0), + CHAN5G(161, 0), CHAN5G(165, 0), + CHAN5G(184, 0), CHAN5G(188, 0), + CHAN5G(192, 0), CHAN5G(196, 0), + CHAN5G(200, 0), CHAN5G(204, 0), + CHAN5G(208, 0), CHAN5G(212, 0), + CHAN5G(216, 0), +}; + +static struct ieee80211_supported_band cw1200_band_2ghz = { + .channels = cw1200_2ghz_chantable, + .n_channels = ARRAY_SIZE(cw1200_2ghz_chantable), + .bitrates = cw1200_g_rates, + .n_bitrates = cw1200_g_rates_size, + .ht_cap = { + .cap = IEEE80211_HT_CAP_SM_PS | + IEEE80211_HT_CAP_GRN_FLD | + /* HT Rx STBC: Rx support of one spatial stream */ + 0x0100 | + IEEE80211_HT_CAP_DELAY_BA | + IEEE80211_HT_CAP_MAX_AMSDU, + .ht_supported = 1, + /* TODO: It was 4K for cut 1.1 HW, if I remember + * it correctly. Needs to be verified on cut 2 HW. */ + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, + .mcs = { + .rx_mask[0] = 0xFF, + .rx_highest = __cpu_to_le16(0x41), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, +}; + +static struct ieee80211_supported_band cw1200_band_5ghz = { + .channels = cw1200_5ghz_chantable, + .n_channels = ARRAY_SIZE(cw1200_5ghz_chantable), + .bitrates = cw1200_a_rates, + .n_bitrates = cw1200_a_rates_size, + .ht_cap = { + .cap = IEEE80211_HT_CAP_SM_PS | + IEEE80211_HT_CAP_GRN_FLD | + /* HT Rx STBC: Rx support of one spatial stream */ + 0x0100 | + IEEE80211_HT_CAP_DELAY_BA | + IEEE80211_HT_CAP_MAX_AMSDU, + .ht_supported = 1, + /* TODO: It was 4K for cut 1.1 HW, if I remember + * it correctly. Needs to be verified on cut 2 HW. */ + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, + .mcs = { + .rx_mask[0] = 0xFF, + .rx_highest = __cpu_to_le16(0x41), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, +}; + +static const struct ieee80211_ops cw1200_ops = { + .start = cw1200_start, + .stop = cw1200_stop, + .add_interface = cw1200_add_interface, + .remove_interface = cw1200_remove_interface, + .tx = cw1200_tx, + .hw_scan = cw1200_hw_scan, + .set_tim = cw1200_set_tim, + .sta_notify = cw1200_sta_notify, + .sta_add = cw1200_sta_add, + .sta_remove = cw1200_sta_remove, + .set_key = cw1200_set_key, + .set_rts_threshold = cw1200_set_rts_threshold, + .config = cw1200_config, + .bss_info_changed = cw1200_bss_info_changed, + .configure_filter = cw1200_configure_filter, + .conf_tx = cw1200_conf_tx, + .get_stats = cw1200_get_stats, + /* .get_tx_stats = cw1200_get_tx_stats */ +}; + +struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) +{ + int i; + struct ieee80211_hw *hw; + struct cw1200_common *priv; + + hw = ieee80211_alloc_hw(priv_data_len, &cw1200_ops); + if (!hw) + return NULL; + + priv = hw->priv; + priv->hw = hw; + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + priv->rates = cw1200_rates; /* TODO: fetch from FW */ + + hw->flags = IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + /* IEEE80211_HW_SUPPORTS_UAPSD | */ + IEEE80211_HW_CONNECTION_MONITOR | + IEEE80211_HW_SUPPORTS_CQM_RSSI | +#ifdef USE_STE_EXTENSIONS + IEEE80211_HW_SUPPORTS_CQM_BEACON_MISS | + IEEE80211_HW_SUPPORTS_CQM_TX_FAIL | +#endif + IEEE80211_HW_BEACON_FILTER; + + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT); + + hw->channel_change_time = 1000; /* TODO: find actual value */ + /* priv->beacon_req_id = cpu_to_le32(0); */ + hw->queues = 4; + priv->noise = -94; + + hw->max_rates = 8; + hw->max_rate_tries = 15; + hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM; + + hw->sta_data_size = sizeof(struct cw1200_sta_priv); + + /* + * For now, disable PS by default because it affects + * link stability significantly. + */ + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &cw1200_band_2ghz; + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &cw1200_band_5ghz; + + hw->wiphy->max_scan_ssids = 2; + + /* TODO: You must fill in the @dev member of this structure */ + /* using SET_IEEE80211_DEV() */ + /* SET_IEEE80211_DEV(hw, &pdev->dev); */ + + SET_IEEE80211_PERM_ADDR(hw, cw1200_mac_template); + + if (hw->wiphy->perm_addr[3] == 0 && + hw->wiphy->perm_addr[4] == 0 && + hw->wiphy->perm_addr[5] == 0) { + get_random_bytes(&hw->wiphy->perm_addr[3], 3); + } + + mutex_init(&priv->wsm_cmd_mux); + mutex_init(&priv->conf_mutex); + priv->workqueue = create_singlethread_workqueue("cw1200_wq"); + sema_init(&priv->scan.lock, 1); + INIT_WORK(&priv->scan.work, cw1200_scan_work); + INIT_DELAYED_WORK(&priv->scan.probe_work, cw1200_probe_work); + INIT_DELAYED_WORK(&priv->scan.timeout, cw1200_scan_timeout); + INIT_WORK(&priv->join_work, cw1200_join_work); + INIT_DELAYED_WORK(&priv->join_timeout, cw1200_join_timeout); + INIT_WORK(&priv->unjoin_work, cw1200_unjoin_work); + INIT_WORK(&priv->wep_key_work, cw1200_wep_key_work); + INIT_WORK(&priv->tx_policy_upload_work, tx_policy_upload_work); + INIT_LIST_HEAD(&priv->event_queue); + INIT_WORK(&priv->event_handler, cw1200_event_handler); + INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work); + INIT_DELAYED_WORK(&priv->connection_loss_work, + cw1200_connection_loss_work); +#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE + INIT_DELAYED_WORK(&priv->keep_alive_work, cw1200_keep_alive_work); +#endif + INIT_WORK(&priv->tx_failure_work, cw1200_tx_failure_work); + INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work); + INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work); + INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work); + + for (i = 0; i < 4; ++i) { + if (unlikely(cw1200_queue_init(&priv->tx_queue[i], i, + 16, CW1200_LINK_ID_AFTER_DTIM + 1))) { + for (; i > 0; i--) + cw1200_queue_deinit(&priv->tx_queue[i - 1]); + ieee80211_free_hw(hw); + return NULL; + } + } + + init_waitqueue_head(&priv->channel_switch_done); + init_waitqueue_head(&priv->wsm_cmd_wq); + init_waitqueue_head(&priv->wsm_startup_done); + wsm_buf_init(&priv->wsm_cmd_buf); + tx_policy_init(priv); + + return hw; +} +EXPORT_SYMBOL_GPL(cw1200_init_common); + +int cw1200_register_common(struct ieee80211_hw *dev) +{ + /* struct cw1200_common *priv = dev->priv; */ + int err; + + err = ieee80211_register_hw(dev); + if (err) { + cw1200_dbg(CW1200_DBG_ERROR, "Cannot register device (%d).\n", + err); + return err; + } + +#ifdef CONFIG_CW1200_LEDS + err = cw1200_init_leds(priv); + if (err) + return err; +#endif /* CONFIG_CW1200_LEDS */ + + cw1200_dbg(CW1200_DBG_MSG, "is registered as '%s'\n", + wiphy_name(dev->wiphy)); + return 0; +} +EXPORT_SYMBOL_GPL(cw1200_register_common); + +void cw1200_free_common(struct ieee80211_hw *dev) +{ + /* struct cw1200_common *priv = dev->priv; */ + /* unsigned int i; */ + + ieee80211_free_hw(dev); +} +EXPORT_SYMBOL_GPL(cw1200_free_common); + +void cw1200_unregister_common(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + + priv->sbus_ops->irq_unsubscribe(priv->sbus_priv); + cw1200_unregister_bh(priv); + +#ifdef CONFIG_CW1200_LEDS + cw1200_unregister_leds(priv); +#endif /* CONFIG_CW1200_LEDS */ + + ieee80211_unregister_hw(dev); + mutex_destroy(&priv->conf_mutex); + mutex_destroy(&priv->eeprom_mutex); + + wsm_buf_deinit(&priv->wsm_cmd_buf); + + kfree(priv->scan.ie); + priv->scan.ie = NULL; + priv->scan.ie_len = 0; + destroy_workqueue(priv->workqueue); + priv->workqueue = NULL; + + if (priv->skb_cache) { + dev_kfree_skb(priv->skb_cache); + priv->skb_cache = NULL; + } +} +EXPORT_SYMBOL_GPL(cw1200_unregister_common); + +int cw1200_probe(const struct sbus_ops *sbus_ops, + struct sbus_priv *sbus, + struct device *pdev, + struct cw1200_common **pself) +{ + int err = -ENOMEM; + struct ieee80211_hw *dev; + struct cw1200_common *priv; + + dev = cw1200_init_common(sizeof(struct cw1200_common)); + if (!dev) + goto err; + + priv = dev->priv; + + priv->sbus_ops = sbus_ops; + priv->sbus_priv = sbus; + priv->pdev = pdev; + + /* WSM callbacks. */ + priv->wsm_cbc.scan_complete = cw1200_scan_complete_cb; + priv->wsm_cbc.tx_confirm = cw1200_tx_confirm_cb; + priv->wsm_cbc.rx = cw1200_rx_cb; + priv->wsm_cbc.suspend_resume = cw1200_suspend_resume; + /* priv->wsm_cbc.set_pm_complete = cw1200_set_pm_complete_cb; */ + /* priv->wsm_cbc.channel_switch = cw1200_channel_switch_cb; */ + + err = cw1200_register_bh(priv); + if (err) + goto err1; + + err = cw1200_load_firmware(priv); + if (err) + goto err2; + + if (wait_event_interruptible_timeout(priv->wsm_startup_done, + priv->wsm_caps.firmwareReady, 3*HZ) <= 0) { + /* TODO: Needs to find how to reset device */ + /* in QUEUE mode properly. */ + goto err3; + } + + err = cw1200_register_common(dev); + if (err) { + priv->sbus_ops->irq_unsubscribe(priv->sbus_priv); + goto err3; + } + + *pself = dev->priv; + return err; + +err3: + sbus_ops->reset(sbus); +err2: + cw1200_unregister_bh(priv); +err1: + cw1200_free_common(dev); +err: + return err; +} +EXPORT_SYMBOL_GPL(cw1200_probe); + +void cw1200_release(struct cw1200_common *self) +{ + cw1200_unregister_common(self->hw); + cw1200_free_common(self->hw); + return; +} +EXPORT_SYMBOL_GPL(cw1200_release); diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c new file mode 100644 index 00000000000..f21ff8f2f99 --- /dev/null +++ b/drivers/staging/cw1200/queue.c @@ -0,0 +1,409 @@ +/* + * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#include "net/mac80211.h" +#include "queue.h" +#include "wsm.h" +#include "txrx.h" +#include "cw1200.h" + +/* private */ struct cw1200_queue_item +{ + struct list_head head; + struct sk_buff *skb; + u32 packetID; + /* For safety purposes only. I do not trust device too much. + * It was observed that it indicates TX for a packet several times, + * so it's not enough to use offset or address as an uniquie ID in a + * queue. + */ + u8 generation; + u8 link_id; + u8 reserved[2]; +}; + +static inline void __cw1200_queue_lock(struct cw1200_queue *queue, + struct cw1200_common *cw1200) +{ + if (queue->tx_locked_cnt++ == 0) { + txrx_printk(KERN_DEBUG "[TX] Queue %d is locked.\n", + queue->queue_id); + ieee80211_stop_queue(cw1200->hw, queue->queue_id); + } +} + +static inline void __cw1200_queue_unlock(struct cw1200_queue *queue, + struct cw1200_common *cw1200) +{ + BUG_ON(!queue->tx_locked_cnt); + if (--queue->tx_locked_cnt == 0) { + txrx_printk(KERN_DEBUG "[TX] Queue %d is unlocked.\n", + queue->queue_id); + ieee80211_wake_queue(cw1200->hw, queue->queue_id); + } +} + +static inline void cw1200_queue_parse_id(u32 packetID, u8 *queue_generation, + u8 *queue_id, + u8 *item_generation, + u8 *item_id) +{ + *item_id = (packetID >> 0) & 0xFF; + *item_generation = (packetID >> 8) & 0xFF; + *queue_id = (packetID >> 16) & 0xFF; + *queue_generation = (packetID >> 24) & 0xFF; +} + +static inline u32 cw1200_queue_make_packet_id(u8 queue_generation, u8 queue_id, + u8 item_generation, u8 item_id) +{ + return ((u32)item_id << 0) | + ((u32)item_generation << 8) | + ((u32)queue_id << 16) | + ((u32)queue_generation << 24); +} + +int cw1200_queue_init(struct cw1200_queue *queue, u8 queue_id, + size_t capacity, size_t map_capacity) +{ + size_t i; + + memset(queue, 0, sizeof(*queue)); + queue->capacity = capacity; + queue->map_capacity = map_capacity; + queue->queue_id = queue_id; + INIT_LIST_HEAD(&queue->queue); + INIT_LIST_HEAD(&queue->pending); + INIT_LIST_HEAD(&queue->free_pool); + spin_lock_init(&queue->lock); + + queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity, + GFP_KERNEL); + if (!queue->pool) + return -ENOMEM; + + queue->link_map_cache = kzalloc(sizeof(int[map_capacity]), + GFP_KERNEL); + if (!queue->link_map_cache) { + kfree(queue->pool); + queue->pool = NULL; + return -ENOMEM; + } + + for (i = 0; i < capacity; ++i) + list_add_tail(&queue->pool[i].head, &queue->free_pool); + + return 0; +} + +int cw1200_queue_clear(struct cw1200_queue *queue) +{ + unsigned long flags; + + spin_lock_irqsave(&queue->lock, flags); + queue->generation++; + list_splice_tail_init(&queue->queue, &queue->pending); + while (!list_empty(&queue->pending)) { + struct cw1200_queue_item *item = list_first_entry( + &queue->pending, struct cw1200_queue_item, head); + WARN_ON(!item->skb); + if (likely(item->skb)) { + dev_kfree_skb_any(item->skb); + item->skb = NULL; + } + list_move_tail(&item->head, &queue->free_pool); + } + queue->num_queued = 0; + queue->num_pending = 0; + queue->num_sent = 0; + memset(queue->link_map_cache, 0, sizeof(int[queue->map_capacity])); + spin_unlock_irqrestore(&queue->lock, flags); + return 0; +} + +int cw1200_queue_deinit(struct cw1200_queue *queue) +{ + cw1200_queue_clear(queue); + INIT_LIST_HEAD(&queue->free_pool); + kfree(queue->pool); + kfree(queue->link_map_cache); + queue->pool = NULL; + queue->capacity = 0; + + return 0; +} + +size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, + u32 allowed_mask) +{ + unsigned long flags; + size_t ret; + int i, bit; + + if (!allowed_mask) + return 0; + + spin_lock_irqsave(&queue->lock, flags); + if (likely(allowed_mask == (u32) -1)) + ret = queue->num_queued - queue->num_pending; + else { + ret = 0; + for (i = 0, bit = 1; i < queue->map_capacity; ++i, bit <<= 1) { + if (allowed_mask & bit) + ret += queue->link_map_cache[i]; + } + } + spin_unlock_irqrestore(&queue->lock, flags); + return ret; +} + +int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, + struct sk_buff *skb, u8 link_id) +{ + int ret; + unsigned long flags; + struct wsm_tx *wsm; + + wsm = (struct wsm_tx *)skb_push(skb, sizeof(struct wsm_tx)); + ret = cw1200_skb_to_wsm(priv, skb, wsm); + if (ret) + return ret; + + if (link_id >= queue->map_capacity) + return -EINVAL; + + spin_lock_irqsave(&queue->lock, flags); + if (!WARN_ON(list_empty(&queue->free_pool))) { + struct cw1200_queue_item *item = list_first_entry( + &queue->free_pool, struct cw1200_queue_item, head); + BUG_ON(item->skb); + + list_move_tail(&item->head, &queue->queue); + item->skb = skb; + item->packetID = cw1200_queue_make_packet_id( + queue->generation, queue->queue_id, + item->generation, item - queue->pool); + wsm->packetID = __cpu_to_le32(item->packetID); + item->link_id = link_id; + + ++queue->num_queued; + ++queue->link_map_cache[link_id]; + + if (queue->num_queued >= queue->capacity) { + queue->overfull = true; + __cw1200_queue_lock(queue, priv); + } + } else { + ret = -ENOENT; + } + spin_unlock_irqrestore(&queue->lock, flags); + return ret; +} + +int cw1200_queue_get(struct cw1200_queue *queue, + u32 allowed_mask, + struct wsm_tx **tx, + struct ieee80211_tx_info **tx_info) +{ + int ret = -ENOENT; + unsigned long flags; + struct cw1200_queue_item *item; + + spin_lock_irqsave(&queue->lock, flags); + list_for_each_entry(item, &queue->queue, head) { + if (allowed_mask & (1 << item->link_id)) { + ret = 0; + break; + } + } + + if (!WARN_ON(ret)) { + *tx = (struct wsm_tx *)item->skb->data; + *tx_info = IEEE80211_SKB_CB(item->skb); + list_move_tail(&item->head, &queue->pending); + ++queue->num_pending; + --queue->link_map_cache[item->link_id]; + } + spin_unlock_irqrestore(&queue->lock, flags); + return ret; +} + +int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID) +{ + int ret = 0; + unsigned long flags; + u8 queue_generation, queue_id, item_generation, item_id; + struct cw1200_queue_item *item; + cw1200_queue_parse_id(packetID, &queue_generation, &queue_id, + &item_generation, &item_id); + + item = &queue->pool[item_id]; + + spin_lock_irqsave(&queue->lock, flags); + BUG_ON(queue_id != queue->queue_id); + if (unlikely(queue_generation != queue->generation)) { + ret = -ENOENT; + } else if (unlikely(item_id >= (unsigned) queue->capacity)) { + WARN_ON(1); + ret = -EINVAL; + } else if (unlikely(item->generation != item_generation)) { + WARN_ON(1); + ret = -ENOENT; + } else { + struct wsm_tx *wsm = (struct wsm_tx *)item->skb->data; + --queue->num_pending; + ++queue->link_map_cache[item->link_id]; + item->generation = ++item_generation; + item->packetID = cw1200_queue_make_packet_id( + queue_generation, queue_id, item_generation, item_id); + wsm->packetID = __cpu_to_le32(item->packetID); + list_move_tail(&item->head, &queue->queue); + } + spin_unlock_irqrestore(&queue->lock, flags); + return ret; +} + +int cw1200_queue_requeue_all(struct cw1200_queue *queue) +{ + unsigned long flags; + + spin_lock_irqsave(&queue->lock, flags); + while (!list_empty(&queue->pending)) { + struct cw1200_queue_item *item = list_first_entry( + &queue->pending, struct cw1200_queue_item, head); + struct wsm_tx *wsm = (struct wsm_tx *)item->skb->data; + + --queue->num_pending; + ++queue->link_map_cache[item->link_id]; + ++item->generation; + item->packetID = cw1200_queue_make_packet_id( + queue->generation, queue->queue_id, + item->generation, item - queue->pool); + wsm->packetID = __cpu_to_le32(item->packetID); + list_move_tail(&item->head, &queue->queue); + } + spin_unlock_irqrestore(&queue->lock, flags); + + return 0; +} + +int cw1200_queue_remove(struct cw1200_queue *queue, struct cw1200_common *priv, + u32 packetID) +{ + int ret = 0; + unsigned long flags; + u8 queue_generation, queue_id, item_generation, item_id; + struct cw1200_queue_item *item; + struct sk_buff *skb_to_free = NULL; + cw1200_queue_parse_id(packetID, &queue_generation, &queue_id, + &item_generation, &item_id); + + item = &queue->pool[item_id]; + + spin_lock_irqsave(&queue->lock, flags); + BUG_ON(queue_id != queue->queue_id); + if (unlikely(queue_generation != queue->generation)) { + ret = -ENOENT; + } else if (unlikely(item_id >= (unsigned) queue->capacity)) { + WARN_ON(1); + ret = -EINVAL; + } else if (unlikely(item->generation != item_generation)) { + WARN_ON(1); + ret = -ENOENT; + } else { + --queue->num_pending; + --queue->num_queued; + ++queue->num_sent; + ++item->generation; + skb_to_free = item->skb; + item->skb = NULL; + /* Do not use list_move_tail here, but list_move: + * try to utilize cache row. + */ + list_move(&item->head, &queue->free_pool); + + if (unlikely(queue->overfull) && + (queue->num_queued <= (queue->capacity >> 1))) { + queue->overfull = false; + __cw1200_queue_unlock(queue, priv); + } + } + spin_unlock_irqrestore(&queue->lock, flags); + + if (skb_to_free) + dev_kfree_skb_any(item->skb); + + return ret; +} + +int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, + struct sk_buff **skb) +{ + int ret = 0; + unsigned long flags; + u8 queue_generation, queue_id, item_generation, item_id; + struct cw1200_queue_item *item; + cw1200_queue_parse_id(packetID, &queue_generation, &queue_id, + &item_generation, &item_id); + + item = &queue->pool[item_id]; + + spin_lock_irqsave(&queue->lock, flags); + BUG_ON(queue_id != queue->queue_id); + if (unlikely(queue_generation != queue->generation)) { + ret = -ENOENT; + } else if (unlikely(item_id >= (unsigned) queue->capacity)) { + WARN_ON(1); + ret = -EINVAL; + } else if (unlikely(item->generation != item_generation)) { + WARN_ON(1); + ret = -ENOENT; + } else { + *skb = item->skb; + item->skb = NULL; + } + spin_unlock_irqrestore(&queue->lock, flags); + return ret; +} + +void cw1200_queue_lock(struct cw1200_queue *queue, struct cw1200_common *cw1200) +{ + unsigned long flags; + spin_lock_irqsave(&queue->lock, flags); + __cw1200_queue_lock(queue, cw1200); + spin_unlock_irqrestore(&queue->lock, flags); +} + +void cw1200_queue_unlock(struct cw1200_queue *queue, + struct cw1200_common *cw1200) +{ + unsigned long flags; + spin_lock_irqsave(&queue->lock, flags); + __cw1200_queue_unlock(queue, cw1200); + spin_unlock_irqrestore(&queue->lock, flags); +} + +/* +int cw1200_queue_get_stats(struct cw1200_queue *queue, + struct ieee80211_tx_queue_stats *stats) +{ + unsigned long flags; + + spin_lock_irqsave(&queue->lock, flags); + stats->len = queue->num_queued; + stats->limit = queue->capacity; + stats->count = queue->num_sent; + spin_unlock_irqrestore(&queue->lock, flags); + + return 0; +} +*/ + diff --git a/drivers/staging/cw1200/queue.h b/drivers/staging/cw1200/queue.h new file mode 100644 index 00000000000..a87505defba --- /dev/null +++ b/drivers/staging/cw1200/queue.h @@ -0,0 +1,72 @@ +/* + * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#ifndef CW1200_QUEUE_H_INCLUDED +#define CW1200_QUEUE_H_INCLUDED + +/* private */ struct cw1200_queue_item; + +/* extern */ struct sk_buff; +/* extern */ struct wsm_tx; +/* extern */ struct cw1200_common; +/* extern */ struct ieee80211_tx_queue_stats; + +struct cw1200_queue { + size_t capacity; + size_t num_queued; + size_t num_pending; + size_t num_sent; + struct cw1200_queue_item *pool; + struct list_head queue; + struct list_head free_pool; + struct list_head pending; + int tx_locked_cnt; + int *link_map_cache; + size_t map_capacity; + bool overfull; + spinlock_t lock; + u8 queue_id; + u8 generation; +}; + +int cw1200_queue_init(struct cw1200_queue *queue, u8 queue_id, + size_t capacity, size_t map_capacity); +int cw1200_queue_clear(struct cw1200_queue *queue); +int cw1200_queue_deinit(struct cw1200_queue *queue); + +size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, + u32 allowed_mask); +int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *cw1200, + struct sk_buff *skb, u8 link_id); +int cw1200_queue_get(struct cw1200_queue *queue, + u32 allowed_mask, + struct wsm_tx **tx, + struct ieee80211_tx_info **tx_info); +int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID); +int cw1200_queue_requeue_all(struct cw1200_queue *queue); +int cw1200_queue_remove(struct cw1200_queue *queue, struct cw1200_common *priv, + u32 packetID); +int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, + struct sk_buff **skb); +void cw1200_queue_lock(struct cw1200_queue *queue, + struct cw1200_common *cw1200); +void cw1200_queue_unlock(struct cw1200_queue *queue, + struct cw1200_common *cw1200); + +/* int cw1200_queue_get_stats(struct cw1200_queue *queue, +struct ieee80211_tx_queue_stats *stats); */ + +static inline u8 cw1200_queue_get_queue_id(u32 packetID) +{ + return (packetID >> 16) & 0xFF; +} + +#endif /* CW1200_QUEUE_H_INCLUDED */ diff --git a/drivers/staging/cw1200/sbus.h b/drivers/staging/cw1200/sbus.h new file mode 100644 index 00000000000..c31a2f30a22 --- /dev/null +++ b/drivers/staging/cw1200/sbus.h @@ -0,0 +1,37 @@ +/* + * Common sbus abstraction layer interface for cw1200 wireless driver + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#ifndef CW1200_SBUS_H +#define CW1200_SBUS_H + +/* + * sbus priv forward definition. + * Implemented and instantiated in particular modules. + */ +struct sbus_priv; + +typedef void (*sbus_irq_handler)(void *priv); + +struct sbus_ops { + int (*sbus_memcpy_fromio)(struct sbus_priv *self, unsigned int addr, + void *dst, int count); + int (*sbus_memcpy_toio)(struct sbus_priv *self, unsigned int addr, + const void *src, int count); + void (*lock)(struct sbus_priv *self); + void (*unlock)(struct sbus_priv *self); + int (*irq_subscribe)(struct sbus_priv *self, sbus_irq_handler handler, + void *priv); + int (*irq_unsubscribe)(struct sbus_priv *self); + int (*reset)(struct sbus_priv *self); + size_t (*align_size)(struct sbus_priv *self, size_t size); +}; + +#endif /* CW1200_SBUS_H */ diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c new file mode 100644 index 00000000000..71268291181 --- /dev/null +++ b/drivers/staging/cw1200/scan.c @@ -0,0 +1,374 @@ +/* + * Scan implementation for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#include +#include "cw1200.h" +#include "scan.h" + +static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan) +{ + int ret, i; + int tmo = 1000; + + for (i = 0; i < scan->numOfChannels; ++i) + tmo += scan->ch[i].maxChannelTime + 10; + + atomic_set(&priv->scan.in_progress, 1); + queue_delayed_work(priv->workqueue, &priv->scan.timeout, + tmo * HZ / 1000); + ret = wsm_scan(priv, scan); + if (unlikely(ret)) { + atomic_set(&priv->scan.in_progress, 0); + cancel_delayed_work_sync(&priv->scan.timeout); + } + return ret; +} + +int cw1200_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_scan_request *req) +{ + struct cw1200_common *priv = hw->priv; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, + }; + int i; + + if (!priv->vif) + return -EINVAL; + + if (req->n_ssids == 1 && !req->ssids[0].ssid_len) + req->n_ssids = 0; + + printk(KERN_DEBUG "[SCAN] Scan request for %d SSIDs.\n", + req->n_ssids); + + if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS) + return -EINVAL; + + if (req->ie_len != priv->scan.ie_len || + memcmp(req->ie, priv->scan.ie, req->ie_len)) { + frame.skb = ieee80211_probereq_get(hw, priv->vif, NULL, 0, + req->ie, req->ie_len); + if (!frame.skb) + return -ENOMEM; + kfree(priv->scan.ie); + priv->scan.ie = NULL; + priv->scan.ie_len = 0; + if (req->ie_len) { + priv->scan.ie = kmalloc(req->ie_len, GFP_KERNEL); + if (priv->scan.ie) { + memcpy(priv->scan.ie, req->ie, req->ie_len); + priv->scan.ie_len = req->ie_len; + } + } + } + + /* will be unlocked in cw1200_scan_work() */ + down(&priv->scan.lock); + mutex_lock(&priv->conf_mutex); + if (frame.skb) { + int ret = wsm_set_template_frame(priv, &frame); + if (ret) { + mutex_unlock(&priv->conf_mutex); + up(&priv->scan.lock); + dev_kfree_skb(frame.skb); + return ret; + } + } + + wsm_lock_tx(priv); + + if (priv->join_status == CW1200_JOIN_STATUS_STA && + priv->powersave_mode.pmMode != WSM_PSM_PS) { + struct wsm_set_pm pm = priv->powersave_mode; + pm.pmMode = WSM_PSM_PS; + WARN_ON(wsm_set_pm(priv, &pm)); + } + + BUG_ON(priv->scan.req); + priv->scan.req = req; + priv->scan.n_ssids = 0; + priv->scan.status = 0; + priv->scan.begin = &req->channels[0]; + priv->scan.end = &req->channels[req->n_channels]; + priv->scan.output_power = priv->output_power; + + for (i = 0; i < req->n_ssids; ++i) { + if (req->ssids[i].ssid_len) { + struct wsm_ssid *dst = + &priv->scan.ssids[priv->scan.n_ssids]; + BUG_ON(req->ssids[i].ssid_len > sizeof(dst->ssid)); + memcpy(&dst->ssid[0], req->ssids[i].ssid, + sizeof(dst->ssid)); + dst->length = req->ssids[i].ssid_len; + ++priv->scan.n_ssids; + } + } + + mutex_unlock(&priv->conf_mutex); + if (frame.skb) + dev_kfree_skb(frame.skb); + queue_work(priv->workqueue, &priv->scan.work); + return 0; +} + +void cw1200_scan_work(struct work_struct *work) +{ + struct cw1200_common *priv = container_of(work, struct cw1200_common, + scan.work); + struct ieee80211_channel **it; + struct wsm_scan scan = { + .scanType = WSM_SCAN_TYPE_FOREGROUND, + .scanFlags = WSM_SCAN_FLAG_SPLIT_METHOD, + }; + int i; + + mutex_lock(&priv->conf_mutex); + + if (!priv->scan.req || (priv->scan.begin == priv->scan.end)) { + if (priv->scan.output_power != priv->output_power) + WARN_ON(wsm_set_output_power(priv, + priv->output_power * 10)); + if (priv->join_status == CW1200_JOIN_STATUS_STA && + priv->powersave_mode.pmMode != WSM_PSM_PS) + WARN_ON(wsm_set_pm(priv, &priv->powersave_mode)); + + if (priv->scan.req) + printk(KERN_DEBUG "[SCAN] Scan completed.\n"); + else + printk(KERN_DEBUG "[SCAN] Scan canceled.\n"); + + priv->scan.req = NULL; + + if (priv->delayed_link_loss) { + priv->delayed_link_loss = 0; + /* Restart beacon loss timer and requeue + BSS loss work. */ + printk(KERN_DEBUG "[CQM] Requeue BSS loss in %d " \ + "beacons.\n", + priv->cqm_beacon_loss_count); + cancel_delayed_work_sync(&priv->bss_loss_work); + queue_delayed_work(priv->workqueue, + &priv->bss_loss_work, + priv->cqm_beacon_loss_count * HZ / 10); + } + + wsm_unlock_tx(priv); + mutex_unlock(&priv->conf_mutex); + ieee80211_scan_completed(priv->hw, priv->scan.status ? 1 : 0); + up(&priv->scan.lock); + return; + } else { + struct ieee80211_channel *first = *priv->scan.begin; + for (it = priv->scan.begin + 1, i = 1; + it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS; + ++it, ++i) { + if ((*it)->band != first->band) + break; + if (((*it)->flags ^ first->flags) & + IEEE80211_CHAN_PASSIVE_SCAN) + break; + if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) && + (*it)->max_power != first->max_power) + break; + } + scan.band = first->band; + /* TODO: Is it optimal? */ + scan.maxTransmitRate = WSM_TRANSMIT_RATE_1; + /* TODO: Is it optimal? */ + scan.numOfProbeRequests = + (first->flags & IEEE80211_CHAN_PASSIVE_SCAN) ? 0 : 2; + scan.numOfSSIDs = priv->scan.n_ssids; + scan.ssids = &priv->scan.ssids[0]; + scan.numOfChannels = it - priv->scan.begin; + /* TODO: Is it optimal? */ + scan.probeDelay = 100; + scan.ch = kzalloc( + sizeof(struct wsm_scan_ch[it - priv->scan.begin]), + GFP_KERNEL); + if (!scan.ch) { + priv->scan.status = -ENOMEM; + goto fail; + } + for (i = 0; i < scan.numOfChannels; ++i) { + scan.ch[i].number = priv->scan.begin[i]->hw_value; + scan.ch[i].minChannelTime = 50; + scan.ch[i].maxChannelTime = 110; + } + if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) && + priv->scan.output_power != first->max_power) { + priv->scan.output_power = first->max_power; + WARN_ON(wsm_set_output_power(priv, + priv->scan.output_power * 10)); + } + priv->scan.status = cw1200_scan_start(priv, &scan); + kfree(scan.ch); + if (WARN_ON(priv->scan.status)) + goto fail; + priv->scan.begin = it; + } + mutex_unlock(&priv->conf_mutex); + return; + +fail: + priv->scan.begin = priv->scan.end; + mutex_unlock(&priv->conf_mutex); + queue_work(priv->workqueue, &priv->scan.work); + return; +} + +static void cw1200_scan_complete(struct cw1200_common *priv) +{ + if (priv->scan.direct_probe) { + printk(KERN_DEBUG "[SCAN] Direct probe complete.\n"); + priv->scan.direct_probe = 0; + + if (priv->delayed_link_loss) { + priv->delayed_link_loss = 0; + /* Requeue BSS loss work now. Direct probe does not + * affect BSS loss subscription. */ + printk(KERN_DEBUG "[CQM] Requeue BSS loss now.\n"); + cancel_delayed_work_sync(&priv->bss_loss_work); + queue_delayed_work(priv->workqueue, + &priv->bss_loss_work, 0); + } + + up(&priv->scan.lock); + wsm_unlock_tx(priv); + } else { + queue_work(priv->workqueue, &priv->scan.work); + } +} + +void cw1200_scan_complete_cb(struct cw1200_common *priv, + struct wsm_scan_complete *arg) +{ + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { + /* STA is stopped. */ + return; + } + + if (likely(atomic_xchg(&priv->scan.in_progress, 0))) { + cancel_delayed_work_sync(&priv->scan.timeout); + cw1200_scan_complete(priv); + } +} + +void cw1200_scan_timeout(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, scan.timeout.work); + if (likely(atomic_xchg(&priv->scan.in_progress, 0))) { + cw1200_dbg(CW1200_DBG_ERROR, + "CW1200 FW: Timeout waiting for scan " \ + "complete notification.\n"); + cw1200_scan_complete(priv); + } +} + +void cw1200_probe_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, scan.probe_work.work); + struct wsm_tx *wsm = (struct wsm_tx *) + priv->scan.probe_skb->data; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, + .skb = priv->scan.probe_skb, + }; + struct wsm_ssid ssids[1] = {{ + .length = 0, + } }; + struct wsm_scan_ch ch[1] = {{ + .minChannelTime = 0, + .maxChannelTime = 10, + } }; + struct wsm_scan scan = { + .scanType = WSM_SCAN_TYPE_FOREGROUND, + .maxTransmitRate = wsm->maxTxRate, + .numOfProbeRequests = 1, + .probeDelay = 0, + .numOfChannels = 1, + .ssids = ssids, + .ch = ch, + }; + u8 *ies; + size_t ies_len; + int ret; + + printk(KERN_DEBUG "[SCAN] Direct probe work.\n"); + + if (!priv->channel) { + dev_kfree_skb(priv->scan.probe_skb); + priv->scan.probe_skb = NULL; + wsm_unlock_tx(priv); + return; + } + + if (unlikely(down_trylock(&priv->scan.lock))) { + /* Scan is already in progress. Requeue self. */ + schedule(); + queue_delayed_work(priv->workqueue, + &priv->scan.probe_work, HZ / 10); + return; + } + + scan.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; + ch[0].number = priv->channel->hw_value; + + skb_pull(frame.skb, sizeof(struct wsm_tx)); + + ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)]; + ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr); + + if (ies_len) { + u8 *ssidie = + (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len); + if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) { + u8 *nextie = &ssidie[2 + ssidie[1]]; + /* Remove SSID from the IE list. It has to be provided + * as a separate argument in cw1200_scan_start call */ + + /* Store SSID localy */ + ssids[0].length = ssidie[1]; + memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length); + scan.numOfSSIDs = 1; + + /* Remove SSID from IE list */ + ssidie[1] = 0; + memmove(&ssidie[2], nextie, &ies[ies_len] - nextie); + skb_trim(frame.skb, frame.skb->len - ssids[0].length); + } + } + + mutex_lock(&priv->conf_mutex); + ret = WARN_ON(wsm_set_template_frame(priv, &frame)); + priv->scan.direct_probe = 1; + if (!ret) { + wsm_flush_tx(priv); + ret = WARN_ON(cw1200_scan_start(priv, &scan)); + } + mutex_unlock(&priv->conf_mutex); + + /* TODO: Report TX status to ieee80211 layer */ + dev_kfree_skb(priv->scan.probe_skb); + priv->scan.probe_skb = NULL; + + if (ret) { + priv->scan.direct_probe = 0; + up(&priv->scan.lock); + wsm_unlock_tx(priv); + } + + return; +} diff --git a/drivers/staging/cw1200/scan.h b/drivers/staging/cw1200/scan.h new file mode 100644 index 00000000000..3e7f6a42d6c --- /dev/null +++ b/drivers/staging/cw1200/scan.h @@ -0,0 +1,56 @@ +/* + * Scan interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#ifndef SCAN_H_INCLUDED +#define SCAN_H_INCLUDED + +#include +#include "wsm.h" + +/* external */ struct sk_buff; +/* external */ struct cfg80211_scan_request; +/* external */ struct ieee80211_channel; +/* external */ struct ieee80211_hw; +/* external */ struct work_struct; + +struct cw1200_scan { + struct semaphore lock; + struct work_struct work; + struct delayed_work timeout; + struct cfg80211_scan_request *req; + struct ieee80211_channel **begin; + struct ieee80211_channel **end; + struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS]; + u8 *ie; + size_t ie_len; + int output_power; + int n_ssids; + int status; + atomic_t in_progress; + /* Direct probe requests workaround */ + struct delayed_work probe_work; + struct sk_buff *probe_skb; + int direct_probe; +}; + +int cw1200_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_scan_request *req); +void cw1200_scan_work(struct work_struct *work); +void cw1200_scan_timeout(struct work_struct *work); +void cw1200_scan_complete_cb(struct cw1200_common *priv, + struct wsm_scan_complete *arg); + +/* ******************************************************************** */ +/* Raw probe requests TX workaround */ +void cw1200_probe_work(struct work_struct *work); + +#endif diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c new file mode 100644 index 00000000000..3bb17b1e616 --- /dev/null +++ b/drivers/staging/cw1200/sta.c @@ -0,0 +1,1026 @@ +/* + * Mac80211 STA API for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#include +#include +#include + +#include "cw1200.h" +#include "sta.h" +#include "fwio.h" +#include "bh.h" + +#ifdef CW1200_DEBUG_ENABLE_STA_LOGS +#define sta_printk(...) printk(__VA_ARGS__) +#else +#define sta_printk(...) +#endif + + +static int cw1200_cancel_scan(struct cw1200_common *priv); + +static inline void __cw1200_free_event_queue(struct list_head *list) +{ + while (!list_empty(list)) { + struct cw1200_wsm_event *event = + list_first_entry(list, struct cw1200_wsm_event, + link); + list_del(&event->link); + kfree(event); + } +} + +/* ******************************************************************** */ +/* STA API */ + +int cw1200_start(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + int ret = 0; + + mutex_lock(&priv->conf_mutex); + + /* default ECDA */ + WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47); + WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94); + WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0); + WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0); + ret = wsm_set_edca_params(priv, &priv->edca); + if (WARN_ON(ret)) + goto out; + + memset(priv->bssid, ~0, ETH_ALEN); + memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN); + priv->mode = NL80211_IFTYPE_MONITOR; + priv->softled_state = 0; + priv->wep_default_key_id = -1; + + priv->cqm_link_loss_count = 60; + priv->cqm_beacon_loss_count = 20; + + ret = cw1200_setup_mac(priv); + if (WARN_ON(ret)) + goto out; + + /* err = cw1200_set_leds(priv); */ + +out: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +void cw1200_stop(struct ieee80211_hw *dev) +{ + struct cw1200_common *priv = dev->priv; + unsigned long flags; + LIST_HEAD(list); + int i; + + struct wsm_reset reset = { + .reset_statistics = true, + }; + + wsm_lock_tx(priv); + + while (down_trylock(&priv->scan.lock)) { + /* Scan is in progress. Force it to stop. */ + priv->scan.req = NULL; + schedule(); + } + up(&priv->scan.lock); + + mutex_lock(&priv->conf_mutex); + cw1200_free_keys(priv); + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + mutex_unlock(&priv->conf_mutex); + + cancel_delayed_work_sync(&priv->scan.probe_work); + cancel_delayed_work_sync(&priv->scan.timeout); + cancel_delayed_work_sync(&priv->join_timeout); + cancel_delayed_work_sync(&priv->bss_loss_work); + cancel_delayed_work_sync(&priv->connection_loss_work); +#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE + cancel_delayed_work_sync(&priv->keep_alive_work); +#endif + switch (priv->join_status) { + case CW1200_JOIN_STATUS_STA: + queue_work(priv->workqueue, &priv->unjoin_work); + break; + case CW1200_JOIN_STATUS_AP: + /* If you see this warning please change the code to iterate + * through the map and reset each link separately. */ + WARN_ON(priv->link_id_map); + priv->sta_asleep_mask = 0; + priv->suspend_multicast = false; + wsm_reset(priv, &reset); + wsm_unlock_tx(priv); + break; + default: + wsm_unlock_tx(priv); + } + flush_workqueue(priv->workqueue); + mutex_lock(&priv->conf_mutex); + + priv->softled_state = 0; + /* cw1200_set_leds(priv); */ + + spin_lock_irqsave(&priv->event_queue_lock, flags); + list_splice_init(&priv->event_queue, &list); + spin_unlock_irqrestore(&priv->event_queue_lock, flags); + __cw1200_free_event_queue(&list); + + priv->delayed_link_loss = 0; + + priv->link_id_map = 0; + priv->join_status = CW1200_JOIN_STATUS_MONITOR; + + /* TODO: Complete deinitialization */ + + for (i = 0; i < 4; i++) + cw1200_queue_clear(&priv->tx_queue[i]); + + /* HACK! */ + if (atomic_xchg(&priv->tx_lock, 1) != 1) + sta_printk(KERN_DEBUG "[STA] TX is force-unlocked due to stop " \ + "request.\n"); + + wsm_unlock_tx(priv); + + mutex_unlock(&priv->conf_mutex); +} + +int cw1200_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + int ret; + struct cw1200_common *priv = dev->priv; + /* __le32 auto_calibration_mode = __cpu_to_le32(1); */ + + mutex_lock(&priv->conf_mutex); + + if (priv->mode != NL80211_IFTYPE_MONITOR) { + mutex_unlock(&priv->conf_mutex); + return -EOPNOTSUPP; + } + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_AP: + priv->mode = vif->type; + break; + default: + mutex_unlock(&priv->conf_mutex); + return -EOPNOTSUPP; + } + + priv->vif = vif; + memcpy(priv->mac_addr, vif->addr, ETH_ALEN); + + ret = WARN_ON(cw1200_setup_mac(priv)); + /* Enable auto-calibration */ + /* Exception in subsequent channel switch; disabled. + WARN_ON(wsm_write_mib(priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE, + &auto_calibration_mode, sizeof(auto_calibration_mode))); + */ + + mutex_unlock(&priv->conf_mutex); + return ret; +} + +void cw1200_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct cw1200_common *priv = dev->priv; + + struct wsm_reset reset = { + .reset_statistics = true, + }; + + mutex_lock(&priv->conf_mutex); + + priv->vif = NULL; + priv->mode = NL80211_IFTYPE_MONITOR; + memset(priv->mac_addr, 0, ETH_ALEN); + memset(priv->bssid, 0, ETH_ALEN); + WARN_ON(wsm_reset(priv, &reset)); + cw1200_free_keys(priv); + cw1200_setup_mac(priv); + + mutex_unlock(&priv->conf_mutex); +} + +int cw1200_config(struct ieee80211_hw *dev, u32 changed) +{ + int ret = 0; + struct cw1200_common *priv = dev->priv; + struct ieee80211_conf *conf = &dev->conf; + + mutex_lock(&priv->conf_mutex); + /* TODO: IEEE80211_CONF_CHANGE_QOS */ + if (changed & IEEE80211_CONF_CHANGE_POWER) { + priv->output_power = conf->power_level; + sta_printk(KERN_DEBUG "[STA] TX power: %d\n", priv->output_power); + WARN_ON(wsm_set_output_power(priv, priv->output_power * 10)); + } + + if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) { + /* TODO: Not sure. Needs to be verified. */ + /* TODO: DTIM skipping */ + int dtim_interval = conf->ps_dtim_period; + int listen_interval = conf->listen_interval; + if (dtim_interval < 1) + dtim_interval = 1; + if (listen_interval < dtim_interval) + listen_interval = 0; + /* TODO: max_sleep_period is not supported + * and silently skipped. */ + sta_printk(KERN_DEBUG "[STA] DTIM %d, listen %d\n", + dtim_interval, listen_interval); + WARN_ON(wsm_set_beacon_wakeup_period(priv, + dtim_interval, listen_interval)); + } + + + if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) && + (priv->channel != conf->channel)) { + struct wsm_switch_channel channel = { + .newChannelNumber = conf->channel->hw_value + }; + cw1200_cancel_scan(priv); + sta_printk(KERN_DEBUG "[STA] Freq %d (wsm ch: %d).\n", + conf->channel->center_freq, conf->channel->hw_value); + WARN_ON(wait_event_interruptible_timeout( + priv->channel_switch_done, + !priv->channel_switch_in_progress, 3 * HZ) <= 0); + WARN_ON(wsm_switch_channel(priv, &channel)); + priv->channel = conf->channel; + } + + if (changed & IEEE80211_CONF_CHANGE_PS) { + priv->powersave_mode.pmMode = + (conf->flags & IEEE80211_CONF_PS) ? + WSM_PSM_PS : WSM_PSM_ACTIVE; + WARN_ON(wsm_set_pm(priv, &priv->powersave_mode)); + } + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + /* TBD: It looks like it's transparent + * there's a monitor interface present -- use this + * to determine for example whether to calculate + * timestamps for packets or not, do not use instead + * of filter flags! */ + } + + if (changed & IEEE80211_CONF_CHANGE_IDLE) { + struct wsm_operational_mode mode = { + /* TODO: wsm_power_mode_quiescent is more efficient, + * but it requires AI to get device on again. */ + .power_mode = (conf->flags & IEEE80211_CONF_IDLE) ? + wsm_power_mode_doze : wsm_power_mode_active + }; + WARN_ON(wsm_set_operational_mode(priv, &mode)); + } + + if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { + unsigned long flags; + sta_printk(KERN_DEBUG "[STA] Retry limits: %d (long), " \ + "%d (short).\n", + conf->long_frame_max_tx_count, + conf->short_frame_max_tx_count); + spin_lock_irqsave(&priv->tx_policy_cache.lock, flags); + priv->long_frame_max_tx_count = conf->long_frame_max_tx_count; + priv->short_frame_max_tx_count = + (conf->short_frame_max_tx_count < 0x0F) ? + conf->short_frame_max_tx_count : 0x0F; + priv->hw->max_rate_tries = priv->short_frame_max_tx_count; + spin_unlock_irqrestore(&priv->tx_policy_cache.lock, flags); + /* TBD: I think we don't need tx_policy_force_upload(). + * Outdated policies will leave cache in a normal way. */ + /* WARN_ON(tx_policy_force_upload(priv)); */ + } + mutex_unlock(&priv->conf_mutex); + return ret; +} + +void cw1200_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ +#if 0 + struct cw1200_common *priv = dev->priv; + struct wsm_rx_filter filter = { + .promiscuous = (*total_flags & FIF_PROMISC_IN_BSS) ? 1 : 0, + .bssid = (*total_flags & FIF_OTHER_BSS) ? 1 : 0, + .fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0, + }; + struct wsm_beacon_filter_control bf_control = { + .enabled = 0, + .bcn_count = (*total_flags & + (FIF_BCN_PRBRESP_PROMISC | FIF_PROMISC_IN_BSS)) ? + 1 : 0, + }; +#endif + + *total_flags &= FIF_PROMISC_IN_BSS | + FIF_OTHER_BSS | + FIF_FCSFAIL | + FIF_BCN_PRBRESP_PROMISC; + +#if 0 + /* FIXME: FW behaves strange if promiscuous mode is enabled. */ + WARN_ON(wsm_set_rx_filter(priv, &filter)); + WARN_ON(wsm_beacon_filter_control(priv, &bf_control)); +#endif +} + +int cw1200_conf_tx(struct ieee80211_hw *dev, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct cw1200_common *priv = dev->priv; + int ret = 0; + + mutex_lock(&priv->conf_mutex); + + if (queue < dev->queues) { + WSM_EDCA_SET(&priv->edca, queue, params->aifs, + params->cw_min, params->cw_max, params->txop); + ret = wsm_set_edca_params(priv, &priv->edca); + } else + ret = -EINVAL; + + mutex_unlock(&priv->conf_mutex); + return ret; +} + +int cw1200_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats) +{ + struct cw1200_common *priv = dev->priv; + + memcpy(stats, &priv->stats, sizeof(*stats)); + return 0; +} + +/* +int cw1200_get_tx_stats(struct ieee80211_hw *dev, + struct ieee80211_tx_queue_stats *stats) +{ + int i; + struct cw1200_common *priv = dev->priv; + + for (i = 0; i < dev->queues; ++i) + cw1200_queue_get_stats(&priv->tx_queue[i], &stats[i]); + + return 0; +} +*/ + +int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + int ret = -EOPNOTSUPP; + struct cw1200_common *priv = dev->priv; + + mutex_lock(&priv->conf_mutex); + + if (cmd == SET_KEY) { + u8 *peer_addr = NULL; + int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? + 1 : 0; + int idx = cw1200_alloc_key(priv); + struct wsm_add_key *wsm_key = &priv->keys[idx]; + + if (idx < 0) { + ret = -EINVAL; + goto finally; + } + + BUG_ON(pairwise && !sta); + if (sta) + peer_addr = sta->addr; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + if (key->keylen > 16) { + cw1200_free_key(priv, idx); + ret = -EINVAL; + goto finally; + } + + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE; + memcpy(wsm_key->wepPairwiseKey.peerAddress, + peer_addr, ETH_ALEN); + memcpy(wsm_key->wepPairwiseKey.keyData, + &key->key[0], key->keylen); + wsm_key->wepPairwiseKey.keyLength = key->keylen; + } else { + wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT; + memcpy(wsm_key->wepGroupKey.keyData, + &key->key[0], key->keylen); + wsm_key->wepGroupKey.keyLength = key->keylen; + wsm_key->wepGroupKey.keyId = key->keyidx; + } + break; + case WLAN_CIPHER_SUITE_TKIP: + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE; + memcpy(wsm_key->tkipPairwiseKey.peerAddress, + peer_addr, ETH_ALEN); + memcpy(wsm_key->tkipPairwiseKey.tkipKeyData, + &key->key[0], 16); + memcpy(wsm_key->tkipPairwiseKey.txMicKey, + &key->key[16], 8); + memcpy(wsm_key->tkipPairwiseKey.rxMicKey, + &key->key[24], 8); + } else { + size_t mic_offset = + (priv->mode == NL80211_IFTYPE_AP) ? + 16 : 24; + wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP; + memcpy(wsm_key->tkipGroupKey.tkipKeyData, + &key->key[0], 16); + memcpy(wsm_key->tkipGroupKey.rxMicKey, + &key->key[mic_offset], 8); + + /* TODO: Where can I find TKIP SEQ? */ + memset(wsm_key->tkipGroupKey.rxSeqCounter, + 0, 8); + wsm_key->tkipGroupKey.keyId = key->keyidx; + + print_hex_dump_bytes("TKIP: ", DUMP_PREFIX_NONE, + key->key, key->keylen); + } + break; + case WLAN_CIPHER_SUITE_CCMP: + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE; + memcpy(wsm_key->aesPairwiseKey.peerAddress, + peer_addr, ETH_ALEN); + memcpy(wsm_key->aesPairwiseKey.aesKeyData, + &key->key[0], 16); + } else { + wsm_key->type = WSM_KEY_TYPE_AES_GROUP; + memcpy(wsm_key->aesGroupKey.aesKeyData, + &key->key[0], 16); + /* TODO: Where can I find AES SEQ? */ + memset(wsm_key->aesGroupKey.rxSeqCounter, + 0, 8); + wsm_key->aesGroupKey.keyId = key->keyidx; + } + break; +#if 0 + case WLAN_CIPHER_SUITE_WAPI: + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE; + memcpy(wsm_key->wapiPairwiseKey.peerAddress, + peer_addr, ETH_ALEN); + memcpy(wsm_key->wapiPairwiseKey.wapiKeyData, + &key->key[0], 16); + memcpy(wsm_key->wapiPairwiseKey.micKeyData, + &key->key[16], 16); + wsm_key->wapiPairwiseKey.keyId = key->keyidx; + } else { + wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP; + memcpy(wsm_key->wapiGroupKey.wapiKeyData, + &key->key[0], 16); + memcpy(wsm_key->wapiGroupKey.micKeyData, + &key->key[16], 16); + wsm_key->wapiGroupKey.keyId = key->keyidx; + } + break; +#endif + default: + WARN_ON(1); + cw1200_free_key(priv, idx); + ret = -EOPNOTSUPP; + goto finally; + } + ret = WARN_ON(wsm_add_key(priv, wsm_key)); + if (!ret) + key->hw_key_idx = idx; + else + cw1200_free_key(priv, idx); + } else if (cmd == DISABLE_KEY) { + struct wsm_remove_key wsm_key = { + .entryIndex = key->hw_key_idx, + }; + + if (wsm_key.entryIndex > WSM_KEY_MAX_INDEX) { + ret = -EINVAL; + goto finally; + } + + cw1200_free_key(priv, wsm_key.entryIndex); + ret = wsm_remove_key(priv, &wsm_key); + } else { + BUG_ON("Unsupported command"); + } + +finally: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +void cw1200_wep_key_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, wep_key_work); + __le32 wep_default_key_id = __cpu_to_le32( + priv->wep_default_key_id); + + sta_printk(KERN_DEBUG "[STA] Setting default WEP key: %d\n", + priv->wep_default_key_id); + wsm_flush_tx(priv); + WARN_ON(wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID, + &wep_default_key_id, sizeof(wep_default_key_id))); + wsm_unlock_tx(priv); +} + +int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + int ret; + __le32 val32; + + if (value != (u32) -1) + val32 = __cpu_to_le32(value); + else + val32 = 0; /* disabled */ + + /* mutex_lock(&priv->conf_mutex); */ + ret = WARN_ON(wsm_write_mib(hw->priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD, + &val32, sizeof(val32))); + /* mutex_unlock(&priv->conf_mutex); */ + return ret; +} + +/* ******************************************************************** */ +/* WSM callbacks */ + +/* TODO: move to txrx.c */ +void cw1200_rx_cb(struct cw1200_common *priv, + struct wsm_rx *arg, + struct sk_buff **skb_p) +{ + struct sk_buff *skb = *skb_p; + struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); + hdr->flag = 0; + + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { + /* STA is stopped. */ + return; + } + + if (unlikely(arg->status)) { + if (arg->status == WSM_STATUS_MICFAILURE) { + sta_printk(KERN_DEBUG "[RX] MIC failure.\n"); + hdr->flag |= RX_FLAG_MMIC_ERROR; + } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) { + sta_printk(KERN_DEBUG "[RX] No key found.\n"); + return; + } else { + sta_printk(KERN_DEBUG "[RX] Receive failure: %d.\n", + arg->status); + return; + } + } + + hdr->mactime = 0; /* Not supported by WSM */ + hdr->freq = ieee80211_channel_to_frequency(arg->channelNumber); + hdr->band = (hdr->freq >= 5000) ? + IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; + hdr->rate_idx = arg->rxedRate; + if (hdr->rate_idx >= 4) /* TODO: Use common convert function. */ + hdr->rate_idx -= 2; + hdr->signal = (s8)arg->rcpiRssi; + hdr->antenna = 0; + + if (arg->flags & 0x07) + hdr->flag |= RX_FLAG_DECRYPTED; + if (arg->flags & BIT(14)) + hdr->flag |= RX_FLAG_HT; +#if 0 + /* Wrong: ACK could be disable for this ACL */ + if (arg->flags & BIT(16)) + priv->last_activity_time = jiffies; +#endif + +#if 0 + print_hex_dump_bytes("RX: ", DUMP_PREFIX_NONE, + skb->data, skb->len); +#endif + + /* Not that we really need _irqsafe variant here, + * but it offloads realtime bh thread and improve + * system performance. */ + ieee80211_rx_irqsafe(priv->hw, skb); + *skb_p = NULL; +} + +void cw1200_free_event_queue(struct cw1200_common *priv) +{ + unsigned long flags; + LIST_HEAD(list); + + spin_lock_irqsave(&priv->event_queue_lock, flags); + list_splice_init(&priv->event_queue, &list); + spin_unlock_irqrestore(&priv->event_queue_lock, flags); + + __cw1200_free_event_queue(&list); +} + +void cw1200_event_handler(struct work_struct *work) +{ + unsigned long flags; + struct cw1200_common *priv = + container_of(work, struct cw1200_common, event_handler); + struct cw1200_wsm_event *event; + LIST_HEAD(list); + + spin_lock_irqsave(&priv->event_queue_lock, flags); + list_splice_init(&priv->event_queue, &list); + spin_unlock_irqrestore(&priv->event_queue_lock, flags); + + list_for_each_entry(event, &list, link) { + switch (event->evt.eventId) { + case WSM_EVENT_ERROR: + /* I even don't know what is it about.. */ + STUB(); + break; + case WSM_EVENT_BSS_LOST: + { + sta_printk(KERN_DEBUG "[CQM] BSS lost.\n"); + cancel_delayed_work_sync(&priv->bss_loss_work); + cancel_delayed_work_sync(&priv->connection_loss_work); + if (!down_trylock(&priv->scan.lock)) { + up(&priv->scan.lock); + priv->delayed_link_loss = 0; + queue_delayed_work(priv->workqueue, + &priv->bss_loss_work, 0); + } else { + /* Scan is in progress. Delay reporting. */ + /* Scan complete will trigger bss_loss_work */ + priv->delayed_link_loss = 1; + /* Also we're starting watchdog. */ + queue_delayed_work(priv->workqueue, + &priv->bss_loss_work, 10 * HZ); + } + break; + } + case WSM_EVENT_BSS_REGAINED: + { + sta_printk(KERN_DEBUG "[CQM] BSS regained.\n"); + priv->delayed_link_loss = 0; + cancel_delayed_work_sync(&priv->bss_loss_work); + cancel_delayed_work_sync(&priv->connection_loss_work); + break; + } + case WSM_EVENT_RADAR_DETECTED: + STUB(); + break; + case WSM_EVENT_RCPI_RSSI: + { + int rssi = (int)(s8)(event->evt.eventData & 0xFF); + int cqm_evt = (rssi <= priv->cqm_rssi_thold) ? + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW : + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; + sta_printk(KERN_DEBUG "[CQM] RSSI event: %d", rssi); + ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, + GFP_KERNEL); + break; + } + case WSM_EVENT_BT_INACTIVE: + STUB(); + break; + case WSM_EVENT_BT_ACTIVE: + STUB(); + break; + } + } + __cw1200_free_event_queue(&list); +} + +void cw1200_bss_loss_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, bss_loss_work.work); + int timeout; /* in beacons */ + + timeout = priv->cqm_link_loss_count - + priv->cqm_beacon_loss_count; + + if (priv->cqm_beacon_loss_count) { + sta_printk(KERN_DEBUG "[CQM] Beacon loss.\n"); + if (timeout <= 0) + timeout = 0; +#ifdef USE_STE_EXTENSIONS + ieee80211_cqm_beacon_miss_notify(priv->vif, GFP_KERNEL); +#endif + } else { + timeout = 0; + } + + cancel_delayed_work_sync(&priv->connection_loss_work); + queue_delayed_work(priv->workqueue, + &priv->connection_loss_work, + timeout * HZ / 10); +} + +void cw1200_connection_loss_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, + connection_loss_work.work); + sta_printk(KERN_DEBUG "[CQM] Reporting connection loss.\n"); + ieee80211_connection_loss(priv->vif); +} + +#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE +void cw1200_keep_alive_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, keep_alive_work.work); + unsigned long now = jiffies; + unsigned long delta = now - priv->last_activity_time; + unsigned long tmo = 30 * HZ; + + if (delta >= tmo) { + sta_printk(KERN_DEBUG "[CQM] Keep-alive ping.\n"); + STUB(); + /* TODO: Do a keep-alive ping :) */ + priv->last_activity_time = now; + } else { + tmo -= delta; + } + queue_delayed_work(priv->workqueue, + &priv->keep_alive_work, tmo); +} +#endif + +void cw1200_tx_failure_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, tx_failure_work); + sta_printk(KERN_DEBUG "[CQM] Reporting TX failure.\n"); +#ifdef USE_STE_EXTENSIONS + ieee80211_cqm_tx_fail_notify(priv->vif, GFP_KERNEL); +#else + (void)priv; +#endif +} + +/* ******************************************************************** */ +/* Internal API */ + +int cw1200_setup_mac(struct cw1200_common *priv) +{ + /* TBD: Do you know how to assing MAC address without + * annoying uploading RX data? */ + u8 prev_mac[ETH_ALEN]; + + /* NOTE: There is a bug in FW: it reports signal + * as RSSI if RSSI subscription is enabled. + * It's not enough to set WSM_RCPI_RSSI_USE_RSSI. */ + struct wsm_rcpi_rssi_threshold threshold = { + .rssiRcpiMode = WSM_RCPI_RSSI_USE_RSSI | + WSM_RCPI_RSSI_THRESHOLD_ENABLE | + WSM_RCPI_RSSI_DONT_USE_UPPER | + WSM_RCPI_RSSI_DONT_USE_LOWER, + .rollingAverageCount = 16, + }; + int ret = 0; + + if (wsm_get_station_id(priv, &prev_mac[0]) + || memcmp(prev_mac, priv->mac_addr, ETH_ALEN)) { + const char *sdd_path = NULL; + const struct firmware *firmware = NULL; + struct wsm_configuration cfg = { + .dot11StationId = &priv->mac_addr[0], + }; + + switch (priv->hw_revision) { + case CW1200_HW_REV_CUT10: + sdd_path = SDD_FILE_10; + break; + case CW1200_HW_REV_CUT11: + sdd_path = SDD_FILE_11; + break; + case CW1200_HW_REV_CUT20: + sdd_path = SDD_FILE_20; + break; + default: + BUG_ON(1); + } + + ret = request_firmware(&firmware, + sdd_path, priv->pdev); + + if (unlikely(ret)) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't load sdd file %s.\n", + __func__, sdd_path); + return ret; + } + + cfg.dpdData = firmware->data; + cfg.dpdData_size = firmware->size; + ret = WARN_ON(wsm_configuration(priv, &cfg)); + + release_firmware(firmware); + } + if (ret) + return ret; + + /* Configure RSSI/SCPI reporting as RSSI. */ + WARN_ON(wsm_set_rcpi_rssi_threshold(priv, &threshold)); + + /* TODO: */ + switch (priv->mode) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_AP: + break; + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + /* TODO: Not verified yet. */ + STUB(); + break; + } + + return 0; +} + +void cw1200_join_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, join_work); + const struct wsm_tx *wsm = priv->join_pending_frame; + const u8 *frame = (u8 *)&wsm[1]; + const u8 *bssid = &frame[4]; /* AP SSID in a 802.11 frame */ + struct cfg80211_bss *bss; + const u8 *ssidie; + const u8 *dtimie; + const struct ieee80211_tim_ie *tim = NULL; + u8 queueId = wsm_queue_id_to_linux(wsm->queueId); + + cancel_delayed_work_sync(&priv->join_timeout); + + bss = cfg80211_get_bss(priv->hw->wiphy, NULL, bssid, NULL, 0, 0, 0); + if (!bss) { + priv->join_pending_frame = NULL; + cw1200_queue_remove(&priv->tx_queue[queueId], + priv, __le32_to_cpu(wsm->packetID)); + return; + } + ssidie = cfg80211_find_ie(WLAN_EID_SSID, + bss->information_elements, + bss->len_information_elements); + dtimie = cfg80211_find_ie(WLAN_EID_TIM, + bss->information_elements, + bss->len_information_elements); + if (dtimie) + tim = (struct ieee80211_tim_ie *)&dtimie[2]; + + mutex_lock(&priv->conf_mutex); + { + struct wsm_join join = { + .mode = (bss->capability & WLAN_CAPABILITY_IBSS) ? + WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS, + .preambleType = WSM_JOIN_PREAMBLE_SHORT, + .probeForJoin = 1, + /* dtimPeriod will be updated after association */ + .dtimPeriod = 1, + .beaconInterval = bss->beacon_interval, + /* basicRateSet will be updated after association */ + .basicRateSet = 7, + }; + + if (tim && tim->dtim_period > 1) { + join.dtimPeriod = tim->dtim_period; + priv->join_dtim_period = tim->dtim_period; + sta_printk(KERN_DEBUG "[STA] Join DTIM: %d\n", + join.dtimPeriod); + } + + priv->join_pending_frame = NULL; + BUG_ON(!wsm); + BUG_ON(!priv->channel); + + join.channelNumber = priv->channel->hw_value; + join.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; + + memcpy(&join.bssid[0], bssid, sizeof(join.bssid)); + memcpy(&priv->join_bssid[0], bssid, sizeof(priv->join_bssid)); + + if (ssidie) { + join.ssidLength = ssidie[1]; + if (WARN_ON(join.ssidLength > sizeof(join.ssid))) + join.ssidLength = sizeof(join.ssid); + memcpy(&join.ssid[0], &ssidie[2], join.ssidLength); + } + + wsm_flush_tx(priv); + + /* TX block_ack can use only 3 TX buffers, + * which is /slightly/ :) unefficient => disabled. + * RX block ACK is enabled for everything but voice. + * TODO: Verify video lags, change 0x3F -> 0x0F + * if necessary. */ + WARN_ON(wsm_set_block_ack_policy(priv, 0x00, 0x3F)); + +#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE + priv->last_activity_time = jiffies; + /* Queue keep-alive ping avery 30 sec. */ + queue_delayed_work(priv->workqueue, + &priv->keep_alive_work, 30 * HZ); +#endif + /* Queue unjoin if not associated in 3 sec. */ + queue_delayed_work(priv->workqueue, + &priv->join_timeout, 3 * HZ); + + if (wsm_join(priv, &join)) { + memset(&priv->join_bssid[0], + 0, sizeof(priv->join_bssid)); + cw1200_queue_remove(&priv->tx_queue[queueId], + priv, __le32_to_cpu(wsm->packetID)); + cancel_delayed_work_sync(&priv->join_timeout); +#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE + cancel_delayed_work_sync(&priv->keep_alive_work); +#endif + } else { + WARN_ON(cw1200_upload_keys(priv)); +#ifndef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE + WARN_ON(wsm_keep_alive_period(priv, 30 /* sec */)); +#endif + cw1200_queue_requeue(&priv->tx_queue[queueId], + __le32_to_cpu(wsm->packetID)); + } + } + mutex_unlock(&priv->conf_mutex); + cfg80211_put_bss(bss); +} + +void cw1200_join_timeout(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, join_timeout.work); + sta_printk(KERN_DEBUG "[WSM] Issue unjoin command (TMO).\n"); + wsm_lock_tx(priv); + cw1200_unjoin_work(&priv->unjoin_work); +} + +void cw1200_unjoin_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, unjoin_work); + + struct wsm_reset reset = { + .reset_statistics = true, + }; + + mutex_lock(&priv->conf_mutex); + BUG_ON(priv->join_status && + priv->join_status != CW1200_JOIN_STATUS_STA); + if (priv->join_status == CW1200_JOIN_STATUS_STA) { + memset(&priv->join_bssid[0], 0, sizeof(priv->join_bssid)); + priv->join_status = CW1200_JOIN_STATUS_MONITOR; + + /* Unjoin is a reset. */ + wsm_flush_tx(priv); + WARN_ON(wsm_reset(priv, &reset)); + priv->join_dtim_period = 0; + WARN_ON(cw1200_setup_mac(priv)); + cw1200_free_event_queue(priv); + cancel_work_sync(&priv->event_handler); + cancel_delayed_work_sync(&priv->connection_loss_work); +#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE + cancel_delayed_work_sync(&priv->keep_alive_work); +#endif + sta_printk(KERN_DEBUG "[STA] Unjoin.\n"); + } + mutex_unlock(&priv->conf_mutex); + wsm_unlock_tx(priv); +} + +/* ******************************************************************** */ +/* STA privates */ + +static int cw1200_cancel_scan(struct cw1200_common *priv) +{ + /* STUB(); */ + return 0; +} diff --git a/drivers/staging/cw1200/sta.h b/drivers/staging/cw1200/sta.h new file mode 100644 index 00000000000..a36127b3843 --- /dev/null +++ b/drivers/staging/cw1200/sta.h @@ -0,0 +1,72 @@ +/* + * Mac80211 STA interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#ifndef STA_H_INCLUDED +#define STA_H_INCLUDED + +/* ******************************************************************** */ +/* mac80211 API */ + +int cw1200_start(struct ieee80211_hw *dev); +void cw1200_stop(struct ieee80211_hw *dev); +int cw1200_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif); +void cw1200_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif); +int cw1200_config(struct ieee80211_hw *dev, u32 changed); +void cw1200_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast); +int cw1200_conf_tx(struct ieee80211_hw *dev, u16 queue, + const struct ieee80211_tx_queue_params *params); +int cw1200_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats); +/* Not more a part of interface? +int cw1200_get_tx_stats(struct ieee80211_hw *dev, + struct ieee80211_tx_queue_stats *stats); +*/ +int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); + +int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value); + +/* ******************************************************************** */ +/* WSM callbacks */ + +void cw1200_rx_cb(struct cw1200_common *priv, + struct wsm_rx *arg, + struct sk_buff **skb_p); +/* void cw1200_set_pm_complete_cb(struct cw1200_common *priv, + struct wsm_set_pm_complete *arg); */ +/* void cw1200_channel_switch_cb(struct cw1200_common *priv); */ + +/* ******************************************************************** */ +/* WSM events */ + +void cw1200_free_event_queue(struct cw1200_common *priv); +void cw1200_event_handler(struct work_struct *work); +void cw1200_bss_loss_work(struct work_struct *work); +void cw1200_connection_loss_work(struct work_struct *work); +void cw1200_keep_alive_work(struct work_struct *work); +void cw1200_tx_failure_work(struct work_struct *work); + +/* ******************************************************************** */ +/* Internal API */ + +int cw1200_setup_mac(struct cw1200_common *priv); +void cw1200_join_work(struct work_struct *work); +void cw1200_join_timeout(struct work_struct *work); +void cw1200_unjoin_work(struct work_struct *work); +void cw1200_wep_key_work(struct work_struct *work); + +#endif diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c new file mode 100644 index 00000000000..5c677556bd9 --- /dev/null +++ b/drivers/staging/cw1200/txrx.c @@ -0,0 +1,586 @@ +/* + * Datapath implementation for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#include + +#include "cw1200.h" +#include "wsm.h" +#include "bh.h" + +#ifdef CW1200_TX_POLICY_DEBUG +#define tx_policy_printk(...) printk(__VA_ARGS__) +#else +#define tx_policy_printk(...) +#endif + +/* ******************************************************************** */ +/* TX queue lock / unlock */ + +static inline void cw1200_tx_queues_lock(struct cw1200_common *priv) +{ + int i; + for (i = 0; i < 4; ++i) + cw1200_queue_lock(&priv->tx_queue[i], priv); +} + +static inline void cw1200_tx_queues_unlock(struct cw1200_common *priv) +{ + int i; + for (i = 0; i < 4; ++i) + cw1200_queue_unlock(&priv->tx_queue[i], priv); +} + +/* ******************************************************************** */ +/* TX policy cache implementation */ + +static void tx_policy_dump(struct tx_policy *policy) +{ + tx_policy_printk(KERN_DEBUG "[TX policy] " + "%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X" + "%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X" + "%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X: %d\n", + policy->raw[0] & 0x0F, policy->raw[0] >> 4, + policy->raw[1] & 0x0F, policy->raw[1] >> 4, + policy->raw[2] & 0x0F, policy->raw[2] >> 4, + policy->raw[3] & 0x0F, policy->raw[3] >> 4, + policy->raw[4] & 0x0F, policy->raw[4] >> 4, + policy->raw[5] & 0x0F, policy->raw[5] >> 4, + policy->raw[6] & 0x0F, policy->raw[6] >> 4, + policy->raw[7] & 0x0F, policy->raw[7] >> 4, + policy->raw[8] & 0x0F, policy->raw[8] >> 4, + policy->raw[9] & 0x0F, policy->raw[9] >> 4, + policy->raw[10] & 0x0F, policy->raw[10] >> 4, + policy->raw[11] & 0x0F, policy->raw[11] >> 4, + policy->defined); +} + +static void tx_policy_build(const struct cw1200_common *priv, + /* [out] */ struct tx_policy *policy, + struct ieee80211_tx_rate *rates, size_t count) +{ + int i; + const struct ieee80211_rate *rates_tbl = priv->rates; + unsigned limit = priv->short_frame_max_tx_count; + unsigned total = 0; + BUG_ON(rates[0].idx < 0); + memset(policy, 0, sizeof(*policy)); + + /* minstrel is buggy a little bit, so distille + * incoming rates first. */ + for (i = 0; i < count; ++i) { + if (rates[i].idx < 0) + break; + /* minstrel is buggy a little bit. */ + if (i && (rates[i].idx == rates[i - 1].idx)) { + rates[i - 1].count += rates[i].count; + break; + } + total += rates[i].count; + if (i && (rates[i].idx > rates[i - 1].idx)) { + struct ieee80211_tx_rate tmp = rates[i - 1]; + rates[i - 1] = rates[i]; + rates[i] = tmp; + } + } + count = i; + if (limit < count) + limit = count; + + if (total > limit) { + for (i = count - 1; i >= 0; --i) { + if (rates[i].count > limit - i) + rates[i].count = limit - i; + limit -= rates[i].count; + } + } + policy->defined = rates_tbl[rates[0].idx].hw_value + 1; + + for (i = 0; i < count; ++i) { + register unsigned rateid, off, shift, retries; + + rateid = rates_tbl[rates[i].idx].hw_value; + off = rateid >> 3; /* eq. rateid / 8 */ + shift = (rateid & 0x07) << 2; /* eq. (rateid % 8) * 4 */ + + retries = rates[i].count; + if (unlikely(retries > 0x0F)) + rates[i].count = retries = 0x0F; + policy->tbl[off] |= __cpu_to_le32(retries << shift); + policy->retry_count += retries; + } + + tx_policy_printk(KERN_DEBUG "[TX policy] Policy (%d): " \ + "%d:%d, %d:%d, %d:%d, %d:%d, %d:%d\n", + count, + rates[0].idx, rates[0].count, + rates[1].idx, rates[1].count, + rates[2].idx, rates[2].count, + rates[3].idx, rates[3].count, + rates[4].idx, rates[4].count); +} + +static inline bool tx_policy_is_equal(const struct tx_policy *wanted, + const struct tx_policy *cached) +{ + size_t count = wanted->defined >> 1; + if (wanted->defined > cached->defined) + return false; + if (count) { + if (memcmp(wanted->raw, cached->raw, count)) + return false; + } + if (wanted->defined & 1) { + if ((wanted->raw[count] & 0x0F) != (cached->raw[count] & 0x0F)) + return false; + } + return true; +} + +static int tx_policy_find(struct tx_policy_cache *cache, + const struct tx_policy *wanted) +{ + /* O(n) complexity. Not so good, but there's only 8 entries in + * the cache. + * Also lru helps to reduce search time. */ + struct tx_policy_cache_entry *it; + /* First search for policy in "used" list */ + list_for_each_entry(it, &cache->used, link) { + if (tx_policy_is_equal(wanted, &it->policy)) + return it - cache->cache; + } + /* Then - in "free list" */ + list_for_each_entry(it, &cache->free, link) { + if (tx_policy_is_equal(wanted, &it->policy)) + return it - cache->cache; + } + return -1; +} + +static inline void tx_policy_use(struct tx_policy_cache *cache, + struct tx_policy_cache_entry *entry) +{ + ++entry->policy.usage_count; + list_move(&entry->link, &cache->used); +} + +static inline int tx_policy_release(struct tx_policy_cache *cache, + struct tx_policy_cache_entry *entry) +{ + int ret = --entry->policy.usage_count; + if (!ret) + list_move(&entry->link, &cache->free); + return ret; +} + +/* ******************************************************************** */ +/* External TX policy cache API */ + +void tx_policy_init(struct cw1200_common *priv) +{ + struct tx_policy_cache *cache = &priv->tx_policy_cache; + int i; + + memset(cache, 0, sizeof(*cache)); + + spin_lock_init(&cache->lock); + INIT_LIST_HEAD(&cache->used); + INIT_LIST_HEAD(&cache->free); + + for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) + list_add(&cache->cache[i].link, &cache->free); +} + +static int tx_policy_get(struct cw1200_common *priv, + struct ieee80211_tx_rate *rates, + size_t count, bool *renew) +{ + int idx; + struct tx_policy_cache *cache = &priv->tx_policy_cache; + struct tx_policy wanted; + unsigned long flags; + + tx_policy_build(priv, &wanted, rates, count); + + spin_lock_irqsave(&cache->lock, flags); + BUG_ON(list_empty(&cache->free)); + idx = tx_policy_find(cache, &wanted); + if (idx >= 0) { + tx_policy_printk(KERN_DEBUG "[TX policy] Used TX policy: %d\n", + idx); + *renew = false; + } else { + struct tx_policy_cache_entry *entry; + *renew = true; + /* If policy is not found create a new one + * using the oldest entry in "free" list */ + entry = list_entry(cache->free.prev, + struct tx_policy_cache_entry, link); + entry->policy = wanted; + idx = entry - cache->cache; + tx_policy_printk(KERN_DEBUG "[TX policy] New TX policy: %d\n", + idx); + tx_policy_dump(&entry->policy); + } + tx_policy_use(cache, &cache->cache[idx]); + if (unlikely(list_empty(&cache->free))) { + /* Lock TX queues. */ + cw1200_tx_queues_lock(priv); + } + spin_unlock_irqrestore(&cache->lock, flags); + return idx; +} + +void tx_policy_put(struct cw1200_common *priv, int idx) +{ + int usage, locked; + struct tx_policy_cache *cache = &priv->tx_policy_cache; + unsigned long flags; + + spin_lock_irqsave(&cache->lock, flags); + locked = list_empty(&cache->free); + usage = tx_policy_release(cache, &cache->cache[idx]); + if (unlikely(locked) && !usage) { + /* Unlock TX queues. */ + cw1200_tx_queues_unlock(priv); + } + spin_unlock_irqrestore(&cache->lock, flags); +} + +/* +bool tx_policy_cache_full(struct cw1200_common *priv) +{ + bool ret; + unsigned long flags; + struct tx_policy_cache *cache = &priv->tx_policy_cache; + spin_lock_irqsave(&cache->lock, flags); + ret = list_empty(&cache->free); + spin_unlock_irqrestore(&cache->lock, flags); + return ret; +} +*/ + +static int tx_policy_upload(struct cw1200_common *priv) +{ + struct tx_policy_cache *cache = &priv->tx_policy_cache; + unsigned long flags; + int i; + struct wsm_set_tx_rate_retry_policy arg = { + .hdr = { + .numTxRatePolicies = 0, + } + }; + spin_lock_irqsave(&cache->lock, flags); + + /* Upload only modified entries. */ + for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) { + struct tx_policy *src = &cache->cache[i].policy; + if (src->retry_count && !src->uploaded) { + struct wsm_set_tx_rate_retry_policy_policy *dst = + &arg.tbl[arg.hdr.numTxRatePolicies]; + dst->policyIndex = i; + dst->shortRetryCount = priv->short_frame_max_tx_count; + dst->longRetryCount = priv->long_frame_max_tx_count; + + /* BIT(2) - Terminate retries when Tx rate retry policy + * finishes. + * BIT(3) - Count initial frame transmission as part of + * rate retry counting but not as a retry + * attempt */ + dst->policyFlags = BIT(2) | BIT(3); + + memcpy(dst->rateCountIndices, src->tbl, + sizeof(dst->rateCountIndices)); + src->uploaded = 1; + ++arg.hdr.numTxRatePolicies; + } + } + spin_unlock_irqrestore(&cache->lock, flags); + tx_policy_printk(KERN_DEBUG "[TX policy] Upload %d policies\n", + arg.hdr.numTxRatePolicies); + return wsm_set_tx_rate_retry_policy(priv, &arg); +} + +void tx_policy_upload_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, tx_policy_upload_work); + + tx_policy_printk(KERN_DEBUG "[TX] TX policy upload.\n"); + WARN_ON(tx_policy_upload(priv)); + + wsm_unlock_tx(priv); + cw1200_tx_queues_unlock(priv); +} + +/* ******************************************************************** */ +/* cw1200 TX implementation */ + +u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates) +{ + u32 ret = 0; + int i; + for (i = 0; i < 32; ++i) { + if (rates & (1 << i)) + ret |= 1 << priv->rates[i].hw_value; + } + return ret; +} + +/* NOTE: cw1200_skb_to_wsm executes in atomic context. */ +int cw1200_skb_to_wsm(struct cw1200_common *priv, struct sk_buff *skb, + struct wsm_tx *wsm) +{ + bool tx_policy_renew = false; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_rate *rate = ieee80211_get_tx_rate(priv->hw, tx_info); + + memset(wsm, 0, sizeof(*wsm)); + wsm->hdr.len = __cpu_to_le16(skb->len); + wsm->hdr.id = __cpu_to_le16(0x0004); + if (rate) { + wsm->maxTxRate = rate->hw_value; + if (rate->flags & IEEE80211_TX_RC_MCS) { + if (cw1200_ht_greenfield(&priv->ht_info)) + wsm->htTxParameters |= + __cpu_to_le32(WSM_HT_TX_GREENFIELD); + else + wsm->htTxParameters |= + __cpu_to_le32(WSM_HT_TX_MIXED); + } + } + wsm->flags = tx_policy_get(priv, + tx_info->control.rates, IEEE80211_TX_MAX_RATES, + &tx_policy_renew) << 4; + + if (tx_policy_renew) { + tx_policy_printk(KERN_DEBUG "[TX] TX policy renew.\n"); + /* It's not so optimal to stop TX queues every now and then. + * Maybe it's better to reimplement task scheduling with + * a counter. */ + /* cw1200_tx_queues_lock(priv); */ + /* Definetly better. TODO. */ + wsm_lock_tx_async(priv); + cw1200_tx_queues_lock(priv); + queue_work(priv->workqueue, &priv->tx_policy_upload_work); + } + + wsm->queueId = wsm_queue_id_to_wsm(skb_get_queue_mapping(skb)); + return 0; +} + +/* ******************************************************************** */ + +int cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct cw1200_common *priv = dev->priv; + unsigned queue = skb_get_queue_mapping(skb); + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *)skb->data; + struct cw1200_sta_priv *sta_priv = + (struct cw1200_sta_priv *)&tx_info->control.sta->drv_priv; + int link_id = 0; + int ret; + + if (tx_info->flags | IEEE80211_TX_CTL_SEND_AFTER_DTIM) + link_id = CW1200_LINK_ID_AFTER_DTIM; + else if (tx_info->control.sta) + link_id = sta_priv->link_id; + + txrx_printk(KERN_DEBUG "[TX] TX %d bytes (queue: %d, link_id: %d).\n", + skb->len, queue, link_id); + + if (WARN_ON(queue >= 4)) + goto err; + +#if 0 + { + /* HACK!!! + * Workarounnd against a bug in WSM_A21.05.0288 firmware. + * In AP mode FW calculates FCS incorrectly when DA + * is FF:FF:FF:FF:FF:FF. Just for verification, + * do not enable this code in the real live. */ + static const u8 mac_ff[] = + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + static const u8 mac_mc[] = + {0x01, 0x00, 0x5e, 0x00, 0x00, 0x16}; + if (!memcmp(&skb->data[4], mac_ff, sizeof(mac_ff))) + memcpy(&skb->data[4], mac_mc, sizeof(mac_mc)); + } +#endif + + + /* IV/ICV injection. */ + /* TODO: Quite unoptimal. It's better co modify mac80211 + * to reserve space for IV */ + if (tx_info->control.hw_key && + (hdr->frame_control & + __cpu_to_le32(IEEE80211_FCTL_PROTECTED))) { + size_t hdrlen = ieee80211_hdrlen(hdr->frame_control); + size_t iv_len = tx_info->control.hw_key->iv_len; + size_t icv_len = tx_info->control.hw_key->icv_len; + u8 *icv; + u8 *newhdr; + + if (tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { + icv_len += 8; /* MIC */ + } + + if (WARN_ON(skb_headroom(skb) < iv_len + WSM_TX_EXTRA_HEADROOM + || skb_tailroom(skb) < icv_len)) { + printk(KERN_ERR "Bug: no space allocated " + "for crypto headers.\n" + "headroom: %d, tailroom: %d, " + "req_headroom: %d, req_tailroom: %d\n" + "Please fix it in cw1200_get_skb().\n", + skb_headroom(skb), skb_tailroom(skb), + iv_len + WSM_TX_EXTRA_HEADROOM, icv_len); + goto err; + } + + newhdr = skb_push(skb, iv_len); + memmove(newhdr, newhdr + iv_len, hdrlen); + memset(&newhdr[hdrlen], 0, iv_len); + icv = skb_put(skb, icv_len); + memset(icv, 0, icv_len); + } + + ret = cw1200_queue_put(&priv->tx_queue[queue], priv, skb, + link_id); + if (!WARN_ON(ret)) + cw1200_bh_wakeup(priv); + else + goto err; + + return NETDEV_TX_OK; + +err: + /* TODO: Update TX failure counters */ + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +/* ******************************************************************** */ + +void cw1200_tx_confirm_cb(struct cw1200_common *priv, + struct wsm_tx_confirm *arg) +{ + u8 queue_id = cw1200_queue_get_queue_id(arg->packetID); + struct cw1200_queue *queue = &priv->tx_queue[queue_id]; + struct sk_buff *skb; + + txrx_printk(KERN_DEBUG "[TX] TX confirm.\n"); + + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { + /* STA is stopped. */ + return; + } + + if (WARN_ON(queue_id >= 4)) + return; + + if ((arg->status == WSM_REQUEUE) && + (arg->flags & WSM_TX_STATUS_REQUEUE)) { + WARN_ON(cw1200_queue_requeue(queue, arg->packetID)); + } else if (!WARN_ON(cw1200_queue_get_skb(queue, arg->packetID, &skb))) { + struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb); + struct wsm_tx *wsm_tx = (struct wsm_tx *)skb->data; + int rate_id = (wsm_tx->flags >> 4) & 0x07; + int tx_count = arg->ackFailures; + u8 ht_flags = 0; + int i; + + if (cw1200_ht_greenfield(&priv->ht_info)) + ht_flags |= IEEE80211_TX_RC_GREEN_FIELD; + + /* Release used TX rate policy */ + tx_policy_put(priv, rate_id); + + if (likely(!arg->status)) { + tx->flags |= IEEE80211_TX_STAT_ACK; +#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE + priv->last_activity_time = jiffies; +#endif + priv->cqm_tx_failure_count = 0; + ++tx_count; + } else { + /* TODO: Update TX failure counters */ + if (unlikely(priv->cqm_tx_failure_thold && + (++priv->cqm_tx_failure_count > + priv->cqm_tx_failure_thold))) { + priv->cqm_tx_failure_thold = 0; + queue_work(priv->workqueue, + &priv->tx_failure_work); + } + } + + for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) { + if (tx->status.rates[i].count >= tx_count) { + tx->status.rates[i].count = tx_count; + break; + } + tx_count -= tx->status.rates[i].count; + if (tx->status.rates[i].flags & IEEE80211_TX_RC_MCS) + tx->status.rates[i].flags |= ht_flags; + } + + for (++i; i < IEEE80211_TX_MAX_RATES; ++i) { + tx->status.rates[i].count = 0; + tx->status.rates[i].idx = -1; + } + + skb_pull(skb, sizeof(struct wsm_tx)); + ieee80211_tx_status(priv->hw, skb); + + WARN_ON(cw1200_queue_remove(queue, priv, arg->packetID)); + } +} + +/* ******************************************************************** */ +/* Security */ + +int cw1200_alloc_key(struct cw1200_common *priv) +{ + int idx; + + idx = ffs(~priv->key_map) - 1; + if (idx < 0 || idx > WSM_KEY_MAX_INDEX) + return -1; + + priv->key_map |= 1 << idx; + priv->keys[idx].entryIndex = idx; + return idx; +} + +void cw1200_free_key(struct cw1200_common *priv, int idx) +{ + BUG_ON(!(priv->key_map & (1 << idx))); + memset(&priv->keys[idx], 0, sizeof(priv->keys[idx])); + priv->key_map &= ~(1 << idx); +} + +void cw1200_free_keys(struct cw1200_common *priv) +{ + memset(&priv->keys, 0, sizeof(priv->keys)); + priv->key_map = 0; +} + +int cw1200_upload_keys(struct cw1200_common *priv) +{ + int idx, ret = 0; + for (idx = 0; idx <= WSM_KEY_MAX_INDEX; ++idx) + if (priv->key_map & (1 << idx)) { + ret = wsm_add_key(priv, &priv->keys[idx]); + if (ret < 0) + break; + } + return ret; +} diff --git a/drivers/staging/cw1200/txrx.h b/drivers/staging/cw1200/txrx.h new file mode 100644 index 00000000000..2be3af99d03 --- /dev/null +++ b/drivers/staging/cw1200/txrx.h @@ -0,0 +1,84 @@ +/* + * Datapath interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#ifndef CW1200_TXRX_H +#define CW1200_TXRX_H + +#include + +/* extern */ struct ieee80211_hw; +/* extern */ struct sk_buff; +/* extern */ struct wsm_tx; +/* extern */ struct wsm_tx_confirm; + +struct tx_policy { + union { + __le32 tbl[3]; + u8 raw[12]; + }; + u8 defined; /* TODO: u32 or u8, profile and select best */ + u8 usage_count; /* --// -- */ + u8 retry_count; /* --// -- */ + u8 uploaded; +}; + +struct tx_policy_cache_entry { + struct tx_policy policy; + struct list_head link; +}; + +#define TX_POLICY_CACHE_SIZE (8) +struct tx_policy_cache { + struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE]; + struct list_head used; + struct list_head free; + spinlock_t lock; +}; + +/* ******************************************************************** */ +/* TX policy cache */ +/* Intention of TX policy cache is an overcomplicated WSM API. + * Device does not accept per-PDU tx retry sequence. + * It uses "tx retry policy id" instead, so driver code has to sync + * linux tx retry sequences with a retry policy table in the device. + */ +void tx_policy_init(struct cw1200_common *priv); +void tx_policy_upload_work(struct work_struct *work); +void tx_policy_put(struct cw1200_common *priv, int idx); + +/* ******************************************************************** */ +/* TX implementation */ + +u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, + u32 rates); +int cw1200_skb_to_wsm(struct cw1200_common *priv, + struct sk_buff *skb, struct wsm_tx *wsm); +int cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb); + +/* ******************************************************************** */ +/* WSM callbacks */ + +void cw1200_tx_confirm_cb(struct cw1200_common *priv, + struct wsm_tx_confirm *arg); + +/* ******************************************************************** */ +/* Timeout */ + +void cw1200_tx_timeout(struct work_struct *work); + +/* ******************************************************************** */ +/* Security */ +int cw1200_alloc_key(struct cw1200_common *priv); +void cw1200_free_key(struct cw1200_common *priv, int idx); +void cw1200_free_keys(struct cw1200_common *priv); +int cw1200_upload_keys(struct cw1200_common *priv); + +#endif /* CW1200_TXRX_H */ diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c new file mode 100644 index 00000000000..37723cc88a1 --- /dev/null +++ b/drivers/staging/cw1200/wsm.c @@ -0,0 +1,1530 @@ +/* + * WSM host interface (HI) implementation for + * ST-Ericsson CW1200 mac80211 drivers. + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include "cw1200.h" +#include "wsm.h" +#include "bh.h" + +#ifdef CW1200_DEBUG_ENABLE_WSM_LOGS +#define wsm_printk(...) printk(__VA_ARGS__) +#else +#define wsm_printk(...) +#endif + +#define WSM_CMD_TIMEOUT (1 * HZ) +#define WSM_CMD_JOIN_TIMEOUT (7 * HZ) /* Join timeout is 5 sec. in FW */ +#define WSM_CMD_START_TIMEOUT (7 * HZ) +#define WSM_TX_TIMEOUT (1 * HZ) +#define WSM_CMD_LAST_CHANCE_TIMEOUT (10 * HZ) + +#define WSM_SKIP(buf, size) \ + do { \ + if (unlikely(buf->data + size > buf->end)) \ + goto underflow; \ + buf->data += size; \ + } while (0) + +#define WSM_GET(buf, ptr, size) \ + do { \ + if (unlikely(buf->data + size > buf->end)) \ + goto underflow; \ + memcpy(ptr, buf->data, size); \ + buf->data += size; \ + } while (0) + +#define __WSM_GET(buf, type, cvt) \ + ({ \ + type val; \ + if (unlikely(buf->data + sizeof(type) > buf->end)) \ + goto underflow; \ + val = cvt(*(type *)buf->data); \ + buf->data += sizeof(type); \ + val; \ + }) + +#define WSM_GET8(buf) __WSM_GET(buf, u8, (u8)) +#define WSM_GET16(buf) __WSM_GET(buf, u16, __le16_to_cpu) +#define WSM_GET32(buf) __WSM_GET(buf, u32, __le32_to_cpu) + +#define WSM_PUT(buf, ptr, size) \ + do { \ + if (unlikely(buf->data + size > buf->end)) \ + if (unlikely(wsm_buf_reserve(buf, size))) \ + goto nomem; \ + memcpy(buf->data, ptr, size); \ + buf->data += size; \ + } while (0) + +#define __WSM_PUT(buf, val, type, cvt) \ + do { \ + if (unlikely(buf->data + sizeof(type) > buf->end)) \ + if (unlikely(wsm_buf_reserve(buf, sizeof(type)))) \ + goto nomem; \ + *(type *)buf->data = cvt(val); \ + buf->data += sizeof(type); \ + } while (0) + +#define WSM_PUT8(buf, val) __WSM_PUT(buf, val, u8, (u8)) +#define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __cpu_to_le16) +#define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __cpu_to_le32) + +static void wsm_buf_reset(struct wsm_buf *buf); +static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size); + +static int wsm_cmd_send(struct cw1200_common *priv, + struct wsm_buf *buf, + void *arg, u16 cmd, long tmo); + +static inline void wsm_cmd_lock(struct cw1200_common *priv) +{ + mutex_lock(&priv->wsm_cmd_mux); +} + +static inline void wsm_cmd_unlock(struct cw1200_common *priv) +{ + mutex_unlock(&priv->wsm_cmd_mux); +} + +/* ******************************************************************** */ +/* WSM API implementation */ + +static int wsm_generic_confirm(struct cw1200_common *priv, + void *arg, + struct wsm_buf *buf) +{ + u32 status = WSM_GET32(buf); + if (status != WSM_STATUS_SUCCESS) + return -EINVAL; + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +int wsm_configuration(struct cw1200_common *priv, struct wsm_configuration *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime); + WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime); + WSM_PUT32(buf, arg->dot11RtsThreshold); + + /* DPD block. */ + WSM_PUT16(buf, arg->dpdData_size + 12); + WSM_PUT16(buf, 1); /* DPD version */ + WSM_PUT(buf, arg->dot11StationId, ETH_ALEN); + WSM_PUT16(buf, 5); /* DPD flags */ + WSM_PUT(buf, arg->dpdData, arg->dpdData_size); + + ret = wsm_cmd_send(priv, buf, arg, 0x0009, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +static int wsm_configuration_confirm(struct cw1200_common *priv, + struct wsm_configuration *arg, + struct wsm_buf *buf) +{ + int i; + int status; + + status = WSM_GET32(buf); + if (WARN_ON(status != WSM_STATUS_SUCCESS)) + return -EINVAL; + + WSM_GET(buf, arg->dot11StationId, ETH_ALEN); + arg->dot11FrequencyBandsSupported = WSM_GET8(buf); + WSM_SKIP(buf, 1); + arg->supportedRateMask = WSM_GET32(buf); + for (i = 0; i < 2; ++i) { + arg->txPowerRange[i].min_power_level = WSM_GET32(buf); + arg->txPowerRange[i].max_power_level = WSM_GET32(buf); + arg->txPowerRange[i].stepping = WSM_GET32(buf); + } + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + u16 cmd = 0x000A | ((arg->link_id & 0x0F) << 6); + + wsm_cmd_lock(priv); + + WSM_PUT32(buf, arg->reset_statistics ? 0 : 1); + ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +struct wsm_mib { + u16 mibId; + void *buf; + size_t buf_size; +}; + +int wsm_read_mib(struct cw1200_common *priv, u16 mibId, void *_buf, + size_t buf_size) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + struct wsm_mib mib_buf = { + .mibId = mibId, + .buf = _buf, + .buf_size = buf_size, + }; + wsm_cmd_lock(priv); + + WSM_PUT16(buf, mibId); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, &mib_buf, 0x0005, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +static int wsm_read_mib_confirm(struct cw1200_common *priv, + struct wsm_mib *arg, + struct wsm_buf *buf) +{ + u16 size; + if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS)) + return -EINVAL; + + if (WARN_ON(WSM_GET16(buf) != arg->mibId)) + return -EINVAL; + + size = WSM_GET16(buf); + if (size > arg->buf_size) + size = arg->buf_size; + + WSM_GET(buf, arg->buf, size); + arg->buf_size = size; + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +int wsm_write_mib(struct cw1200_common *priv, u16 mibId, void *_buf, + size_t buf_size) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + wsm_cmd_lock(priv); + + WSM_PUT16(buf, mibId); + WSM_PUT16(buf, buf_size); + WSM_PUT(buf, _buf, buf_size); + + ret = wsm_cmd_send(priv, buf, NULL, 0x0006, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg) +{ + int i; + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + if (unlikely(arg->numOfChannels > 48)) + return -EINVAL; + + if (unlikely(arg->numOfSSIDs > 2)) + return -EINVAL; + + if (unlikely(arg->band > 1)) + return -EINVAL; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->band); + WSM_PUT8(buf, arg->scanType); + WSM_PUT8(buf, arg->scanFlags); + WSM_PUT8(buf, arg->maxTransmitRate); + WSM_PUT32(buf, arg->autoScanInterval); + WSM_PUT8(buf, arg->numOfProbeRequests); + WSM_PUT8(buf, arg->numOfChannels); + WSM_PUT8(buf, arg->numOfSSIDs); + WSM_PUT8(buf, arg->probeDelay); + + for (i = 0; i < arg->numOfChannels; ++i) { + WSM_PUT16(buf, arg->ch[i].number); + WSM_PUT16(buf, 0); + WSM_PUT32(buf, arg->ch[i].minChannelTime); + WSM_PUT32(buf, arg->ch[i].maxChannelTime); + WSM_PUT32(buf, 0); + } + + for (i = 0; i < arg->numOfSSIDs; ++i) { + WSM_PUT32(buf, arg->ssids[i].length); + WSM_PUT(buf, &arg->ssids[i].ssid[0], + sizeof(arg->ssids[i].ssid)); + } + + ret = wsm_cmd_send(priv, buf, NULL, 0x0007, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_stop_scan(struct cw1200_common *priv) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + wsm_cmd_lock(priv); + ret = wsm_cmd_send(priv, buf, NULL, 0x0008, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; +} + + +static int wsm_tx_confirm(struct cw1200_common *priv, struct wsm_buf *buf) +{ + if (priv->wsm_cbc.tx_confirm) { + struct wsm_tx_confirm tx_confirm; + + tx_confirm.packetID = WSM_GET32(buf); + tx_confirm.status = WSM_GET32(buf); + tx_confirm.txedRate = WSM_GET8(buf); + tx_confirm.ackFailures = WSM_GET8(buf); + tx_confirm.flags = WSM_GET16(buf); + tx_confirm.mediaDelay = WSM_GET32(buf); + tx_confirm.txQueueDelay = WSM_GET32(buf); + priv->wsm_cbc.tx_confirm(priv, &tx_confirm); + } + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +static int wsm_join_confirm(struct cw1200_common *priv, + struct wsm_join *arg, + struct wsm_buf *buf) +{ + if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS)) { + priv->join_status = CW1200_JOIN_STATUS_MONITOR; + wsm_unlock_tx(priv); + return -EINVAL; + } + + arg->minPowerLevel = WSM_GET32(buf); + arg->maxPowerLevel = WSM_GET32(buf); + + priv->join_status = CW1200_JOIN_STATUS_STA; + wsm_unlock_tx(priv); + return 0; + +underflow: + WARN_ON(1); + priv->join_status = CW1200_JOIN_STATUS_MONITOR; + wsm_unlock_tx(priv); + return -EINVAL; +} + +int wsm_join(struct cw1200_common *priv, struct wsm_join *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->band); + WSM_PUT16(buf, arg->channelNumber); + WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid)); + WSM_PUT16(buf, arg->atimWindow); + WSM_PUT8(buf, arg->preambleType); + WSM_PUT8(buf, arg->probeForJoin); + WSM_PUT8(buf, arg->dtimPeriod); + WSM_PUT8(buf, arg->flags); + WSM_PUT32(buf, arg->ssidLength); + WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid)); + WSM_PUT32(buf, arg->beaconInterval); + WSM_PUT32(buf, arg->basicRateSet); + + ret = wsm_cmd_send(priv, buf, arg, 0x000B, WSM_CMD_JOIN_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_bss_params(struct cw1200_common *priv, + const struct wsm_set_bss_params *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, 0); + WSM_PUT8(buf, arg->beaconLostCount); + WSM_PUT16(buf, arg->aid); + WSM_PUT32(buf, arg->operationalRateSet); + + ret = wsm_cmd_send(priv, buf, NULL, 0x0011, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT(buf, arg, sizeof(*arg)); + + ret = wsm_cmd_send(priv, buf, NULL, 0x000C, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_remove_key(struct cw1200_common *priv, const struct wsm_remove_key *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->entryIndex); + WSM_PUT8(buf, 0); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, NULL, 0x000D, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_tx_queue_params(struct cw1200_common *priv, + const struct wsm_set_tx_queue_params *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + /* TODO: verify me. */ + WSM_PUT8(buf, arg->queueId); + WSM_PUT8(buf, 0); + WSM_PUT8(buf, arg->ackPolicy); + WSM_PUT8(buf, 0); + WSM_PUT32(buf, arg->maxTransmitLifetime); + WSM_PUT16(buf, arg->allowedMediumTime); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_edca_params(struct cw1200_common *priv, + const struct wsm_edca_params *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + /* TODO: verify me. */ + /* Implemented according to specification. Note that there is a + * mismatch in BK and BE mapping. */ + + WSM_PUT16(buf, arg->params[1].cwMin); + WSM_PUT16(buf, arg->params[0].cwMin); + WSM_PUT16(buf, arg->params[2].cwMin); + WSM_PUT16(buf, arg->params[3].cwMin); + + WSM_PUT16(buf, arg->params[1].cwMax); + WSM_PUT16(buf, arg->params[0].cwMax); + WSM_PUT16(buf, arg->params[2].cwMax); + WSM_PUT16(buf, arg->params[3].cwMax); + + WSM_PUT8(buf, arg->params[1].aifns); + WSM_PUT8(buf, arg->params[0].aifns); + WSM_PUT8(buf, arg->params[2].aifns); + WSM_PUT8(buf, arg->params[3].aifns); + + WSM_PUT16(buf, arg->params[1].txOpLimit); + WSM_PUT16(buf, arg->params[0].txOpLimit); + WSM_PUT16(buf, arg->params[2].txOpLimit); + WSM_PUT16(buf, arg->params[3].txOpLimit); + + WSM_PUT32(buf, arg->params[1].maxReceiveLifetime); + WSM_PUT32(buf, arg->params[0].maxReceiveLifetime); + WSM_PUT32(buf, arg->params[2].maxReceiveLifetime); + WSM_PUT32(buf, arg->params[3].maxReceiveLifetime); + + ret = wsm_cmd_send(priv, buf, NULL, 0x0013, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_switch_channel(struct cw1200_common *priv, + const struct wsm_switch_channel *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_lock_tx(priv); + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->channelMode); + WSM_PUT8(buf, arg->channelSwitchCount); + WSM_PUT16(buf, arg->newChannelNumber); + + priv->channel_switch_in_progress = 1; + + ret = wsm_cmd_send(priv, buf, NULL, 0x0016, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + if (ret) { + wsm_unlock_tx(priv); + priv->channel_switch_in_progress = 0; + } + return ret; + +nomem: + wsm_cmd_unlock(priv); + wsm_unlock_tx(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT32(buf, arg->pmMode); + + ret = wsm_cmd_send(priv, buf, NULL, 0x0010, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->band); + WSM_PUT16(buf, arg->channelNumber); + WSM_PUT32(buf, arg->CTWindow); + WSM_PUT32(buf, arg->beaconInterval); + WSM_PUT8(buf, arg->DTIMPeriod); + WSM_PUT8(buf, arg->preambleType); + WSM_PUT8(buf, arg->probeDelay); + WSM_PUT8(buf, arg->ssidLength); + WSM_PUT(buf, arg->ssid, sizeof(arg->ssid)); + WSM_PUT32(buf, arg->basicRateSet); + + ret = wsm_cmd_send(priv, buf, NULL, 0x0017, WSM_CMD_START_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_beacon_transmit(struct cw1200_common *priv, + const struct wsm_beacon_transmit *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT32(buf, arg->enableBeaconing ? 1 : 0); + + ret = wsm_cmd_send(priv, buf, NULL, 0x0018, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_start_find(struct cw1200_common *priv) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + ret = wsm_cmd_send(priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; +} + +/* ******************************************************************** */ + +int wsm_stop_find(struct cw1200_common *priv) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + ret = wsm_cmd_send(priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT); + wsm_cmd_unlock(priv); + return ret; +} + +/* ******************************************************************** */ + +int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + u16 cmd = 0x001C | ((arg->link_id & 0x0F) << 6); + + wsm_cmd_lock(priv); + + WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr)); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; +} + + +/* ******************************************************************** */ +/* WSM indication events implementation */ + +static int wsm_startup_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + u16 status; + char fw_label[129]; + static const char * const fw_types[] = { + "ETF", + "WFM", + "WSM", + "HI test", + "Platform test" + }; + + priv->wsm_caps.numInpChBufs = WSM_GET16(buf); + priv->wsm_caps.sizeInpChBuf = WSM_GET16(buf); + priv->wsm_caps.hardwareId = WSM_GET16(buf); + priv->wsm_caps.hardwareSubId = WSM_GET16(buf); + status = WSM_GET16(buf); + priv->wsm_caps.firmwareCap = WSM_GET16(buf); + priv->wsm_caps.firmwareType = WSM_GET16(buf); + priv->wsm_caps.firmwareApiVer = WSM_GET16(buf); + priv->wsm_caps.firmwareBuildNumber = WSM_GET16(buf); + priv->wsm_caps.firmwareVersion = WSM_GET16(buf); + WSM_GET(buf, &fw_label[0], sizeof(fw_label) - 1); + fw_label[sizeof(fw_label) - 1] = 0; /* Do not trust FW too much. */ + + if (WARN_ON(status)) + return -EINVAL; + + if (WARN_ON(priv->wsm_caps.firmwareType > 4)) + return -EINVAL; + + printk(KERN_INFO "CW1200 WSM init done.\n" + " Input buffers: %d x %d bytes\n" + " Hardware: %d.%d\n" + " %s firmware [%s], ver: %d, build: %d," + " api: %d, cap: 0x%.4X\n", + priv->wsm_caps.numInpChBufs, priv->wsm_caps.sizeInpChBuf, + priv->wsm_caps.hardwareId, priv->wsm_caps.hardwareSubId, + fw_types[priv->wsm_caps.firmwareType], + &fw_label[0], priv->wsm_caps.firmwareVersion, + priv->wsm_caps.firmwareBuildNumber, + priv->wsm_caps.firmwareApiVer, priv->wsm_caps.firmwareCap); + + priv->wsm_caps.firmwareReady = 1; + + wake_up_interruptible(&priv->wsm_startup_done); + return 0; + +underflow: + WARN_ON(1); + return -EINVAL; +} + +static int wsm_receive_indication(struct cw1200_common *priv, + struct wsm_buf *buf, + struct sk_buff **skb_p) +{ + priv->rx_timestamp = jiffies; + if (priv->wsm_cbc.rx) { + struct wsm_rx rx; + size_t hdr_len; + __le16 fctl; + + rx.status = WSM_GET32(buf); + rx.channelNumber = WSM_GET16(buf); + rx.rxedRate = WSM_GET8(buf); + rx.rcpiRssi = WSM_GET8(buf); + rx.flags = WSM_GET32(buf); + fctl = *(__le16 *)buf->data; + hdr_len = buf->data - buf->begin; + skb_pull(*skb_p, hdr_len); + if (!rx.status && unlikely(ieee80211_is_deauth(fctl))) { + if (priv->join_status == CW1200_JOIN_STATUS_STA) { + /* Shedule unjoin work */ + wsm_printk(KERN_DEBUG \ + "[WSM] Issue unjoin command (RX).\n"); + wsm_lock_tx_async(priv); + queue_work(priv->workqueue, &priv->unjoin_work); + } + } + priv->wsm_cbc.rx(priv, &rx, skb_p); + if (*skb_p) + skb_push(*skb_p, hdr_len); + } + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_event_indication(struct cw1200_common *priv, struct wsm_buf *buf) +{ + int first; + unsigned long flags; + struct cw1200_wsm_event *event; + + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { + /* STA is stopped. */ + return 0; + } + + event = kzalloc(sizeof(struct cw1200_wsm_event), GFP_KERNEL); + + event->evt.eventId = __le32_to_cpu(WSM_GET32(buf)); + event->evt.eventData = __le32_to_cpu(WSM_GET32(buf)); + + wsm_printk(KERN_DEBUG "[WSM] Event: %d(%d)\n", + event->evt.eventId, event->evt.eventData); + + spin_lock_irqsave(&priv->event_queue_lock, flags); + first = list_empty(&priv->event_queue); + list_add_tail(&event->link, &priv->event_queue); + spin_unlock_irqrestore(&priv->event_queue_lock, flags); + + if (first) + queue_work(priv->workqueue, &priv->event_handler); + + return 0; + +underflow: + kfree(event); + return -EINVAL; +} + +static int wsm_channel_switch_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + wsm_unlock_tx(priv); /* Re-enable datapath */ + WARN_ON(WSM_GET32(buf)); + + priv->channel_switch_in_progress = 0; + wake_up_interruptible(&priv->channel_switch_done); + + if (priv->wsm_cbc.channel_switch) + priv->wsm_cbc.channel_switch(priv); + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_set_pm_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + return 0; +} + +static int wsm_scan_complete_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + if (priv->wsm_cbc.scan_complete) { + struct wsm_scan_complete arg; + arg.status = WSM_GET32(buf); + arg.psm = WSM_GET8(buf); + arg.numChannels = WSM_GET8(buf); + priv->wsm_cbc.scan_complete(priv, &arg); + } + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_find_complete_indication(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + /* TODO: Implement me. */ + STUB(); + return 0; +} + +static int wsm_suspend_resume_indication(struct cw1200_common *priv, + int link_id, struct wsm_buf *buf) +{ + if (priv->wsm_cbc.suspend_resume) { + u32 flags; + struct wsm_suspend_resume arg; + + flags = WSM_GET32(buf); + arg.link_id = link_id; + arg.stop = !(flags & 1); + arg.multicast = !!(flags & 8); + arg.queue = (flags >> 1) & 3; + + priv->wsm_cbc.suspend_resume(priv, &arg); + } + return 0; + +underflow: + return -EINVAL; +} + + +/* ******************************************************************** */ +/* WSM TX */ + +int wsm_cmd_send(struct cw1200_common *priv, + struct wsm_buf *buf, + void *arg, u16 cmd, long tmo) +{ + unsigned long flags; + size_t buf_len = buf->data - buf->begin; + int ret; + + if (cmd == 0x0006) /* Write MIB */ + wsm_printk(KERN_DEBUG "[WSM] >>> 0x%.4X [MIB: 0x%.4X] (%d)\n", + cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]), + buf_len); + else + wsm_printk(KERN_DEBUG "[WSM] >>> 0x%.4X (%d)\n", cmd, buf_len); + + /* Fill HI message header */ + /* BH will add sequence number */ + ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len); + ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd); + + spin_lock_irqsave(&priv->wsm_cmd.lock, flags); + BUG_ON(priv->wsm_cmd.ptr); + priv->wsm_cmd.done = 0; + priv->wsm_cmd.ptr = buf->begin; + priv->wsm_cmd.len = buf_len; + priv->wsm_cmd.arg = arg; + priv->wsm_cmd.cmd = cmd; + spin_unlock_irqrestore(&priv->wsm_cmd.lock, flags); + + cw1200_bh_wakeup(priv); + + if (unlikely(priv->bh_error)) { + /* Do not wait for timeout if BH is dead. Exit immediately. */ + ret = 0; + } else { + long rx_timestamp; + /* Firmware prioritizes data traffic over control confirm. + * Loop below checks if data was RXed and increases timeout + * accordingly. */ + do { + /* It's safe to use unprotected access to + * wsm_cmd.done here */ + ret = wait_event_interruptible_timeout( + priv->wsm_cmd_wq, + priv->wsm_cmd.done, tmo); + rx_timestamp = jiffies - priv->rx_timestamp; + if (unlikely(rx_timestamp < 0)) + rx_timestamp = tmo + 1; + } while (!ret && rx_timestamp <= tmo); + } + + if (unlikely(ret == 0)) { + u16 raceCheck; + + spin_lock_irqsave(&priv->wsm_cmd.lock, flags); + raceCheck = priv->wsm_cmd.cmd; + priv->wsm_cmd.arg = NULL; + priv->wsm_cmd.ptr = NULL; + spin_unlock_irqrestore(&priv->wsm_cmd.lock, flags); + + /* Race condition check to make sure _confirm is not called + * after exit of _send */ + if (raceCheck == 0xFFFF) { + /* If wsm_handle_rx got stuck in _confirm we will hang + * system there. It's better than silently currupt + * stack or heap, isn't it? */ + BUG_ON(wait_event_interruptible_timeout( + priv->wsm_cmd_wq, priv->wsm_cmd.done, + WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); + } + ret = -ETIMEDOUT; + } else { + spin_lock_irqsave(&priv->wsm_cmd.lock, flags); + BUG_ON(!priv->wsm_cmd.done); + ret = priv->wsm_cmd.ret; + spin_unlock_irqrestore(&priv->wsm_cmd.lock, flags); + } + wsm_buf_reset(buf); + return ret; +} + +/* ******************************************************************** */ +/* WSM TX port control */ + +void wsm_lock_tx(struct cw1200_common *priv) +{ + wsm_cmd_lock(priv); + if (atomic_add_return(1, &priv->tx_lock) == 1) { + WARN_ON(wait_event_interruptible_timeout(priv->hw_bufs_used_wq, + !priv->hw_bufs_used, WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); + wsm_printk(KERN_DEBUG "[WSM] TX is locked.\n"); + } + wsm_cmd_unlock(priv); +} + +void wsm_lock_tx_async(struct cw1200_common *priv) +{ + if (atomic_add_return(1, &priv->tx_lock) == 1) + wsm_printk(KERN_DEBUG "[WSM] TX is locked.\n"); +} + +void wsm_flush_tx(struct cw1200_common *priv) +{ + BUG_ON(!atomic_read(&priv->tx_lock)); + WARN_ON(wait_event_interruptible_timeout(priv->hw_bufs_used_wq, + !priv->hw_bufs_used, WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); +} + +void wsm_unlock_tx(struct cw1200_common *priv) +{ + int tx_lock; + tx_lock = atomic_sub_return(1, &priv->tx_lock); + if (tx_lock < 0) { + BUG_ON(1); + } else if (tx_lock == 0) { + cw1200_bh_wakeup(priv); + wsm_printk(KERN_DEBUG "[WSM] TX is unlocked.\n"); + } +} + +/* ******************************************************************** */ +/* WSM RX */ + +int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len) +{ + STUB(); + return 0; +} + +int wsm_handle_rx(struct cw1200_common *priv, int id, + struct wsm_hdr *wsm, struct sk_buff **skb_p) +{ + int ret = 0; + unsigned long flags; + struct wsm_buf wsm_buf; + int link_id = (id >> 6) & 0x0F; + + /* Strip link id. */ + id &= ~(0x0F << 6); + + wsm_buf.begin = (u8 *)&wsm[0]; + wsm_buf.data = (u8 *)&wsm[1]; + wsm_buf.end = &wsm_buf.begin[__le32_to_cpu(wsm->len)]; + + wsm_printk(KERN_DEBUG "[WSM] <<< 0x%.4X (%d)\n", id, + wsm_buf.end - wsm_buf.begin); + + if (id == 0x404) { + ret = wsm_tx_confirm(priv, &wsm_buf); + cw1200_bh_wakeup(priv); + } else if (id & 0x0400) { + void *wsm_arg; + u16 wsm_cmd; + + /* Do not trust FW too much. Protection against repeated + * response and race condition removal (see above). */ + spin_lock_irqsave(&priv->wsm_cmd.lock, flags); + wsm_arg = priv->wsm_cmd.arg; + wsm_cmd = priv->wsm_cmd.cmd & ~(0x0F << 6); + priv->wsm_cmd.cmd = 0xFFFF; + spin_unlock_irqrestore(&priv->wsm_cmd.lock, flags); + + if (WARN_ON((id & ~0x0400) != wsm_cmd)) { + /* Note that any non-zero is a fatal retcode. */ + ret = -EINVAL; + goto out; + } + + switch (id) { + case 0x0409: + /* Note that wsm_arg can be NULL in case of timeout in + * wsm_cmd_send(). */ + if (likely(wsm_arg)) + ret = wsm_configuration_confirm(priv, wsm_arg, + &wsm_buf); + break; + case 0x0405: + if (likely(wsm_arg)) + ret = wsm_read_mib_confirm(priv, wsm_arg, + &wsm_buf); + break; + case 0x040B: + if (likely(wsm_arg)) + ret = wsm_join_confirm(priv, wsm_arg, &wsm_buf); + break; + case 0x0406: /* write_mib */ + case 0x0407: /* start-scan */ + case 0x0408: /* stop-scan */ + case 0x040A: /* wsm_reset */ + case 0x040C: /* add_key */ + case 0x040D: /* remove_key */ + case 0x0410: /* wsm_set_pm */ + case 0x0411: /* set_bss_params */ + case 0x0412: /* set_tx_queue_params */ + case 0x0413: /* set_edca_params */ + case 0x0416: /* switch_channel */ + case 0x0417: /* start */ + case 0x0418: /* beacon_transmit */ + case 0x0419: /* start_find */ + case 0x041A: /* stop_find */ + case 0x041C: /* map_link */ + WARN_ON(wsm_arg != NULL); + ret = wsm_generic_confirm(priv, wsm_arg, &wsm_buf); + if (ret) + printk(KERN_ERR + "[WSM] wsm_generic_confirm " + "failed for request 0x%.4X.\n", + id); + break; + default: + BUG_ON(1); + } + + spin_lock_irqsave(&priv->wsm_cmd.lock, flags); + priv->wsm_cmd.ret = ret; + priv->wsm_cmd.done = 1; + spin_unlock_irqrestore(&priv->wsm_cmd.lock, flags); + ret = 0; /* Error response from device should ne stop BH. */ + + wake_up_interruptible(&priv->wsm_cmd_wq); + } else if (id & 0x0800) { + switch (id) { + case 0x0801: + ret = wsm_startup_indication(priv, &wsm_buf); + break; + case 0x0804: + ret = wsm_receive_indication(priv, &wsm_buf, skb_p); + break; + case 0x0805: + ret = wsm_event_indication(priv, &wsm_buf); + break; + case 0x080A: + ret = wsm_channel_switch_indication(priv, &wsm_buf); + break; + case 0x0809: + ret = wsm_set_pm_indication(priv, &wsm_buf); + break; + case 0x0806: + ret = wsm_scan_complete_indication(priv, &wsm_buf); + break; + case 0x080B: + ret = wsm_find_complete_indication(priv, &wsm_buf); + break; + case 0x080C: + ret = wsm_suspend_resume_indication(priv, link_id, &wsm_buf); + break; + default: + STUB(); + } + } else { + WARN_ON(1); + ret = -EINVAL; + } +out: + return ret; +} + +static bool wsm_handle_tx_data(struct cw1200_common *priv, + const struct wsm_tx *wsm, + const struct ieee80211_tx_info *tx_info) +{ + bool handled = false; + const struct ieee80211_hdr *frame = + (struct ieee80211_hdr *)&wsm[1]; + __le16 fctl = frame->frame_control; + enum { + doProbe, + doDrop, + doJoin, + doWep, + doTx, + } action = doTx; + + switch (priv->mode) { + case NL80211_IFTYPE_STATION: + if (unlikely(!priv->join_status || + memcmp(frame->addr1, priv->join_bssid, + sizeof(priv->join_bssid)))) { + if (ieee80211_is_auth(fctl)) + action = doJoin; + else + action = doDrop; + } + break; + case NL80211_IFTYPE_AP: + if (unlikely(!priv->join_status)) + action = doDrop; + break; + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + STUB(); + case NL80211_IFTYPE_MONITOR: + default: + action = doDrop; + break; + } + + if (action == doTx) { + if (unlikely(ieee80211_is_probe_req(fctl))) + action = doProbe; + else if ((fctl & __cpu_to_le32(IEEE80211_FCTL_PROTECTED)) && + tx_info->control.hw_key && + unlikely(tx_info->control.hw_key->keyidx != + priv->wep_default_key_id) && + (tx_info->control.hw_key->cipher == + WLAN_CIPHER_SUITE_WEP40 || + tx_info->control.hw_key->cipher == + WLAN_CIPHER_SUITE_WEP104)) + action = doWep; + } + + switch (action) { + case doProbe: + { + /* An interesting FW "feature". Device filters + * probe responses. + * The easiest way to get it back is to convert + * probe request into WSM start_scan command. */ + int rate_id = (wsm->flags >> 4) & 0x07; + struct cw1200_queue *queue = + &priv->tx_queue[cw1200_queue_get_queue_id( + wsm->packetID)]; + wsm_printk(KERN_DEBUG \ + "[WSM] Convert probe request to scan.\n"); + wsm_lock_tx_async(priv); + BUG_ON(priv->scan.probe_skb); + BUG_ON(cw1200_queue_get_skb(queue, + wsm->packetID, + &priv->scan.probe_skb)); + BUG_ON(cw1200_queue_remove(queue, priv, + wsm->packetID)); + /* Release used TX rate policy */ + tx_policy_put(priv, rate_id); + queue_delayed_work(priv->workqueue, + &priv->scan.probe_work, 0); + handled = true; + } + break; + case doDrop: + { + /* See detailed description of "join" below. + * We are dropping everything except AUTH in non-joined mode. */ + struct sk_buff *skb; + int rate_id = (wsm->flags >> 4) & 0x07; + struct cw1200_queue *queue = + &priv->tx_queue[cw1200_queue_get_queue_id( + wsm->packetID)]; + wsm_printk(KERN_DEBUG "[WSM] Drop frame (0x%.4X):" + " not joined.\n", fctl); + BUG_ON(cw1200_queue_get_skb(queue, wsm->packetID, &skb)); + BUG_ON(cw1200_queue_remove(queue, priv, wsm->packetID)); + /* Release used TX rate policy */ + tx_policy_put(priv, rate_id); + /* Release SKB. TODO: report TX failure. */ + dev_kfree_skb(skb); + handled = true; + } + break; + case doJoin: + { + /* There is one more interesting "feature" + * in FW: it can't do RX/TX before "join". + * "Join" here is not an association, + * but just a syncronization between AP and STA. + * BTW that means device can't receive frames + * in monitor mode. + * priv->join_status is used only in bh thread and does + * not require protection */ + wsm_printk(KERN_DEBUG "[WSM] Issue join command.\n"); + wsm_lock_tx_async(priv); + BUG_ON(priv->join_pending_frame); + priv->join_pending_frame = wsm; + queue_work(priv->workqueue, &priv->join_work); + handled = true; + } + break; + case doWep: + { + wsm_printk(KERN_DEBUG "[WSM] Issue set_default_wep_key.\n"); + wsm_lock_tx_async(priv); + priv->wep_default_key_id = tx_info->control.hw_key->keyidx; + queue_work(priv->workqueue, &priv->wep_key_work); + handled = true; + } + break; + case doTx: + { +#if 0 + /* Kept for history. If you want to implement wsm->more, + * make sure you are able to send a frame after that. */ + wsm->more = (count > 1) ? 1 : 0; + if (wsm->more) { + /* HACK!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * It's undocumented in WSM spec, but CW1200 hangs + * if 'more' is set and no TX is performed due to TX + * buffers limitation. */ + if (priv->hw_bufs_used + 1 == + priv->wsm_caps.numInpChBufs) + wsm->more = 0; + } + + /* BUG!!! FIXME: we can't use 'more' at all: we don't know + * future. It could be a request from upper layer with TX lock + * requirements (scan, for example). If "more" is set device + * will not send data and wsm_tx_lock() will fail... + * It's not obvious how to fix this deadlock. Any ideas? + * As a workaround more is set to 0. */ + wsm->more = 0; +#endif /* 0 */ + + if (ieee80211_is_deauth(fctl) && + priv->mode != NL80211_IFTYPE_AP) { + /* Shedule unjoin work */ + wsm_printk(KERN_DEBUG "[WSM] Issue unjoin command" + " (TX).\n"); +#if 0 + wsm->more = 0; +#endif /* 0 */ + wsm_lock_tx_async(priv); + queue_work(priv->workqueue, &priv->unjoin_work); + } + } + break; + } + return handled; +} + +static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv, + struct cw1200_queue **queue_p, + u32* tx_allowed_mask_p, + bool *more) +{ + int i; + struct cw1200_queue *queue = NULL; + u32 tx_allowed_mask; + int mcasts = 0; + + /* Search for a queue with multicast frames buffered */ + if (priv->sta_asleep_mask && !priv->suspend_multicast) { + tx_allowed_mask = 1 << CW1200_LINK_ID_AFTER_DTIM; + for (i = 0; i < 4; ++i) { + mcasts += cw1200_queue_get_num_queued( + &priv->tx_queue[i], tx_allowed_mask); + if (!queue && mcasts) + queue = &priv->tx_queue[i]; + if (mcasts > 1) + break; + } + if (mcasts) + goto found; + } + + /* Search for unicast traffic */ + for (i = 0; i < 4; ++i) { + queue = &priv->tx_queue[i]; + tx_allowed_mask = ~priv->sta_asleep_mask; + if (priv->sta_asleep_mask) { + tx_allowed_mask |= ~priv->tx_suspend_mask[i]; + } else { + tx_allowed_mask |= 1 << CW1200_LINK_ID_AFTER_DTIM; + } + if (cw1200_queue_get_num_queued( + queue, tx_allowed_mask)) + goto found; + } + return -ENOENT; + +found: + *queue_p = queue; + *tx_allowed_mask_p = tx_allowed_mask; + *more = mcasts > 1; + return 0; +} + +int wsm_get_tx(struct cw1200_common *priv, u8 **data, + size_t *tx_len) +{ + unsigned long flags; + struct wsm_tx *wsm = NULL; + struct ieee80211_tx_info *tx_info; + struct cw1200_queue *queue; + struct cw1200_sta_priv *sta_priv; + u32 tx_allowed_mask = 0; + /* + * Count was intended as an input for wsm->more flag. + * During implementation it was found that wsm->more + * is not usable, see details above. It is kept just + * in case you would like to try to implement it again. + */ + int count = 0; + + /* More is used only for broadcasts. */ + bool more; + + if (priv->wsm_cmd.ptr) { + ++count; + spin_lock_irqsave(&priv->wsm_cmd.lock, flags); + BUG_ON(!priv->wsm_cmd.ptr); + *data = priv->wsm_cmd.ptr; + *tx_len = priv->wsm_cmd.len; + spin_unlock_irqrestore(&priv->wsm_cmd.lock, flags); + } else { + for (;;) { + if (atomic_add_return(0, &priv->tx_lock)) + break; + + if (wsm_get_tx_queue_and_mask(priv, &queue, + &tx_allowed_mask, &more)) + break; + + if (cw1200_queue_get(queue, + tx_allowed_mask, + &wsm, &tx_info)) + continue; + + if (wsm_handle_tx_data(priv, wsm, tx_info)) + continue; /* Handled by WSM */ + + if (tx_info->control.sta) { + /* Update link id */ + sta_priv = (struct cw1200_sta_priv *) + &tx_info->control.sta->drv_priv; + wsm->hdr.id &= __cpu_to_le16(~(0x0F << 6)); + wsm->hdr.id |= + cpu_to_le16(sta_priv->link_id << 6); + } + + *data = (u8 *)wsm; + *tx_len = __le16_to_cpu(wsm->hdr.len); + if (more) { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) &wsm[1]; + /* more buffered multicast/broadcast frames + * ==> set MoreData flag in IEEE 802.11 header + * to inform PS STAs */ + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } else if (priv->mode == NL80211_IFTYPE_AP && + !priv->suspend_multicast) { + priv->suspend_multicast = true; + queue_work(priv->workqueue, + &priv->multicast_stop_work); + } + + wsm_printk(KERN_DEBUG "[WSM] >>> 0x%.4X (%d) %p %c\n", + 0x0004, *tx_len, *data, + wsm->more ? 'M' : ' '); + ++count; + break; + } + } + + return count; +} + +void wsm_txed(struct cw1200_common *priv, u8 *data) +{ + unsigned long flags; + if (data == priv->wsm_cmd.ptr) { + spin_lock_irqsave(&priv->wsm_cmd.lock, flags); + priv->wsm_cmd.ptr = NULL; + spin_unlock_irqrestore(&priv->wsm_cmd.lock, flags); + } + + /* TODO: data queues */ +} + +/* ******************************************************************** */ +/* WSM buffer */ + +void wsm_buf_init(struct wsm_buf *buf) +{ + BUG_ON(buf->begin); + buf->begin = kmalloc(SDIO_BLOCK_SIZE, GFP_KERNEL | GFP_DMA); + buf->end = buf->begin ? &buf->begin[SDIO_BLOCK_SIZE] : buf->begin; + wsm_buf_reset(buf); +} + +void wsm_buf_deinit(struct wsm_buf *buf) +{ + kfree(buf->begin); + buf->begin = buf->data = buf->end = NULL; +} + +static void wsm_buf_reset(struct wsm_buf *buf) +{ + if (buf->begin) { + buf->data = &buf->begin[4]; + *(u32 *)buf->begin = 0; + } else + buf->data = buf->begin; +} + +static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size) +{ + size_t pos = buf->data - buf->begin; + size_t size = pos + extra_size; + + + if (size & (SDIO_BLOCK_SIZE - 1)) { + size &= SDIO_BLOCK_SIZE; + size += SDIO_BLOCK_SIZE; + } + + buf->begin = krealloc(buf->begin, size, GFP_KERNEL | GFP_DMA); + if (buf->begin) { + buf->data = &buf->begin[pos]; + buf->end = &buf->begin[size]; + return 0; + } else { + buf->end = buf->data = buf->begin; + return -ENOMEM; + } +} + + diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h new file mode 100644 index 00000000000..f80e60fdc89 --- /dev/null +++ b/drivers/staging/cw1200/wsm.h @@ -0,0 +1,1512 @@ +/* + * WSM host interface (HI) interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * Based on CW1200 UMAC WSM API, which is + * Copyright (C) ST-Ericsson SA 2010 + * Author: Stewart Mathers + * + * 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. + */ + +#ifndef CW1200_WSM_H_INCLUDED +#define CW1200_WSM_H_INCLUDED + +#include + +struct cw1200_common; + +/* Bands */ +/* Radio band 2.412 -2.484 GHz. */ +#define WSM_PHY_BAND_2_4G (0) + +/* Radio band 4.9375-5.8250 GHz. */ +#define WSM_PHY_BAND_5G (1) + +/* Transmit rates */ +/* 1 Mbps ERP-DSSS */ +#define WSM_TRANSMIT_RATE_1 (0) + +/* 2 Mbps ERP-DSSS */ +#define WSM_TRANSMIT_RATE_2 (1) + +/* 5.5 Mbps ERP-CCK, ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_5 (2) */ + +/* 11 Mbps ERP-CCK, ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_11 (3) */ + +/* 22 Mbps ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_22 (4) */ + +/* 33 Mbps ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_33 (5) */ + +/* 6 Mbps (3 Mbps) ERP-OFDM, BPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_6 (6) + +/* 9 Mbps (4.5 Mbps) ERP-OFDM, BPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_9 (7) + +/* 12 Mbps (6 Mbps) ERP-OFDM, QPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_12 (8) + +/* 18 Mbps (9 Mbps) ERP-OFDM, QPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_18 (9) + +/* 24 Mbps (12 Mbps) ERP-OFDM, 16QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_24 (10) + +/* 36 Mbps (18 Mbps) ERP-OFDM, 16QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_36 (11) + +/* 48 Mbps (24 Mbps) ERP-OFDM, 64QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_48 (12) + +/* 54 Mbps (27 Mbps) ERP-OFDM, 64QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_54 (13) + +/* 6.5 Mbps HT-OFDM, BPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_6 (14) + +/* 13 Mbps HT-OFDM, QPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_13 (15) + +/* 19.5 Mbps HT-OFDM, QPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_19 (16) + +/* 26 Mbps HT-OFDM, 16QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_26 (17) + +/* 39 Mbps HT-OFDM, 16QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_39 (18) + +/* 52 Mbps HT-OFDM, 64QAM coding rate 2/3 */ +#define WSM_TRANSMIT_RATE_HT_52 (19) + +/* 58.5 Mbps HT-OFDM, 64QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_58 (20) + +/* 65 Mbps HT-OFDM, 64QAM coding rate 5/6 */ +#define WSM_TRANSMIT_RATE_HT_65 (21) + +/* Scan types */ +/* Foreground scan */ +#define WSM_SCAN_TYPE_FOREGROUND (0) + +/* Background scan */ +#define WSM_SCAN_TYPE_BACKGROUND (1) + +/* Auto scan */ +#define WSM_SCAN_TYPE_AUTO (2) + +/* Scan flags */ +/* Forced background scan means if the station cannot */ +/* enter the power-save mode, it shall force to perform a */ +/* background scan. Only valid when ScanType is */ +/* background scan. */ +#define WSM_SCAN_FLAG_FORCE_BACKGROUND (BIT(0)) + +/* The WLAN device scans one channel at a time so */ +/* that disturbance to the data traffic is minimized. */ +#define WSM_SCAN_FLAG_SPLIT_METHOD (BIT(1)) + +/* Preamble Type. Long if not set. */ +#define WSM_SCAN_FLAG_SHORT_PREAMBLE (BIT(2)) + +/* 11n Tx Mode. Mixed if not set. */ +#define WSM_SCAN_FLAG_11N_GREENFIELD (BIT(3)) + +/* Scan constraints */ +/* Maximum number of channels to be scanned. */ +#define WSM_SCAN_MAX_NUM_OF_CHANNELS (48) + +/* The maximum number of SSIDs that the device can scan for. */ +#define WSM_SCAN_MAX_NUM_OF_SSIDS (2) + +/* Power management modes */ +/* 802.11 Active mode */ +#define WSM_PSM_ACTIVE (0) + +/* 802.11 PS mode */ +#define WSM_PSM_PS (1) + +/* Undetermined */ +/* Note : Undetermined status is reported when the */ +/* NULL data frame used to advertise the PM mode to */ +/* the AP at Pre or Post Background Scan is not Acknowledged */ +#define WSM_PSM_UNKNOWN (2) + +/* Use this flag to enable the fast power-saving mode */ +#define WSM_PM_F_FAST_PSM_ENABLE (0x80) + +/* Queue IDs */ +/* best effort/legacy */ +#define WSM_QUEUE_BEST_EFFORT (0) + +/* background */ +#define WSM_QUEUE_BACKGROUND (1) + +/* video */ +#define WSM_QUEUE_VIDEO (2) + +/* voice */ +#define WSM_QUEUE_VOICE (3) + +/* HT TX parameters */ +/* Non-HT */ +#define WSM_HT_TX_NON_HT (0) + +/* Mixed format */ +#define WSM_HT_TX_MIXED (1) + +/* Greenfield format */ +#define WSM_HT_TX_GREENFIELD (2) + +/* STBC allowed */ +#define WSM_HT_TX_STBC (BIT(7)) + +/* TX status */ +/* Frame was sent aggregated */ +/* Only valid for WSM_SUCCESS status. */ +#define WSM_TX_STATUS_AGGREGATION (BIT(0)) + +/* Host should requeue this frame later. */ +/* Valid only when status is WSM_REQUEUE. */ +#define WSM_TX_STATUS_REQUEUE (BIT(1)) + +/* Normal Ack */ +#define WSM_TX_STATUS_NORMAL_ACK (0<<2) + +/* No Ack */ +#define WSM_TX_STATUS_NO_ACK (1<<2) + +/* No explicit acknowledgement */ +#define WSM_TX_STATUS_NO_EXPLICIT_ACK (2<<2) + +/* Block Ack */ +/* Only valid for WSM_SUCCESS status. */ +#define WSM_TX_STATUS_BLOCK_ACK (3<<2) + +/* RX status */ +/* Unencrypted */ +#define WSM_RX_STATUS_UNENCRYPTED (0<<0) + +/* WEP */ +#define WSM_RX_STATUS_WEP (1<<0) + +/* TKIP */ +#define WSM_RX_STATUS_TKIP (2<<0) + +/* AES */ +#define WSM_RX_STATUS_AES (3<<0) + +/* WAPI */ +#define WSM_RX_STATUS_WAPI (4<<0) + +/* Macro to fetch encryption subfield. */ +#define WSM_RX_STATUS_ENCRYPTION(status) ((status) & 0x07) + +/* Frame was part of an aggregation */ +#define WSM_RX_STATUS_AGGREGATE (BIT(3)) + +/* Frame was first in the aggregation */ +#define WSM_RX_STATUS_AGGREGATE_FIRST (BIT(4)) + +/* Frame was last in the aggregation */ +#define WSM_RX_STATUS_AGGREGATE_LAST (BIT(5)) + +/* Indicates a defragmented frame */ +#define WSM_RX_STATUS_DEFRAGMENTED (BIT(6)) + +/* Indicates a Beacon frame */ +#define WSM_RX_STATUS_BEACON (BIT(7)) + +/* Indicates STA bit beacon TIM field */ +#define WSM_RX_STATUS_TIM (BIT(8)) + +/* Indicates Beacon frame's virtual bitmap contains multicast bit */ +#define WSM_RX_STATUS_MULTICAST (BIT(9)) + +/* Indicates frame contains a matching SSID */ +#define WSM_RX_STATUS_MATCHING_SSID (BIT(10)) + +/* Indicates frame contains a matching BSSI */ +#define WSM_RX_STATUS_MATCHING_BSSI (BIT(11)) + +/* Indicates More bit set in Framectl field */ +#define WSM_RX_STATUS_MORE_DATA (BIT(12)) + +/* Indicates frame received during a measurement process */ +#define WSM_RX_STATUS_MEASUREMENT (BIT(13)) + +/* Indicates frame received as an HT packet */ +#define WSM_RX_STATUS_HT (BIT(14)) + +/* Indicates frame received with STBC */ +#define WSM_RX_STATUS_STBC (BIT(15)) + +/* Indicates Address 1 field matches dot11StationId */ +#define WSM_RX_STATUS_ADDRESS1 (BIT(16)) + +/* Indicates Group address present in the Address 1 field */ +#define WSM_RX_STATUS_GROUP (BIT(17)) + +/* Indicates Broadcast address present in the Address 1 field */ +#define WSM_RX_STATUS_BROADCAST (BIT(18)) + +/* Indicates group key used with encrypted frames */ +#define WSM_RX_STATUS_GROUP_KEY (BIT(19)) + +/* Macro to fetch encryption key index. */ +#define WSM_RX_STATUS_KEY_IDX(status) (((status >> 20)) & 0x0F) + +/* Join mode */ +/* IBSS */ +#define WSM_JOIN_MODE_IBSS (0) + +/* BSS */ +#define WSM_JOIN_MODE_BSS (1) + +/* PLCP preamble type */ +/* For long preamble */ +#define WSM_JOIN_PREAMBLE_LONG (0) + +/* For short preamble (Long for 1Mbps) */ +#define WSM_JOIN_PREAMBLE_SHORT (1) + +/* For short preamble (Long for 1 and 2Mbps) */ +#define WSM_JOIN_PREAMBLE_SHORT_2 (2) + +/* Join flags */ +/* Unsynchronized */ +#define WSM_JOIN_FLAGS_UNSYNCRONIZED (1) + +/* Key types */ +#define WSM_KEY_TYPE_WEP_DEFAULT (0) +#define WSM_KEY_TYPE_WEP_PAIRWISE (1) +#define WSM_KEY_TYPE_TKIP_GROUP (2) +#define WSM_KEY_TYPE_TKIP_PAIRWISE (3) +#define WSM_KEY_TYPE_AES_GROUP (4) +#define WSM_KEY_TYPE_AES_PAIRWISE (5) +#define WSM_KEY_TYPE_WAPI_GROUP (6) +#define WSM_KEY_TYPE_WAPI_PAIRWISE (7) + +/* Key indexes */ +#define WSM_KEY_MAX_INDEX (10) + +/* ACK policy */ +#define WSM_ACK_POLICY_NORMAL (0) +#define WSM_ACK_POLICY_NO_ACK (1) + +/* Start modes */ +#define WSM_START_MODE_AP (0) /* Mini AP */ +#define WSM_START_MODE_P2P (1) /* P2P */ + +/* SetAssociationMode MIB flags */ +#define WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE (BIT(0)) +#define WSM_ASSOCIATION_MODE_USE_HT_MODE (BIT(1)) +#define WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET (BIT(2)) +#define WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING (BIT(3)) +#define WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES (BIT(4)) + +/* RcpiRssiThreshold MIB flags */ +#define WSM_RCPI_RSSI_THRESHOLD_ENABLE (BIT(0)) +#define WSM_RCPI_RSSI_USE_RSSI (BIT(1)) +#define WSM_RCPI_RSSI_DONT_USE_UPPER (BIT(2)) +#define WSM_RCPI_RSSI_DONT_USE_LOWER (BIT(3)) + +/* WSM events */ +/* Error */ +#define WSM_EVENT_ERROR (0) + +/* BSS lost */ +#define WSM_EVENT_BSS_LOST (1) + +/* BSS regained */ +#define WSM_EVENT_BSS_REGAINED (2) + +/* Radar detected */ +#define WSM_EVENT_RADAR_DETECTED (3) + +/* RCPI or RSSI threshold triggered */ +#define WSM_EVENT_RCPI_RSSI (4) + +/* BT inactive */ +#define WSM_EVENT_BT_INACTIVE (5) + +/* BT active */ +#define WSM_EVENT_BT_ACTIVE (6) + +/* MIB IDs */ +/* 4.1 dot11StationId */ +#define WSM_MIB_ID_DOT11_STATION_ID 0x0000 + +/* 4.2 dot11MaxtransmitMsduLifeTime */ +#define WSM_MIB_ID_DOT11_MAX_TRANSMIT_LIFTIME 0x0001 + +/* 4.3 dot11MaxReceiveLifeTime */ +#define WSM_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME 0x0002 + +/* 4.4 dot11SlotTime */ +#define WSM_MIB_ID_DOT11_SLOT_TIME 0x0003 + +/* 4.5 dot11GroupAddressesTable */ +#define WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE 0x0004 + +/* 4.6 dot11WepDefaultKeyId */ +#define WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID 0x0005 + +/* 4.7 dot11CurrentTxPowerLevel */ +#define WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL 0x0006 + +/* 4.8 dot11RTSThreshold */ +#define WSM_MIB_ID_DOT11_RTS_THRESHOLD 0x0007 + +/* 4.9 NonErpProtection */ +#define WSM_MIB_ID_NON_ERP_PROTECTION 0x1000 + +/* 4.10 ArpIpAddressesTable */ +#define WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE 0x1001 + +/* 4.11 TemplateFrame */ +#define WSM_MIB_ID_TEMPLATE_FRAME 0x1002 + +/* 4.12 RxFilter */ +#define WSM_MIB_ID_RX_FILTER 0x1003 + +/* 4.13 BeaconFilterTable */ +#define WSM_MIB_ID_BEACON_FILTER_TABLE 0x1004 + +/* 4.14 BeaconFilterEnable */ +#define WSM_MIB_ID_BEACON_FILTER_ENABLE 0x1005 + +/* 4.15 OperationalPowerMode */ +#define WSM_MIB_ID_OPERATIONAL_POWER_MODE 0x1006 + +/* 4.16 BeaconWakeUpPeriod */ +#define WSM_MIB_ID_BEACON_WAKEUP_PERIOD 0x1007 + +/* 4.17 RcpiRssiThreshold */ +#define WSM_MIB_ID_RCPI_RSSI_THRESHOLD 0x1009 + +/* 4.18 StatisticsTable */ +#define WSM_MIB_ID_STATISTICS_TABLE 0x100A + +/* 4.19 IbssPsConfig */ +#define WSM_MIB_ID_IBSS_PS_CONFIG 0x100B + +/* 4.20 CountersTable */ +#define WSM_MIB_ID_COUNTERS_TABLE 0x100C + +/* 4.21 BlockAckPolicy */ +#define WSM_MIB_ID_BLOCK_ACK_POLICY 0x100E + +/* 4.22 OverrideInternalTxRate */ +#define WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE 0x100F + +/* 4.23 SetAssociationMode */ +#define WSM_MIB_ID_SET_ASSOCIATION_MODE 0x1010 + +/* 4.24 UpdateEptaConfigData */ +#define WSM_MIB_ID_UPDATE_EPTA_CONFIG_DATA 0x1011 + +/* 4.25 SelectCcaMethod */ +#define WSM_MIB_ID_SELECT_CCA_METHOD 0x1012 + +/* 4.26 SetUpasdInformation */ +#define WSM_MIB_ID_SET_UAPSD_INFORMATION 0x1013 + +/* 4.27 SetAutoCalibrationMode WBF00004073 */ +#define WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE 0x1015 + +/* 4.28 SetTxRateRetryPolicy */ +#define WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY 0x1016 + +/* 4.29 SetHostMessageTypeFilter */ +#define WSM_MIB_ID_SET_HOST_MSG_TYPE_FILTER 0x1017 + +/* 4.30 P2PFindInfo */ +#define WSM_MIB_ID_P2P_FIND_INFO 0x1018 + +/* 4.31 P2PPsModeInfo */ +#define WSM_MIB_ID_P2P_PS_MODE_INFO 0x1019 + +/* 4.32 SetEtherTypeDataFrameFilter */ +#define WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER 0x101A + +/* 4.33 SetUDPPortDataFrameFilter */ +#define WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER 0x101B + +/* 4.34 SetMagicDataFrameFilter */ +#define WSM_MIB_ID_SET_MAGIC_DATAFRAME_FILTER 0x101C + +/* This is the end of specification. */ + +/* 4.35 P2PDeviceInfo */ +#define WSM_MIB_ID_P2P_DEVICE_INFO 0x101D + +/* 4.36 SetWCDMABand */ +#define WSM_MIB_ID_SET_WCDMA_BAND 0x101E + +/* 4.37 GroupTxSequenceCounter */ +#define WSM_MIB_ID_GRP_SEQ_COUNTER 0x101F + +/* 1020 4.38 ProtectedMgmtPolicy */ + +/* 4.39 SetHtProtection */ +#define WSM_MID_ID_SET_HT_PROTECTION 0x1021 + +/* 4.40 GPIO Command */ +#define WSM_MIB_ID_GPIO_COMMAND 0x1022 + +/* 4.41 TSF Counter Value */ +#define WSM_MIB_ID_TSF_COUNTER 0x1023 + +/* Test Purposes Only */ +#define WSM_MIB_ID_BLOCK_ACK_INFO 0x100D + +/* 4.43 Keep-alive period */ +#define WSM_MIB_ID_KEEP_ALIVE_PERIOD 0x1025 + +/* 4.44 Disable BSSID filter */ +#define WSM_MIB_ID_DISABLE_BSSID_FILTER 0x1026 + +/* Frame template types */ +#define WSM_FRAME_TYPE_PROBE_REQUEST (0) +#define WSM_FRAME_TYPE_BEACON (1) +#define WSM_FRAME_TYPE_NULL (2) +#define WSM_FRAME_TYPE_QOS_NULL (3) +#define WSM_FRAME_TYPE_PS_POLL (4) +#define WSM_FRAME_TYPE_PROBE_RESPONSE (5) + +#define WSM_FRAME_GREENFIELD (0x80) /* See 4.11 */ + +/* Status */ +/* The WSM firmware has completed a request */ +/* successfully. */ +#define WSM_STATUS_SUCCESS (0) + +/* This is a generic failure code if other error codes do */ +/* not apply. */ +#define WSM_STATUS_FAILURE (1) + +/* A request contains one or more invalid parameters. */ +#define WSM_INVALID_PARAMETER (2) + +/* The request cannot perform because the device is in */ +/* an inappropriate mode. */ +#define WSM_ACCESS_DENIED (3) + +/* The frame received includes a decryption error. */ +#define WSM_STATUS_DECRYPTFAILURE (4) + +/* A MIC failure is detected in the received packets. */ +#define WSM_STATUS_MICFAILURE (5) + +/* The transmit request failed due to retry limit being */ +/* exceeded. */ +#define WSM_STATUS_RETRY_EXCEEDED (6) + +/* The transmit request failed due to MSDU life time */ +/* being exceeded. */ +#define WSM_STATUS_TX_LIFETIME_EXCEEDED (7) + +/* The link to the AP is lost. */ +#define WSM_STATUS_LINK_LOST (8) + +/* No key was found for the encrypted frame */ +#define WSM_STATUS_NO_KEY_FOUND (9) + +/* Jammer was detected when transmitting this frame */ +#define WSM_STATUS_JAMMER_DETECTED (10) + +/* The message should be requeued later. */ +/* This is applicable only to Transmit */ +#define WSM_REQUEUE (11) + + +struct wsm_hdr { + __le16 len; + __le16 id; +}; + +/* ******************************************************************** */ +/* WSM capcbility */ + +struct wsm_caps { + u16 numInpChBufs; + u16 sizeInpChBuf; + u16 hardwareId; + u16 hardwareSubId; + u16 firmwareCap; + u16 firmwareType; + u16 firmwareApiVer; + u16 firmwareBuildNumber; + u16 firmwareVersion; + int firmwareReady; +}; + +/* ******************************************************************** */ +/* WSM commands */ + +struct wsm_tx_power_range { + int min_power_level; + int max_power_level; + u32 stepping; +}; + +/* 3.1 */ +struct wsm_configuration { + /* [in] */ u32 dot11MaxTransmitMsduLifeTime; + /* [in] */ u32 dot11MaxReceiveLifeTime; + /* [in] */ u32 dot11RtsThreshold; + /* [in, out] */ u8 *dot11StationId; + /* [in] */ const void *dpdData; + /* [in] */ size_t dpdData_size; + /* [out] */ u8 dot11FrequencyBandsSupported; + /* [out] */ u32 supportedRateMask; + /* [out] */ struct wsm_tx_power_range txPowerRange[2]; +}; + +int wsm_configuration(struct cw1200_common *priv, + struct wsm_configuration *arg); + +/* 3.3 */ +struct wsm_reset { + /* [in] */ int link_id; + /* [in] */ bool reset_statistics; +}; + +int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg); + +/* 3.5 */ +int wsm_read_mib(struct cw1200_common *priv, u16 mibId, void *buf, + size_t buf_size); + +/* 3.7 */ +int wsm_write_mib(struct cw1200_common *priv, u16 mibId, void *buf, + size_t buf_size); + +/* 3.9 */ +struct wsm_ssid { + u8 ssid[32]; + u32 length; +}; + +struct wsm_scan_ch { + u16 number; + u32 minChannelTime; + u32 maxChannelTime; + u32 txPowerLevel; +}; + +/* 3.13 */ +struct wsm_scan_complete { + /* WSM_STATUS_... */ + u32 status; + + /* WSM_PSM_... */ + u8 psm; + + /* Number of channels that the scan operation completed. */ + u8 numChannels; +}; + +typedef void (*wsm_scan_complete_cb) (struct cw1200_common *priv, + struct wsm_scan_complete *arg); + +/* 3.9 */ +struct wsm_scan { + /* WSM_PHY_BAND_... */ + /* [in] */ u8 band; + + /* WSM_SCAN_TYPE_... */ + /* [in] */ u8 scanType; + + /* WSM_SCAN_FLAG_... */ + /* [in] */ u8 scanFlags; + + /* WSM_TRANSMIT_RATE_... */ + /* [in] */ u8 maxTransmitRate; + + /* Interval period in TUs that the device shall the re- */ + /* execute the requested scan. Max value supported by the device */ + /* is 256s. */ + /* [in] */ u32 autoScanInterval; + + /* Number of probe requests (per SSID) sent to one (1) */ + /* channel. Zero (0) means that none is send, which */ + /* means that a passive scan is to be done. Value */ + /* greater than zero (0) means that an active scan is to */ + /* be done. */ + /* [in] */ u32 numOfProbeRequests; + + /* Number of channels to be scanned. */ + /* Maximum value is WSM_SCAN_MAX_NUM_OF_CHANNELS. */ + /* [in] */ u8 numOfChannels; + + /* Number of SSID provided in the scan command (this */ + /* is zero (0) in broadcast scan) */ + /* The maximum number of SSIDs is WSM_SCAN_MAX_NUM_OF_SSIDS. */ + /* [in] */ u8 numOfSSIDs; + + /* The delay time (in microseconds) period */ + /* before sending a probe-request. */ + /* [in] */ u8 probeDelay; + + /* SSIDs to be scanned [numOfSSIDs]; */ + /* [in] */ struct wsm_ssid *ssids; + + /* Channels to be scanned [numOfChannels]; */ + /* [in] */ struct wsm_scan_ch *ch; +}; + +int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg); + +/* 3.11 */ +int wsm_stop_scan(struct cw1200_common *priv); + +/* 3.14 */ +struct wsm_tx_confirm { + /* Packet identifier used in wsm_tx. */ + /* [out] */ u32 packetID; + + /* WSM_STATUS_... */ + /* [out] */ u32 status; + + /* WSM_TRANSMIT_RATE_... */ + /* [out] */ u8 txedRate; + + /* The number of times the frame was transmitted */ + /* without receiving an acknowledgement. */ + /* [out] */ u8 ackFailures; + + /* WSM_TX_STATUS_... */ + /* [out] */ u16 flags; + + /* The total time in microseconds that the frame spent in */ + /* the WLAN device before transmission as completed. */ + /* [out] */ u32 mediaDelay; + + /* The total time in microseconds that the frame spent in */ + /* the WLAN device before transmission was started. */ + /* [out] */ u32 txQueueDelay; +}; + +/* 3.15 */ +typedef void (*wsm_tx_confirm_cb) (struct cw1200_common *priv, + struct wsm_tx_confirm *arg); + +/* Note that ideology of wsm_tx struct is different against the rest of + * WSM API. wsm_hdr is /not/ a caller-adapted struct to be used as an input + * argument for WSM call, but a prepared bytestream to be sent to firmware. + * It is filled partly in cw1200_skb_to_wsm, partly in low-level WSM code. + * Please pay attention once again: ideology is different. + * + * Legend: + * - [in]: cw1200_skb_to_wsm must fill this field. + * - [wsm]: the field is filled by low-level WSM. + */ +struct wsm_tx { + /* common WSM header */ + /* [in/wsm] */ struct wsm_hdr hdr; + + /* Packet identifier that meant to be used in completion. */ + /* [in] */ __le32 packetID; + + /* WSM_TRANSMIT_RATE_... */ + /* [in] */ u8 maxTxRate; + + /* WSM_QUEUE_... */ + /* [in] */ u8 queueId; + + /* True: another packet is pending on the host for transmission. */ + /* [wsm] */ u8 more; + + /* Bit 0 = 0 - Start expiry time from first Tx attempt (default) */ + /* Bit 0 = 1 - Start expiry time from receipt of Tx Request */ + /* Bits 3:1 - PTA Priority */ + /* Bits 6:4 - Tx Rate Retry Policy */ + /* Bit 7 - Reserved */ + /* [in] */ u8 flags; + + /* Should be 0. */ + /* [in] */ __le32 reserved; + + /* The elapsed time in TUs, after the initial transmission */ + /* of an MSDU, after which further attempts to transmit */ + /* the MSDU shall be terminated. Overrides the global */ + /* dot11MaxTransmitMsduLifeTime setting [optional] */ + /* Device will set the default value if this is 0. */ + /* [wsm] */ __le32 expireTime; + + /* WSM_HT_TX_... */ + /* [in] */ __le32 htTxParameters; +}; + +/* = sizeof(generic hi hdr) + sizeof(wsm hdr) + sizeof(alignment) */ +#define WSM_TX_EXTRA_HEADROOM (28) + +/* 3.16 */ +struct wsm_rx { + /* WSM_STATUS_... */ + /* [out] */ u32 status; + + /* Specifies the channel of the received packet. */ + /* [out] */ u16 channelNumber; + + /* WSM_TRANSMIT_RATE_... */ + /* [out] */ u8 rxedRate; + + /* This value is expressed in signed Q8.0 format for */ + /* RSSI and unsigned Q7.1 format for RCPI. */ + /* [out] */ u8 rcpiRssi; + + /* WSM_RX_STATUS_... */ + /* [out] */ u32 flags; + + /* An 802.11 frame. */ + /* [out] */ void *frame; + + /* Size of the frame */ + /* [out] */ size_t frame_size; +}; + +/* = sizeof(generic hi hdr) + sizeof(wsm hdr) */ +#define WSM_RX_EXTRA_HEADROOM (16) + +typedef void (*wsm_rx_cb) (struct cw1200_common *priv, struct wsm_rx *arg, + struct sk_buff **skb_p); + +/* 3.17 */ +struct wsm_event { + /* WSM_STATUS_... */ + /* [out] */ u32 eventId; + + /* Indication parameters. */ + /* For error indication, this shall be a 32-bit WSM status. */ + /* For RCPI or RSSI indication, this should be an 8-bit */ + /* RCPI or RSSI value. */ + /* [out] */ u32 eventData; +}; + +struct cw1200_wsm_event { + struct list_head link; + struct wsm_event evt; +}; + +/* 3.18 - 3.22 */ +/* Measurement. Skipped for now. Irrelevent. */ + +typedef void (*wsm_event_cb) (struct cw1200_common *priv, + struct wsm_event *arg); + +/* 3.23 */ +struct wsm_join { + /* WSM_JOIN_MODE_... */ + /* [in] */ u8 mode; + + /* WSM_PHY_BAND_... */ + /* [in] */ u8 band; + + /* Specifies the channel number to join. The channel */ + /* number will be mapped to an actual frequency */ + /* according to the band */ + /* [in] */ u16 channelNumber; + + /* Specifies the BSSID of the BSS or IBSS to be joined */ + /* or the IBSS to be started. */ + /* [in] */ u8 bssid[6]; + + /* ATIM window of IBSS */ + /* When ATIM window is zero the initiated IBSS does */ + /* not support power saving. */ + /* [in] */ u16 atimWindow; + + /* WSM_JOIN_PREAMBLE_... */ + /* [in] */ u8 preambleType; + + /* Specifies if a probe request should be send with the */ + /* specified SSID when joining to the network. */ + /* [in] */ u8 probeForJoin; + + /* DTIM Period (In multiples of beacon interval) */ + /* [in] */ u8 dtimPeriod; + + /* WSM_JOIN_FLAGS_... */ + /* [in] */ u8 flags; + + /* Length of the SSID */ + /* [in] */ u32 ssidLength; + + /* Specifies the SSID of the IBSS to join or start */ + /* [in] */ u8 ssid[32]; + + /* Specifies the time between TBTTs in TUs */ + /* [in] */ u32 beaconInterval; + + /* A bit mask that defines the BSS basic rate set. */ + /* [in] */ u32 basicRateSet; + + /* Minimum transmission power level in units of 0.1dBm */ + /* [out] */ int minPowerLevel; + + /* Maximum transmission power level in units of 0.1dBm */ + /* [out] */ int maxPowerLevel; +}; + +int wsm_join(struct cw1200_common *priv, struct wsm_join *arg); + +/* 3.25 */ +struct wsm_set_pm { + /* WSM_PSM_... */ + /* [in] */ u8 pmMode; + + /* in unit of 500us; 0 to use default */ + /* [in] */ u8 fastPsmIdlePeriod; + + /* in unit of 500us; 0 to use default */ + /* [in] */ u8 apPsmChangePeriod; + + /* in unit of 500us; 0 to disable auto-pspoll */ + /* [in] */ u8 minAutoPsPollPeriod; +}; + +int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg); + +/* 3.27 */ +struct wsm_set_pm_complete { + u8 psm; /* WSM_PSM_... */ +}; + +typedef void (*wsm_set_pm_complete_cb) (struct cw1200_common *priv, + struct wsm_set_pm_complete *arg); + +/* 3.28 */ +struct wsm_set_bss_params { + /* The number of lost consecutive beacons after which */ + /* the WLAN device should indicate the BSS-Lost event */ + /* to the WLAN host driver. */ + u8 beaconLostCount; + + /* The AID received during the association process. */ + u16 aid; + + /* The operational rate set mask */ + u32 operationalRateSet; +}; + +int wsm_set_bss_params(struct cw1200_common *priv, + const struct wsm_set_bss_params *arg); + +/* 3.30 */ +struct wsm_add_key { + u8 type; /* WSM_KEY_TYPE_... */ + u8 entryIndex; /* Key entry index: 0 -- WSM_KEY_MAX_INDEX */ + u16 reserved; + union { + struct { + u8 peerAddress[6]; /* MAC address of the + * peer station */ + u8 reserved; + u8 keyLength; /* Key length in bytes */ + u8 keyData[16]; /* Key data */ + } __packed wepPairwiseKey; + struct { + u8 keyId; /* Unique per key identifier + * (0..3) */ + u8 keyLength; /* Key length in bytes */ + u16 reserved; + u8 keyData[16]; /* Key data */ + } __packed wepGroupKey; + struct { + u8 peerAddress[6]; /* MAC address of the + * peer station */ + u8 reserved[2]; + u8 tkipKeyData[16]; /* TKIP key data */ + u8 rxMicKey[8]; /* Rx MIC key */ + u8 txMicKey[8]; /* Tx MIC key */ + } __packed tkipPairwiseKey; + struct { + u8 tkipKeyData[16]; /* TKIP key data */ + u8 rxMicKey[8]; /* Rx MIC key */ + u8 keyId; /* Key ID */ + u8 reserved[3]; + u8 rxSeqCounter[8]; /* Receive Sequence Counter */ + } __packed tkipGroupKey; + struct { + u8 peerAddress[6]; /* MAC address of the + * peer station */ + u16 reserved; + u8 aesKeyData[16]; /* AES key data */ + } __packed aesPairwiseKey; + struct { + u8 aesKeyData[16]; /* AES key data */ + u8 keyId; /* Key ID */ + u8 reserved[3]; + u8 rxSeqCounter[8]; /* Receive Sequence Counter */ + } __packed aesGroupKey; + struct { + u8 peerAddress[6]; /* MAC address of the + * peer station */ + u8 keyId; /* Key ID */ + u8 reserved; + u8 wapiKeyData[16]; /* WAPI key data */ + u8 micKeyData[16]; /* MIC key data */ + } __packed wapiPairwiseKey; + struct { + u8 wapiKeyData[16]; /* WAPI key data */ + u8 micKeyData[16]; /* MIC key data */ + u8 keyId; /* Key ID */ + u8 reserved[3]; + } __packed wapiGroupKey; + } __packed; +} __packed; + +int wsm_add_key(struct cw1200_common *priv, const struct wsm_add_key *arg); + +/* 3.32 */ +struct wsm_remove_key { + /* Key entry index : 0-10 */ + u8 entryIndex; +}; + +int wsm_remove_key(struct cw1200_common *priv, + const struct wsm_remove_key *arg); + +/* 3.34 */ +struct wsm_set_tx_queue_params { + /* 0 best effort/legacy */ + /* 1 background */ + /* 2 video */ + /* 3 voice */ + u8 queueId; + + /* WSM_ACK_POLICY_... */ + u8 ackPolicy; + + /* Medium Time of TSPEC (in 32us units) allowed per */ + /* One Second Averaging Period for this queue. */ + u16 allowedMediumTime; + + /* dot11MaxTransmitMsduLifetime to be used for the */ + /* specified queue. */ + u32 maxTransmitLifetime; +}; + +int wsm_set_tx_queue_params(struct cw1200_common *priv, + const struct wsm_set_tx_queue_params *arg); + +/* 3.36 */ +struct wsm_edca_queue_params { + /* CWmin (in slots) for the access class. */ + /* [in] */ u16 cwMin; + + /* CWmax (in slots) for the access class. */ + /* [in] */ u16 cwMax; + + /* AIFS (in slots) for the access class. */ + /* [in] */ u8 aifns; + + /* TX OP Limit (in microseconds) for the access class. */ + /* [in] */ u16 txOpLimit; + + /* dot11MaxReceiveLifetime to be used for the specified */ + /* the access class. Overrides the global */ + /* dot11MaxReceiveLifetime value */ + /* [in] */ u32 maxReceiveLifetime; +}; + +struct wsm_edca_params { + /* NOTE: index is a linux queue id. */ + struct wsm_edca_queue_params params[4]; +}; + +#define WSM_EDCA_SET(edca, queue, aifs, cw_min, cw_max, txop) \ + do { \ + struct wsm_edca_queue_params *p = &(edca)->params[queue]; \ + p->cwMin = (cw_min); \ + p->cwMax = (cw_max); \ + p->aifns = (aifs); \ + p->txOpLimit = (txop); \ + } while (0) + +int wsm_set_edca_params(struct cw1200_common *priv, + const struct wsm_edca_params *arg); + +/* 3.38 */ +/* Set-System info. Skipped for now. Irrelevent. */ + +/* 3.40 */ +struct wsm_switch_channel { + /* 1 - means the STA shall not transmit any further */ + /* frames until the channel switch has completed */ + /* [in] */ u8 channelMode; + + /* Number of TBTTs until channel switch occurs. */ + /* 0 - indicates switch shall occur at any time */ + /* 1 - occurs immediately before the next TBTT */ + /* [in] */ u8 channelSwitchCount; + + /* The new channel number to switch to. */ + /* Note this is defined as per section 2.7. */ + /* [in] */ u16 newChannelNumber; +}; + +int wsm_switch_channel(struct cw1200_common *priv, + const struct wsm_switch_channel *arg); + +typedef void (*wsm_channel_switch_cb) (struct cw1200_common *priv); + +struct wsm_start { + /* WSM_START_MODE_... */ + /* [in] */ u8 mode; + + /* WSM_PHY_BAND_... */ + /* [in] */ u8 band; + + /* Channel number */ + /* [in] */ u16 channelNumber; + + /* Client Traffic window in units of TU */ + /* Valid only when mode == ..._P2P */ + /* [in] */ u32 CTWindow; + + /* Interval between two consecutive */ + /* beacon transmissions in TU. */ + /* [in] */ u32 beaconInterval; + + /* DTIM period in terms of beacon intervals */ + /* [in] */ u8 DTIMPeriod; + + /* WSM_JOIN_PREAMBLE_... */ + /* [in] */ u8 preambleType; + + /* The delay time (in microseconds) period */ + /* before sending a probe-request. */ + /* [in] */ u8 probeDelay; + + /* Length of the SSID */ + /* [in] */ u8 ssidLength; + + /* SSID of the BSS or P2P_GO to be started now. */ + /* [in] */ u8 ssid[32]; + + /* The basic supported rates for the MiniAP. */ + /* [in] */ u32 basicRateSet; +}; + +int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg); + +struct wsm_beacon_transmit { + /* 1: enable; 0: disable */ + /* [in] */ u8 enableBeaconing; +}; + +int wsm_beacon_transmit(struct cw1200_common *priv, + const struct wsm_beacon_transmit *arg); + +int wsm_start_find(struct cw1200_common *priv); + +int wsm_stop_find(struct cw1200_common *priv); + +typedef void (*wsm_find_complete_cb) (struct cw1200_common *priv, u32 status); + +struct wsm_suspend_resume { + /* See 3.52 */ + /* Link ID */ + /* [out] */ int link_id; + /* Stop sending further Tx requests down to device for this link */ + /* [out] */ bool stop; + /* Transmit multicast Frames */ + /* [out] */ bool multicast; + /* The AC on which Tx to be suspended /resumed. */ + /* This is applicable only for U-APSD */ + /* WSM_QUEUE_... */ + /* [out] */ int queue; +}; + +typedef void (*wsm_suspend_resume_cb) (struct cw1200_common *priv, + struct wsm_suspend_resume *arg); + +/* 3.53 Update-IE request: Not implemented: not relevant. */ + +/* 3.56 */ +struct wsm_map_link { + /* MAC address of the remote device */ + /* [in] */ u8 mac_addr[6]; + /* [in] */ u8 link_id; +}; + +int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg); + +struct wsm_cbc { + wsm_scan_complete_cb scan_complete; + wsm_tx_confirm_cb tx_confirm; + wsm_rx_cb rx; + wsm_event_cb event; + wsm_set_pm_complete_cb set_pm_complete; + wsm_channel_switch_cb channel_switch; + wsm_find_complete_cb find_complete; + wsm_suspend_resume_cb suspend_resume; +}; + +/* ******************************************************************** */ +/* MIB shortcats */ + +static inline int wsm_set_output_power(struct cw1200_common *priv, + int power_level) +{ + __le32 val = __cpu_to_le32(power_level); + return wsm_write_mib(priv, WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL, + &val, sizeof(val)); +} + +static inline int wsm_set_beacon_wakeup_period(struct cw1200_common *priv, + unsigned dtim_interval, + unsigned listen_interval) +{ + struct { + u8 numBeaconPeriods; + u8 reserved; + __le16 listenInterval; + } val = { + dtim_interval, 0, __cpu_to_le16(listen_interval)}; + if (dtim_interval > 0xFF || listen_interval > 0xFFFF) + return -EINVAL; + else + return wsm_write_mib(priv, WSM_MIB_ID_BEACON_WAKEUP_PERIOD, + &val, sizeof(val)); +} + +struct wsm_rcpi_rssi_threshold { + u8 rssiRcpiMode; /* WSM_RCPI_RSSI_... */ + u8 lowerThreshold; + u8 upperThreshold; + u8 rollingAverageCount; +}; + +static inline int wsm_set_rcpi_rssi_threshold(struct cw1200_common *priv, + struct wsm_rcpi_rssi_threshold *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_RCPI_RSSI_THRESHOLD, arg, + sizeof(*arg)); +} + +static inline int wsm_get_station_id(struct cw1200_common *priv, u8 * mac) +{ + return wsm_read_mib(priv, WSM_MIB_ID_DOT11_STATION_ID, mac, ETH_ALEN); +} + +struct wsm_rx_filter { + bool promiscuous; + bool bssid; + bool fcs; +}; + +static inline int wsm_set_rx_filter(struct cw1200_common *priv, + const struct wsm_rx_filter *arg) +{ + __le32 val = 0; + if (arg->promiscuous) + val |= __cpu_to_le32(BIT(0)); + if (arg->bssid) + val |= __cpu_to_le32(BIT(1)); + if (arg->fcs) + val |= __cpu_to_le32(BIT(2)); + return wsm_write_mib(priv, WSM_MIB_ID_RX_FILTER, &val, sizeof(val)); +} + +struct wsm_beacon_filter_control { + int enabled; + int bcn_count; +}; + +static inline int wsm_beacon_filter_control(struct cw1200_common *priv, + struct wsm_beacon_filter_control *arg) +{ + struct { + __le32 enabled; + __le32 bcn_count; + } val; + val.enabled = __cpu_to_le32(arg->enabled); + val.bcn_count = __cpu_to_le32(arg->bcn_count); + return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_ENABLE, &val, + sizeof(val)); +} + +enum wsm_power_mode { + wsm_power_mode_active = 0, + wsm_power_mode_doze = 1, + wsm_power_mode_quiescent = 2, +}; + +struct wsm_operational_mode { + enum wsm_power_mode power_mode; + int disableMoreFlagUsage; + int performAntDiversity; +}; + +static inline int wsm_set_operational_mode(struct cw1200_common *priv, + const struct wsm_operational_mode *arg) +{ + u8 val = arg->power_mode; + if (arg->disableMoreFlagUsage) + val |= BIT(4); + if (arg->performAntDiversity) + val |= BIT(5); + return wsm_write_mib(priv, WSM_MIB_ID_OPERATIONAL_POWER_MODE, &val, + sizeof(val)); +} + +struct wsm_template_frame { + u8 frame_type; + u8 rate; + struct sk_buff *skb; +}; + +static inline int wsm_set_template_frame(struct cw1200_common *priv, + struct wsm_template_frame *arg) +{ + int ret; + u8 *p = skb_push(arg->skb, 4); + p[0] = arg->frame_type; + p[1] = arg->rate; + ((u16 *) p)[1] = __cpu_to_le32(arg->skb->len - 4); + ret = wsm_write_mib(priv, WSM_MIB_ID_TEMPLATE_FRAME, p, arg->skb->len); + skb_pull(arg->skb, 4); + return ret; +} + +static inline int wsm_set_block_ack_policy(struct cw1200_common *priv, + u8 blockAckTxTidPolicy, + u8 blockAckRxTidPolicy) +{ + struct { + u8 blockAckTxTidPolicy; + u8 reserved1; + u8 blockAckRxTidPolicy; + u8 reserved2; + } val = { + .blockAckTxTidPolicy = blockAckTxTidPolicy, + .blockAckRxTidPolicy = blockAckRxTidPolicy, + }; + return wsm_write_mib(priv, WSM_MIB_ID_BLOCK_ACK_POLICY, &val, + sizeof(val)); +} + +struct wsm_association_mode { + u8 flags; /* WSM_ASSOCIATION_MODE_... */ + u8 preambleType; /* WSM_JOIN_PREAMBLE_... */ + u8 greenfieldMode; /* 1 for greenfield */ + u8 mpduStartSpacing; + __le32 basicRateSet; +}; + +static inline int wsm_set_association_mode(struct cw1200_common *priv, + struct wsm_association_mode *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_SET_ASSOCIATION_MODE, arg, + sizeof(*arg)); +} + +struct wsm_set_tx_rate_retry_policy_header { + u8 numTxRatePolicies; + u8 reserved[3]; +} __packed; + +struct wsm_set_tx_rate_retry_policy_policy { + u8 policyIndex; + u8 shortRetryCount; + u8 longRetryCount; + u8 policyFlags; + u8 rateRecoveryCount; + u8 reserved[3]; + __le32 rateCountIndices[3]; +} __packed; + +struct wsm_set_tx_rate_retry_policy { + struct wsm_set_tx_rate_retry_policy_header hdr; + struct wsm_set_tx_rate_retry_policy_policy tbl[8]; +} __packed; + +static inline int wsm_set_tx_rate_retry_policy(struct cw1200_common *priv, + struct wsm_set_tx_rate_retry_policy *arg) +{ + size_t size = sizeof(struct wsm_set_tx_rate_retry_policy_header) + + arg->hdr.numTxRatePolicies * + sizeof(struct wsm_set_tx_rate_retry_policy_policy); + return wsm_write_mib(priv, WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg, + size); +} + +/* Undocumented MIBs: */ +/* 4.35 P2PDeviceInfo */ +#define D11_MAX_SSID_LEN (32) + +struct wsm_p2p_device_type { + __le16 categoryId; + u8 oui[4]; + __le16 subCategoryId; +} __packed; + +struct wsm_p2p_device_info { + struct wsm_p2p_device_type primaryDevice; + u8 reserved1[3]; + u8 devNameSize; + u8 localDevName[D11_MAX_SSID_LEN]; + u8 reserved2[3]; + u8 numSecDevSupported; + struct wsm_p2p_device_type secondaryDevices[0]; +} __packed; + +/* 4.36 SetWCDMABand - WO */ +struct wsm_cdma_band { + u8 WCDMA_Band; + u8 reserved[3]; +} __packed; + +/* 4.37 GroupTxSequenceCounter - RO */ +struct wsm_group_tx_seq { + __le32 bits_47_16; + __le16 bits_15_00; + __le16 reserved; +} __packed; + +/* 4.39 SetHtProtection - WO */ +#define WSM_DUAL_CTS_PROT_ENB (1 << 0) +#define WSM_NON_GREENFIELD_STA PRESENT(1 << 1) +#define WSM_HT_PROT_MODE__NO_PROT (0 << 2) +#define WSM_HT_PROT_MODE__NON_MEMBER (1 << 2) +#define WSM_HT_PROT_MODE__20_MHZ (2 << 2) +#define WSM_HT_PROT_MODE__NON_HT_MIXED (3 << 2) +#define WSM_LSIG_TXOP_PROT_FULL (1 << 4) +#define WSM_LARGE_L_LENGTH_PROT (1 << 5) + +struct wsm_ht_protection { + __le32 flags; +} __packed; + +/* 4.40 GPIO Command - R/W */ +#define WSM_GPIO_COMMAND_SETUP 0 +#define WSM_GPIO_COMMAND_READ 1 +#define WSM_GPIO_COMMAND_WRITE 2 +#define WSM_GPIO_COMMAND_RESET 3 +#define WSM_GPIO_ALL_PINS 0xFF + +struct wsm_gpio_command { + u8 GPIO_Command; + u8 pin; + __le16 config; +} __packed; + +/* 4.41 TSFCounter - RO */ +struct wsm_tsf_counter { + __le64 TSF_Counter; +} __packed; + +/* 4.43 Keep alive period */ +struct wsm_keep_alive_period { + __le16 keepAlivePeriod; + u8 reserved[2]; +} __packed; + +static inline int wsm_keep_alive_period(struct cw1200_common *priv, + int period) +{ + struct wsm_keep_alive_period arg = { + .keepAlivePeriod = period, + }; + return wsm_write_mib(priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD, + &arg, sizeof(arg)); +}; + +/* BSSID filtering */ +struct wsm_set_bssid_filtering { + u8 filter; + u8 reserved[3]; +} __packed; + +static inline int wsm_set_bssid_filtering(struct cw1200_common *priv, + bool enabled) +{ + struct wsm_set_bssid_filtering arg = { + .filter = !enabled, + }; + return wsm_write_mib(priv, WSM_MIB_ID_DISABLE_BSSID_FILTER, + &arg, sizeof(arg)); +} + +/* ******************************************************************** */ +/* WSM TX port control */ + +void wsm_lock_tx(struct cw1200_common *priv); +void wsm_lock_tx_async(struct cw1200_common *priv); +void wsm_flush_tx(struct cw1200_common *priv); +void wsm_unlock_tx(struct cw1200_common *priv); + +/* ******************************************************************** */ +/* WSM / BH API */ + +int wsm_handle_exception(struct cw1200_common *priv, u8 * data, size_t len); +int wsm_handle_rx(struct cw1200_common *priv, int id, struct wsm_hdr *wsm, + struct sk_buff **skb_p); + +/* ******************************************************************** */ +/* wsm_buf API */ + +struct wsm_buf { + u8 *begin; + u8 *data; + u8 *end; +}; + +void wsm_buf_init(struct wsm_buf *buf); +void wsm_buf_deinit(struct wsm_buf *buf); + +/* ******************************************************************** */ +/* wsm_cmd API */ + +struct wsm_cmd { + spinlock_t lock; + int done; + u8 *ptr; + size_t len; + void *arg; + int ret; + u16 cmd; +}; + +/* ******************************************************************** */ +/* WSM TX buffer access */ + +int wsm_get_tx(struct cw1200_common *priv, u8 ** data, size_t * tx_len); +void wsm_txed(struct cw1200_common *priv, u8 * data); + +/* ******************************************************************** */ +/* Queue mapping: WSM <---> linux */ +/* Linux: VO VI BE BK */ +/* WSM: BE BK VI VO */ + +static inline u8 wsm_queue_id_to_linux(u8 queueId) +{ + static const u8 queue_mapping[] = { + 2, 3, 1, 0 + }; + return queue_mapping[queueId]; +} + +static inline u8 wsm_queue_id_to_wsm(u8 queueId) +{ + static const u8 queue_mapping[] = { + 3, 2, 0, 1 + }; + return queue_mapping[queueId]; +} + +#endif /* CW1200_HWIO_H_INCLUDED */ -- cgit v1.2.3 From a1a5d048aa916433cc3c71a151b9d863720be9df Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 28 Apr 2011 08:12:50 +0200 Subject: WLAN: CW1200: Support for GPIO-based IRQ. Signed-off-by: Dmitry Tarnyagin --- drivers/staging/cw1200/Kconfig | 11 +++++ drivers/staging/cw1200/cw1200_sdio.c | 84 +++++++++++++++++++++++++++++++++++- 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/Kconfig b/drivers/staging/cw1200/Kconfig index 19ac553b745..e24df0069a8 100644 --- a/drivers/staging/cw1200/Kconfig +++ b/drivers/staging/cw1200/Kconfig @@ -28,6 +28,17 @@ config CW1200_NON_POWER_OF_TWO_BLOCKSIZES which does not have support for non-power-of-two SDIO transfer. If unsure, say Y. +config CW1200_USE_GPIO_IRQ + bool "Use GPIO interrupt" + depends on CW1200 + help + Say Y here if you want to include GPIO IRQ support instead of SDIO IRQ. + If unsure, say N. + +config CW1200_GPIO_IRQ_NUM + int "GPIO number for the IRQ line" + depends on CW1200_USE_GPIO_IRQ + default 216 config CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE bool "Software keep-alive (DEVELOPMENT)" diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index 0864984436c..ac25e0500d9 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -80,6 +81,7 @@ static void cw1200_sdio_unlock(struct sbus_priv *self) sdio_release_host(self->func); } +#ifndef CONFIG_CW1200_USE_GPIO_IRQ static void cw1200_sdio_irq_handler(struct sdio_func *func) { struct sbus_priv *self = sdio_get_drvdata(func); @@ -91,6 +93,76 @@ static void cw1200_sdio_irq_handler(struct sdio_func *func) self->irq_handler(self->irq_priv); spin_unlock_irqrestore(&self->lock, flags); } +#else /* CONFIG_CW1200_USE_GPIO_IRQ */ +static irqreturn_t cw1200_gpio_irq_handler(int irq, void *dev_id) +{ + struct sbus_priv *self = dev_id; + + BUG_ON(!self); + if (self->irq_handler) + self->irq_handler(self->irq_priv); + return IRQ_HANDLED; +} + +static int cw1200_request_irq(struct sbus_priv *self, + irq_handler_t handler) +{ + int ret; + int func_num; + int gpio = CONFIG_CW1200_GPIO_IRQ_NUM; + int irq = gpio_to_irq(gpio); + u8 cccr; + + ret = gpio_request(gpio, "cw1200_irq"); + if (WARN_ON(ret)) + goto exit; + + ret = gpio_direction_input(gpio); + if (WARN_ON(ret)) + goto free_gpio; + + ret = request_any_context_irq(irq, handler, + IRQF_TRIGGER_RISING, "cw1200_irq", self); + if (WARN_ON(ret < 0)) + goto free_gpio; + + ret = enable_irq_wake(irq); + if (WARN_ON(ret)) + goto free_irq; + + /* Hack to access Fuction-0 */ + func_num = self->func->num; + self->func->num = 0; + + cccr = sdio_readb(self->func, SDIO_CCCR_IENx, &ret); + if (WARN_ON(ret)) + goto set_func; + + /* Master interrupt enable ... */ + cccr |= 1; + + /* ... for our function */ + cccr |= 1 << func_num; + + sdio_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret); + if (WARN_ON(ret)) + goto set_func; + + /* Restore the WLAN function number */ + self->func->num = func_num; + return 0; + +set_func: + self->func->num = func_num; + disable_irq_wake(irq); +free_irq: + free_irq(irq, self); +free_gpio: + gpio_free(gpio); +exit: + return ret; +} +#endif /* CONFIG_CW1200_USE_GPIO_IRQ */ static int cw1200_sdio_irq_subscribe(struct sbus_priv *self, sbus_irq_handler handler, @@ -109,14 +181,18 @@ static int cw1200_sdio_irq_subscribe(struct sbus_priv *self, printk(KERN_DEBUG "SW IRQ subscribe\n"); sdio_claim_host(self->func); +#ifndef CONFIG_CW1200_USE_GPIO_IRQ ret = sdio_claim_irq(self->func, cw1200_sdio_irq_handler); +#else + ret = cw1200_request_irq(self, cw1200_gpio_irq_handler); +#endif sdio_release_host(self->func); return ret; } static int cw1200_sdio_irq_unsubscribe(struct sbus_priv *self) { - int ret; + int ret = 0; unsigned long flags; WARN_ON(!self->irq_handler); @@ -124,9 +200,15 @@ static int cw1200_sdio_irq_unsubscribe(struct sbus_priv *self) return 0; printk(KERN_DEBUG "SW IRQ unsubscribe\n"); +#ifndef CONFIG_CW1200_USE_GPIO_IRQ sdio_claim_host(self->func); ret = sdio_release_irq(self->func); sdio_release_host(self->func); +#else + disable_irq_wake(gpio_to_irq(CONFIG_CW1200_GPIO_IRQ_NUM)); + free_irq(gpio_to_irq(CONFIG_CW1200_GPIO_IRQ_NUM), self); + gpio_free(CONFIG_CW1200_GPIO_IRQ_NUM); +#endif spin_lock_irqsave(&self->lock, flags); self->irq_priv = NULL; -- cgit v1.2.3 From aa4cfc64e61761acd66b7a4586bc07eb2a931204 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Sun, 1 May 2011 17:20:35 +0200 Subject: WLAN: CW1200: Power management is implemnted. Power management is on in this commit. Device is configured in quiescent mode when idle and in dose mode when operating. BH always checks if device is awake before trying to access it. Timeout for putting device back to sleep is 1 second after last device access. Verification with WSM_A21.05.0288 shows that device is staying awake even in quiescent mode. Bug in firmware? To be investigated. Signed-off-by: Dmitry Tarnyagin --- drivers/staging/cw1200/bh.c | 82 +++++++++++++++++++++++++++++++++++++---- drivers/staging/cw1200/bh.h | 3 ++ drivers/staging/cw1200/cw1200.h | 2 + drivers/staging/cw1200/main.c | 7 ++++ drivers/staging/cw1200/sta.c | 8 ++-- drivers/staging/cw1200/wsm.c | 33 ++++++++++++++++- 6 files changed, 123 insertions(+), 12 deletions(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index 94711af1ea3..286d4cb43b7 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -31,7 +31,7 @@ static int cw1200_bh(void *arg); -/* TODO: Werify these numbers with WSM specification. */ +/* TODO: Verify these numbers with WSM specification. */ #define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4) /* an SPI message cannot be bigger than (2"12-1)*2 bytes * "*2" to cvt to bytes */ @@ -178,6 +178,42 @@ static inline int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv, return ret; } +static int cw1200_device_wakeup(struct cw1200_common *priv) +{ + u16 ctrl_reg; + int ret; + + bh_printk(KERN_DEBUG "[BH] Device wakeup.\n"); + + /* To force the device to be always-on, the host sets WLAN_UP to 1 */ + ret = cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, + ST90TDS_CONT_WUP_BIT); + if (WARN_ON(ret)) + return ret; + + ret = cw1200_bh_read_ctrl_reg(priv, &ctrl_reg); + if (WARN_ON(ret)) + return ret; + + /* If the device returns WLAN_RDY as 1, the device is active and will + * remain active. */ + if (ctrl_reg & ST90TDS_CONT_RDY_BIT) { + bh_printk(KERN_DEBUG "[BH] Device awake.\n"); + return 1; + } + + return 0; +} + +/* Must be called from BH thraed. */ +void cw1200_enable_powersave(struct cw1200_common *priv, + bool enable) +{ + bh_printk(KERN_DEBUG "[BH] Powerave is %s.\n", + enable ? "enabled" : "disabled"); + priv->powersave_enabled = enable; +} + static int cw1200_bh(void *arg) { struct cw1200_common *priv = arg; @@ -191,18 +227,37 @@ static int cw1200_bh(void *arg) int rx_resync = 1; u16 ctrl_reg = 0; int tx_allowed; + int pending_tx = 0; + long status; for (;;) { - int status = wait_event_interruptible(priv->bh_wq, ({ + if (!priv->hw_bufs_used + && priv->powersave_enabled + && !priv->device_can_sleep) + status = 1 * HZ; + else + status = MAX_SCHEDULE_TIMEOUT; + + status = wait_event_interruptible_timeout(priv->bh_wq, ({ rx = atomic_xchg(&priv->bh_rx, 0); tx = atomic_xchg(&priv->bh_tx, 0); term = atomic_xchg(&priv->bh_term, 0); (rx || tx || term); - })); + }), status); - if (status || term) + if (status < 0 || term) break; + if (!status) { + bh_printk(KERN_DEBUG "[BH] Device wakedown.\n"); + WARN_ON(cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 0)); + priv->device_can_sleep = true; + continue; + } + + tx += pending_tx; + pending_tx = 0; + if (rx) { size_t alloc_len; u8 *data; @@ -211,7 +266,7 @@ static int cw1200_bh(void *arg) priv, &ctrl_reg))) break; rx: - read_len = (ctrl_reg & 0xFFF) * 2; + read_len = (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2; if (!read_len) goto tx; @@ -327,6 +382,19 @@ tx: u8 *data; int ret; + if (priv->device_can_sleep) { + ret = cw1200_device_wakeup(priv); + if (WARN_ON(ret < 0)) + break; + else if (ret) + priv->device_can_sleep = false; + else { + /* Wait for "awake" interrupt */ + pending_tx = tx; + continue; + } + } + wsm_alloc_tx_buffer(priv); ret = wsm_get_tx(priv, &data, &tx_len); if (ret <= 0) { @@ -381,13 +449,13 @@ tx: /* HACK!!! Device tends not to send interrupt * if this extra check is missing */ - if (!(ctrl_reg & 0xFFF)) { + if (!(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)) { if (WARN_ON(cw1200_bh_read_ctrl_reg( priv, &ctrl_reg))) break; } - if (ctrl_reg & 0xFFF) + if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) goto rx; } diff --git a/drivers/staging/cw1200/bh.h b/drivers/staging/cw1200/bh.h index b44388e164b..af71a5a1066 100644 --- a/drivers/staging/cw1200/bh.h +++ b/drivers/staging/cw1200/bh.h @@ -22,5 +22,8 @@ int cw1200_register_bh(struct cw1200_common *priv); void cw1200_unregister_bh(struct cw1200_common *priv); void cw1200_irq_handler(struct cw1200_common *priv); void cw1200_bh_wakeup(struct cw1200_common *priv); +/* Must be called from BH thread. */ +void cw1200_enable_powersave(struct cw1200_common *priv, + bool enable); #endif /* CW1200_BH_H */ diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 2251f1ca756..09b3cea1fca 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -129,6 +129,8 @@ struct cw1200_common { int hw_bufs_used; wait_queue_head_t hw_bufs_used_wq; struct sk_buff *skb_cache; + bool powersave_enabled; + bool device_can_sleep; /* WSM */ struct wsm_caps wsm_caps; diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index e1251b4e4e4..676b6264ecc 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -395,6 +395,10 @@ int cw1200_probe(const struct sbus_ops *sbus_ops, int err = -ENOMEM; struct ieee80211_hw *dev; struct cw1200_common *priv; + struct wsm_operational_mode mode = { + .power_mode = wsm_power_mode_quiescent, + .disableMoreFlagUsage = true, + }; dev = cw1200_init_common(sizeof(struct cw1200_common)); if (!dev) @@ -429,6 +433,9 @@ int cw1200_probe(const struct sbus_ops *sbus_ops, goto err3; } + /* Set low-power mode. */ + WARN_ON(wsm_set_operational_mode(priv, &mode)); + err = cw1200_register_common(dev); if (err) { priv->sbus_ops->irq_unsubscribe(priv->sbus_priv); diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 3bb17b1e616..0ae58e7bc02 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -283,12 +283,14 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) if (changed & IEEE80211_CONF_CHANGE_IDLE) { struct wsm_operational_mode mode = { - /* TODO: wsm_power_mode_quiescent is more efficient, - * but it requires AI to get device on again. */ .power_mode = (conf->flags & IEEE80211_CONF_IDLE) ? - wsm_power_mode_doze : wsm_power_mode_active + wsm_power_mode_quiescent : + wsm_power_mode_doze, + .disableMoreFlagUsage = true, }; + wsm_lock_tx(priv); WARN_ON(wsm_set_operational_mode(priv, &mode)); + wsm_unlock_tx(priv); } if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 37723cc88a1..a145ec157f3 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -255,13 +255,19 @@ int wsm_write_mib(struct cw1200_common *priv, u16 mibId, void *_buf, { int ret; struct wsm_buf *buf = &priv->wsm_cmd_buf; + struct wsm_mib mib_buf = { + .mibId = mibId, + .buf = _buf, + .buf_size = buf_size, + }; + wsm_cmd_lock(priv); WSM_PUT16(buf, mibId); WSM_PUT16(buf, buf_size); WSM_PUT(buf, _buf, buf_size); - ret = wsm_cmd_send(priv, buf, NULL, 0x0006, WSM_CMD_TIMEOUT); + ret = wsm_cmd_send(priv, buf, &mib_buf, 0x0006, WSM_CMD_TIMEOUT); wsm_cmd_unlock(priv); return ret; @@ -270,6 +276,25 @@ nomem: return -ENOMEM; } +static int wsm_write_mib_confirm(struct cw1200_common *priv, + struct wsm_mib *arg, + struct wsm_buf *buf) +{ + int ret; + + ret = wsm_generic_confirm(priv, arg, buf); + if (ret) + return ret; + + if (arg->mibId == 0x1006) { + /* OperationalMode: update PM status. */ + const char *p = arg->buf; + cw1200_enable_powersave(priv, + (p[0] & 0x0F) ? true : false); + } + return 0; +} + /* ******************************************************************** */ int wsm_scan(struct cw1200_common *priv, const struct wsm_scan *arg) @@ -1093,11 +1118,15 @@ int wsm_handle_rx(struct cw1200_common *priv, int id, ret = wsm_read_mib_confirm(priv, wsm_arg, &wsm_buf); break; + case 0x0406: + if (likely(wsm_arg)) + ret = wsm_write_mib_confirm(priv, wsm_arg, + &wsm_buf); + break; case 0x040B: if (likely(wsm_arg)) ret = wsm_join_confirm(priv, wsm_arg, &wsm_buf); break; - case 0x0406: /* write_mib */ case 0x0407: /* start-scan */ case 0x0408: /* stop-scan */ case 0x040A: /* wsm_reset */ -- cgit v1.2.3 From 79cda77dc61307241153a37d07430e74138121dc Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Tue, 3 May 2011 22:25:16 +0200 Subject: WLAN: CW1200: Support for HREF/HREFV60/Snowball. 1. Added support for HREF/HREFV60/Snowball platforms. 2. Configuration cleanup. Signed-off-by: Dmitry Tarnyagin --- drivers/staging/cw1200/Kconfig | 13 ------- drivers/staging/cw1200/Makefile | 40 --------------------- drivers/staging/cw1200/ap.c | 14 ++++---- drivers/staging/cw1200/bh.c | 22 ++++++------ drivers/staging/cw1200/cw1200.h | 20 ++--------- drivers/staging/cw1200/cw1200_sdio.c | 69 +++++++++++++++++++++++------------- drivers/staging/cw1200/fwio.c | 2 -- drivers/staging/cw1200/hwio.h | 2 -- drivers/staging/cw1200/main.c | 8 ++--- drivers/staging/cw1200/sta.c | 36 +++++++++---------- drivers/staging/cw1200/txrx.c | 6 ++-- drivers/staging/cw1200/wsm.c | 2 +- 12 files changed, 92 insertions(+), 142 deletions(-) diff --git a/drivers/staging/cw1200/Kconfig b/drivers/staging/cw1200/Kconfig index e24df0069a8..d864fffd1c9 100644 --- a/drivers/staging/cw1200/Kconfig +++ b/drivers/staging/cw1200/Kconfig @@ -12,14 +12,6 @@ config CW1200 if CW1200 -config CW1200_U8500_PLATFORM - bool "U8500 platform support" - depends on CW1200 - select CW1200_NON_POWER_OF_TWO_BLOCKSIZES - help - Say Y if you want to include support for the u8500 platform. - This will add u8500 specific initializing code for cw1200. - config CW1200_NON_POWER_OF_TWO_BLOCKSIZES bool "Platform supports non-power-of-two SDIO transfer" depends on CW1200 @@ -35,11 +27,6 @@ config CW1200_USE_GPIO_IRQ Say Y here if you want to include GPIO IRQ support instead of SDIO IRQ. If unsure, say N. -config CW1200_GPIO_IRQ_NUM - int "GPIO number for the IRQ line" - depends on CW1200_USE_GPIO_IRQ - default 216 - config CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE bool "Software keep-alive (DEVELOPMENT)" depends on CW1200 diff --git a/drivers/staging/cw1200/Makefile b/drivers/staging/cw1200/Makefile index f14637ed297..01e35c2e03e 100644 --- a/drivers/staging/cw1200/Makefile +++ b/drivers/staging/cw1200/Makefile @@ -1,43 +1,3 @@ -ifeq ($(CONFIG_CW1200_U8500_PLATFORM),y) - EXTRA_CFLAGS += -DCW1200_U8500_PLATFORM=1 -endif - -ifeq ($(CONFIG_CW1200_NON_POWER_OF_TWO_BLOCKSIZES),y) - EXTRA_CFLAGS += -DCW1200_NON_POWER_OF_TWO_BLOCKSIZES=1 -endif - -ifeq ($(CONFIG_CW1200_USE_STE_EXTENSIONS),y) - EXTRA_CFLAGS += -DUSE_STE_EXTENSIONS=1 -endif - -ifeq ($(CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE),y) - EXTRA_CFLAGS += -DCW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE=1 -endif - -ifeq ($(CONFIG_CW1200_BH_DEBUG),y) - EXTRA_CFLAGS += -DCW1200_DEBUG_ENABLE_BH_LOGS=1 -endif - -ifeq ($(CONFIG_CW1200_WSM_DEBUG),y) - EXTRA_CFLAGS += -DCW1200_DEBUG_ENABLE_WSM_LOGS=1 -endif - -ifeq ($(CONFIG_CW1200_WSM_DUMPS),y) - EXTRA_CFLAGS += -DCW1200_DEBUG_ENABLE_WSM_DUMPS=1 -endif - -ifeq ($(CONFIG_CW1200_TXRX_DEBUG),y) - EXTRA_CFLAGS += -DCW1200_TXRX_DEBUG=1 -endif - -ifeq ($(CONFIG_CW1200_TX_POLICY_DEBUG),y) - EXTRA_CFLAGS += -DCW1200_TX_POLICY_DEBUG=1 -endif - -ifeq ($(CONFIG_CW1200_STA_DEBUG),y) - EXTRA_CFLAGS += -DCW1200_DEBUG_ENABLE_STA_LOGS -endif - cw1200_core-objs := fwio.o txrx.o main.o queue.o hwio.o bh.o wsm.o sta.o ap.o scan.o cw1200_wlan-objs := cw1200_sdio.o diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 93aee42ab6c..245db3a2e5e 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -14,7 +14,7 @@ #include "ap.h" #include "bh.h" -#ifdef CW1200_DEBUG_ENABLE_STA_LOGS +#if defined(CONFIG_CW1200_STA_DEBUG) #define ap_printk(...) printk(__VA_ARGS__) #else #define ap_printk(...) @@ -249,13 +249,13 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, priv->association_mode.mpduStartSpacing = cw1200_ht_ampdu_density(&priv->ht_info); -#ifdef USE_STE_EXTENSIONS +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) priv->cqm_beacon_loss_count = info->cqm_beacon_miss_thold; priv->cqm_tx_failure_thold = info->cqm_tx_fail_thold; priv->cqm_tx_failure_count = 0; -#endif /* USE_STE_EXTENSIONS */ +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ priv->bss_params.beaconLostCount = priv->cqm_beacon_loss_count ? @@ -348,14 +348,14 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, ap_printk(KERN_DEBUG "[CQM] RSSI threshold subscribe: %d +- %d\n", info->cqm_rssi_thold, info->cqm_rssi_hyst); -#ifdef USE_STE_EXTENSIONS +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) ap_printk(KERN_DEBUG "[CQM] Beacon loss subscribe: %d\n", info->cqm_beacon_miss_thold); ap_printk(KERN_DEBUG "[CQM] TX failure subscribe: %d\n", info->cqm_tx_fail_thold); priv->cqm_rssi_thold = info->cqm_rssi_thold; priv->cqm_rssi_hyst = info->cqm_rssi_hyst; -#endif /* USE_STE_EXTENSIONS */ +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ if (info->cqm_rssi_thold || info->cqm_rssi_hyst) { /* RSSI subscription enabled */ /* TODO: It's not a correct way of setting threshold. @@ -378,7 +378,7 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, } WARN_ON(wsm_set_rcpi_rssi_threshold(priv, &threshold)); -#ifdef USE_STE_EXTENSIONS +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) priv->cqm_tx_failure_thold = info->cqm_tx_fail_thold; priv->cqm_tx_failure_count = 0; @@ -392,7 +392,7 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, priv->cqm_link_loss_count; WARN_ON(wsm_set_bss_params(priv, &priv->bss_params)); } -#endif /* USE_STE_EXTENSIONS */ +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ } mutex_unlock(&priv->conf_mutex); } diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index 286d4cb43b7..b382753b5aa 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -23,7 +23,7 @@ #include "wsm.h" #include "sbus.h" -#ifdef CW1200_DEBUG_ENABLE_BH_LOGS +#if defined(CONFIG_CW1200_BH_DEBUG) #define bh_printk(...) printk(__VA_ARGS__) #else #define bh_printk(...) @@ -283,15 +283,15 @@ rx: BUG_ON(SDIO_BLOCK_SIZE & (SDIO_BLOCK_SIZE - 1)); -#ifdef CW1200_NON_POWER_OF_TWO_BLOCKSIZES +#if defined(CONFIG_CW1200_NON_POWER_OF_TWO_BLOCKSIZES) alloc_len = priv->sbus_ops->align_size( priv->sbus_priv, read_len); -#else +#else /* CONFIG_CW1200_NON_POWER_OF_TWO_BLOCKSIZES */ /* Platform's SDIO workaround */ alloc_len = read_len & ~(SDIO_BLOCK_SIZE - 1); if (read_len & (SDIO_BLOCK_SIZE - 1)) alloc_len += SDIO_BLOCK_SIZE; -#endif +#endif /* CONFIG_CW1200_NON_POWER_OF_TWO_BLOCKSIZES */ skb_rx = cw1200_get_skb(priv, alloc_len); if (WARN_ON(!skb_rx)) @@ -315,10 +315,10 @@ rx: if (WARN_ON(wsm_len > read_len)) break; -#ifdef CW1200_DEBUG_ENABLE_WSM_DUMPS +#if defined(CONFIG_CW1200_WSM_DUMPS) print_hex_dump_bytes("<-- ", DUMP_PREFIX_NONE, data, wsm_len); -#endif /* CW1200_DEBUG_ENABLE_WSM_DUMPS */ +#endif /* CONFIG_CW1200_WSM_DUMPS */ wsm_id = __le32_to_cpu(wsm->id) & 0xFFF; wsm_seq = (__le32_to_cpu(wsm->id) >> 13) & 7; @@ -414,10 +414,10 @@ tx: #endif -#ifdef CW1200_NON_POWER_OF_TWO_BLOCKSIZES +#if defined(CONFIG_CW1200_NON_POWER_OF_TWO_BLOCKSIZES) tx_len = priv->sbus_ops->align_size( priv->sbus_priv, tx_len); -#else +#else /* CONFIG_CW1200_NON_POWER_OF_TWO_BLOCKSIZES */ /* HACK!!! Platform limitation. * It is also supported by upper layer: * there is always enough space at the @@ -426,7 +426,7 @@ tx: tx_len &= ~(SDIO_BLOCK_SIZE - 1); tx_len += SDIO_BLOCK_SIZE; } -#endif +#endif /* CONFIG_CW1200_NON_POWER_OF_TWO_BLOCKSIZES */ wsm->id |= __cpu_to_le32( priv->wsm_tx_seq << 13); @@ -437,10 +437,10 @@ tx: break; } -#ifdef CW1200_DEBUG_ENABLE_WSM_DUMPS +#if defined(CONFIG_CW1200_WSM_DUMPS) print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE, data, __le32_to_cpu(wsm->len)); -#endif /* CW1200_DEBUG_ENABLE_WSM_DUMPS */ +#endif /* CONFIG_CW1200_WSM_DUMPS */ wsm_txed(priv, data); priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & 7; diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 09b3cea1fca..c4e85893c28 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -30,24 +30,10 @@ #include "txrx.h" #include "ht.h" -/* Debug switches */ - -/* Dump all WSM (HI) messages. Quite a lot of logs. */ -/* #define CW1200_DEBUG_ENABLE_WSM_DUMPS */ - -/* Enable WSM logs. Useful for low-level debugging (WSM HI control flow). */ -/* #define CW1200_DEBUG_ENABLE_WSM_LOGS */ - -/* Enable BH logs. Useful for ultra-level debugging (interrupts and so on). */ -/* #define CW1200_DEBUG_ENABLE_BH_LOGS */ - -/* Enable TX/RX logs. */ -/* #define CW1200_TXRX_DEBUG */ - /* extern */ struct sbus_ops; /* extern */ struct task_struct; -#ifdef CW1200_TXRX_DEBUG +#if defined(CONFIG_CW1200_TXRX_DEBUG) #define txrx_printk(...) printk(__VA_ARGS__) #else #define txrx_printk(...) @@ -177,10 +163,10 @@ struct cw1200_common { struct work_struct event_handler; struct delayed_work bss_loss_work; struct delayed_work connection_loss_work; -#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE +#if defined(CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) struct delayed_work keep_alive_work; unsigned long last_activity_time; -#endif +#endif /* CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ struct work_struct tx_failure_work; int delayed_link_loss; diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index ac25e0500d9..d2b63368a00 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -18,6 +18,7 @@ #include #include #include +#include #include @@ -37,12 +38,24 @@ MODULE_PARM_DESC(device, "SDIO interface device is connected to"); #define CW1200_GPIO_LOW (0) #define CW1200_GPIO_HIGH (1) +enum cw1200_plat_t { + cw1200_plat_href = 0, + cw1200_plat_hrefv60, + cw1200_plat_snowball, +}; + +struct cw1200_gpio { + int reset; + int irq; +}; + struct sbus_priv { struct sdio_func *func; struct cw1200_common *core; spinlock_t lock; sbus_irq_handler irq_handler; void *irq_priv; + enum cw1200_plat_t plat; }; static const struct sdio_device_id if_sdio_ids[] = { @@ -50,6 +63,22 @@ static const struct sdio_device_id if_sdio_ids[] = { { /* end: all zeroes */ }, }; +/* TODO: Move to the platform data */ +static const struct cw1200_gpio cw1200_gpio_table[] = { + [cw1200_plat_href] = { + .reset = 215, + .irq = 216, + }, + [cw1200_plat_hrefv60] = { + .reset = 85, + .irq = 4, + }, + [cw1200_plat_snowball] = { + .reset = 215, + .irq = 216, + }, +}; + /* sbus_ops implemetation */ static int cw1200_sdio_memcpy_fromio(struct sbus_priv *self, @@ -109,7 +138,7 @@ static int cw1200_request_irq(struct sbus_priv *self, { int ret; int func_num; - int gpio = CONFIG_CW1200_GPIO_IRQ_NUM; + int gpio = cw1200_gpio_table[self->plat].irq; int irq = gpio_to_irq(gpio); u8 cccr; @@ -194,6 +223,10 @@ static int cw1200_sdio_irq_unsubscribe(struct sbus_priv *self) { int ret = 0; unsigned long flags; +#ifdef CONFIG_CW1200_USE_GPIO_IRQ + int gpio = cw1200_gpio_table[self->plat].irq; + int irq = gpio_to_irq(gpio); +#endif WARN_ON(!self->irq_handler); if (!self->irq_handler) @@ -205,9 +238,9 @@ static int cw1200_sdio_irq_unsubscribe(struct sbus_priv *self) ret = sdio_release_irq(self->func); sdio_release_host(self->func); #else - disable_irq_wake(gpio_to_irq(CONFIG_CW1200_GPIO_IRQ_NUM)); - free_irq(gpio_to_irq(CONFIG_CW1200_GPIO_IRQ_NUM), self); - gpio_free(CONFIG_CW1200_GPIO_IRQ_NUM); + disable_irq_wake(irq); + free_irq(irq, self); + gpio_free(gpio); #endif spin_lock_irqsave(&self->lock, flags); @@ -218,7 +251,6 @@ static int cw1200_sdio_irq_unsubscribe(struct sbus_priv *self) return ret; } -#ifdef CW1200_U8500_PLATFORM static int cw1200_detect_card(void) { /* HACK!!! @@ -286,25 +318,6 @@ static int cw1200_sdio_reset(struct sbus_priv *self) return 0; } -#else /* CW1200_U8500_PLATFORM */ - -static int cw1200_sdio_off(void) -{ - return 0; -} - -static int cw1200_sdio_on(void) -{ - return 0; -} - -static int cw1200_sdio_reset(struct sbus_priv *self) -{ - return 0; -} - -#endif /* CW1200_U8500_PLATFORM */ - static size_t cw1200_align_size(struct sbus_priv *self, size_t size) { return sdio_align_size(self->func, size); @@ -327,6 +340,7 @@ static int cw1200_sdio_probe(struct sdio_func *func, { struct sbus_priv *self; int status; + cw1200_dbg(CW1200_DBG_INIT, "Probe called\n"); self = kzalloc(sizeof(*self), GFP_KERNEL); @@ -335,6 +349,13 @@ static int cw1200_sdio_probe(struct sdio_func *func, return -ENOMEM; } + if (machine_is_snowball()) + self->plat = cw1200_plat_snowball; + else if (machine_is_hrefv60()) + self->plat = cw1200_plat_hrefv60; + else + self->plat = cw1200_plat_href; + spin_lock_init(&self->lock); self->func = func; sdio_set_drvdata(func, self); diff --git a/drivers/staging/cw1200/fwio.c b/drivers/staging/cw1200/fwio.c index a85062e2e3a..e4c9684bc18 100644 --- a/drivers/staging/cw1200/fwio.c +++ b/drivers/staging/cw1200/fwio.c @@ -228,13 +228,11 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) (size_t)DOWNLOAD_BLOCK_SIZE); memcpy(buf, &firmware->data[put], block_size); -#ifdef BUG_POWER_OF_TWO_BLOCKSIZE if (block_size < DOWNLOAD_BLOCK_SIZE) { memset(&buf[block_size], 0, DOWNLOAD_BLOCK_SIZE - block_size); tx_size = DOWNLOAD_BLOCK_SIZE; } -#endif /* BUG_POWER_OF_TWO_BLOCKSIZE */ /* send the block to sram */ ret = cw1200_apb_write(priv, diff --git a/drivers/staging/cw1200/hwio.h b/drivers/staging/cw1200/hwio.h index 59f3ca94eb1..38b5c94edf8 100644 --- a/drivers/staging/cw1200/hwio.h +++ b/drivers/staging/cw1200/hwio.h @@ -19,8 +19,6 @@ /* extern */ struct cw1200_common; -#define BUG_POWER_OF_TWO_BLOCKSIZE - /* DPLL initial values */ #define DPLL_INIT_VAL_9000 (0x00000191) #define DPLL_INIT_VAL_CW1200 (0x0EC4F121) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 676b6264ecc..c3b4c2816cb 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -234,10 +234,10 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) /* IEEE80211_HW_SUPPORTS_UAPSD | */ IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_CQM_RSSI | -#ifdef USE_STE_EXTENSIONS +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) IEEE80211_HW_SUPPORTS_CQM_BEACON_MISS | IEEE80211_HW_SUPPORTS_CQM_TX_FAIL | -#endif +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ IEEE80211_HW_BEACON_FILTER; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | @@ -296,9 +296,9 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work); INIT_DELAYED_WORK(&priv->connection_loss_work, cw1200_connection_loss_work); -#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE +#if defined(CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) INIT_DELAYED_WORK(&priv->keep_alive_work, cw1200_keep_alive_work); -#endif +#endif /* CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ INIT_WORK(&priv->tx_failure_work, cw1200_tx_failure_work); INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work); INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work); diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 0ae58e7bc02..9a99f032d3f 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -18,7 +18,7 @@ #include "fwio.h" #include "bh.h" -#ifdef CW1200_DEBUG_ENABLE_STA_LOGS +#if defined(CONFIG_CW1200_STA_DEBUG) #define sta_printk(...) printk(__VA_ARGS__) #else #define sta_printk(...) @@ -107,9 +107,9 @@ void cw1200_stop(struct ieee80211_hw *dev) cancel_delayed_work_sync(&priv->join_timeout); cancel_delayed_work_sync(&priv->bss_loss_work); cancel_delayed_work_sync(&priv->connection_loss_work); -#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE +#if defined(CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) cancel_delayed_work_sync(&priv->keep_alive_work); -#endif +#endif /* CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ switch (priv->join_status) { case CW1200_JOIN_STATUS_STA: queue_work(priv->workqueue, &priv->unjoin_work); @@ -728,9 +728,9 @@ void cw1200_bss_loss_work(struct work_struct *work) sta_printk(KERN_DEBUG "[CQM] Beacon loss.\n"); if (timeout <= 0) timeout = 0; -#ifdef USE_STE_EXTENSIONS +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) ieee80211_cqm_beacon_miss_notify(priv->vif, GFP_KERNEL); -#endif +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ } else { timeout = 0; } @@ -750,7 +750,7 @@ void cw1200_connection_loss_work(struct work_struct *work) ieee80211_connection_loss(priv->vif); } -#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE +#if defined(CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) void cw1200_keep_alive_work(struct work_struct *work) { struct cw1200_common *priv = @@ -770,18 +770,18 @@ void cw1200_keep_alive_work(struct work_struct *work) queue_delayed_work(priv->workqueue, &priv->keep_alive_work, tmo); } -#endif +#endif /* CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ void cw1200_tx_failure_work(struct work_struct *work) { struct cw1200_common *priv = container_of(work, struct cw1200_common, tx_failure_work); sta_printk(KERN_DEBUG "[CQM] Reporting TX failure.\n"); -#ifdef USE_STE_EXTENSIONS +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) ieee80211_cqm_tx_fail_notify(priv->vif, GFP_KERNEL); -#else +#else /* CONFIG_CW1200_USE_STE_EXTENSIONS */ (void)priv; -#endif +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ } /* ******************************************************************** */ @@ -944,12 +944,12 @@ void cw1200_join_work(struct work_struct *work) * if necessary. */ WARN_ON(wsm_set_block_ack_policy(priv, 0x00, 0x3F)); -#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE +#if defined(CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) priv->last_activity_time = jiffies; /* Queue keep-alive ping avery 30 sec. */ queue_delayed_work(priv->workqueue, &priv->keep_alive_work, 30 * HZ); -#endif +#endif /* CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ /* Queue unjoin if not associated in 3 sec. */ queue_delayed_work(priv->workqueue, &priv->join_timeout, 3 * HZ); @@ -960,14 +960,14 @@ void cw1200_join_work(struct work_struct *work) cw1200_queue_remove(&priv->tx_queue[queueId], priv, __le32_to_cpu(wsm->packetID)); cancel_delayed_work_sync(&priv->join_timeout); -#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE +#if defined(CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) cancel_delayed_work_sync(&priv->keep_alive_work); -#endif +#endif /* CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ } else { WARN_ON(cw1200_upload_keys(priv)); -#ifndef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE +#if !defined(CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) WARN_ON(wsm_keep_alive_period(priv, 30 /* sec */)); -#endif +#endif /* CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ cw1200_queue_requeue(&priv->tx_queue[queueId], __le32_to_cpu(wsm->packetID)); } @@ -1009,9 +1009,9 @@ void cw1200_unjoin_work(struct work_struct *work) cw1200_free_event_queue(priv); cancel_work_sync(&priv->event_handler); cancel_delayed_work_sync(&priv->connection_loss_work); -#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE +#if defined(CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) cancel_delayed_work_sync(&priv->keep_alive_work); -#endif +#endif /* CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ sta_printk(KERN_DEBUG "[STA] Unjoin.\n"); } mutex_unlock(&priv->conf_mutex); diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 5c677556bd9..cf52d18f318 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -15,7 +15,7 @@ #include "wsm.h" #include "bh.h" -#ifdef CW1200_TX_POLICY_DEBUG +#if defined(CONFIG_CW1200_TX_POLICY_DEBUG) #define tx_policy_printk(...) printk(__VA_ARGS__) #else #define tx_policy_printk(...) @@ -506,9 +506,9 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, if (likely(!arg->status)) { tx->flags |= IEEE80211_TX_STAT_ACK; -#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE +#if defined(CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) priv->last_activity_time = jiffies; -#endif +#endif /* CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ priv->cqm_tx_failure_count = 0; ++tx_count; } else { diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index a145ec157f3..3442c71eb25 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -20,7 +20,7 @@ #include "wsm.h" #include "bh.h" -#ifdef CW1200_DEBUG_ENABLE_WSM_LOGS +#if defined(CONFIG_CW1200_WSM_DEBUG) #define wsm_printk(...) printk(__VA_ARGS__) #else #define wsm_printk(...) -- cgit v1.2.3 From 5e4dd0bfb7826b97621983f8f5a617f505b0ad04 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 5 May 2011 19:49:26 +0200 Subject: WLAN: CW1200: Fix for a crash in cfg80211_get_drvinfo() on ifconfig. wiphy_dev was not initialized. Signed-off-by: Dmitry Tarnyagin Change-Id: I799a78d12eee87c518e1613b4cb6a4726df253b2 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/22855 Reviewed-by: Philippe LANGLAIS Tested-by: Philippe LANGLAIS --- drivers/staging/cw1200/main.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index c3b4c2816cb..7365cccf2b4 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -267,10 +267,6 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) hw->wiphy->max_scan_ssids = 2; - /* TODO: You must fill in the @dev member of this structure */ - /* using SET_IEEE80211_DEV() */ - /* SET_IEEE80211_DEV(hw, &pdev->dev); */ - SET_IEEE80211_PERM_ADDR(hw, cw1200_mac_template); if (hw->wiphy->perm_addr[3] == 0 && @@ -409,6 +405,7 @@ int cw1200_probe(const struct sbus_ops *sbus_ops, priv->sbus_ops = sbus_ops; priv->sbus_priv = sbus; priv->pdev = pdev; + SET_IEEE80211_DEV(priv->hw, pdev); /* WSM callbacks. */ priv->wsm_cbc.scan_complete = cw1200_scan_complete_cb; -- cgit v1.2.3 From e9cf90034b582f7213d105f1ceb7871dd819219e Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Sat, 7 May 2011 19:56:44 +0200 Subject: WLAN: CW1200: HT: ampdu_action is implemented. + ampdu_action is implemented. + New configuration option for enabling 11n support (disabled by default). Signed-off-by: Dmitry Tarnyagin Change-Id: I66b873708e8ebe0967b56b5605871402b24ef09a Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/22856 Reviewed-by: Philippe LANGLAIS Tested-by: Philippe LANGLAIS --- drivers/staging/cw1200/Kconfig | 9 +++++++++ drivers/staging/cw1200/ap.c | 32 ++++++++++++++++++++++++++++++++ drivers/staging/cw1200/ap.h | 4 ++++ drivers/staging/cw1200/main.c | 7 +++++++ 4 files changed, 52 insertions(+) diff --git a/drivers/staging/cw1200/Kconfig b/drivers/staging/cw1200/Kconfig index d864fffd1c9..80a7d13682d 100644 --- a/drivers/staging/cw1200/Kconfig +++ b/drivers/staging/cw1200/Kconfig @@ -38,6 +38,15 @@ config CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE If unsure, say N. +config CW1200_HT_SUPPORT + bool "11n support (DEVELOPMENT)" + depends on CW1200 + help + Say Y if you want to enable 11n support in the driver. + Note that 11n support is not 100% verified, status is unknown. + + If unsure, say N. + menu "Driver debug features" depends on CW1200 diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 245db3a2e5e..fbf7ba78a6e 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -422,6 +422,38 @@ void cw1200_multicast_stop_work(struct work_struct *work) wsm_unlock_tx(priv); } +int cw1200_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn) +{ + int ret = 0; + + switch (action) { + /* + * The hw itself takes care of setting up BlockAck mechanisms. + * So, we only have to allow mac80211 to nagotiate a BlockAck + * agreement. Once that is done, the hw will BlockAck incoming + * AMPDUs without further setup. + */ + case IEEE80211_AMPDU_RX_START: + case IEEE80211_AMPDU_RX_STOP: + break; + case IEEE80211_AMPDU_TX_START: + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + case IEEE80211_AMPDU_TX_STOP: + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + break; + default: + ret = -ENOTSUPP; + } + + return ret; +} + /* ******************************************************************** */ /* WSM callback */ void cw1200_suspend_resume(struct cw1200_common *priv, diff --git a/drivers/staging/cw1200/ap.h b/drivers/staging/cw1200/ap.h index 77c364dfc2d..63e6165ed69 100644 --- a/drivers/staging/cw1200/ap.h +++ b/drivers/staging/cw1200/ap.h @@ -25,6 +25,10 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u32 changed); +int cw1200_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn); void cw1200_suspend_resume(struct cw1200_common *priv, struct wsm_suspend_resume *arg); diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 7365cccf2b4..37bcbda9779 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -70,6 +70,7 @@ static struct ieee80211_rate cw1200_rates[] = { RATETAB_ENT(360, 11, 0), RATETAB_ENT(480, 12, 0), RATETAB_ENT(540, 13, 0), +#if defined(CONFIG_CW1200_HT_SUPPORT) RATETAB_ENT(65, 14, 0), RATETAB_ENT(130, 15, 0), RATETAB_ENT(195, 16, 0), @@ -78,6 +79,7 @@ static struct ieee80211_rate cw1200_rates[] = { RATETAB_ENT(520, 19, 0), RATETAB_ENT(585, 20, 0), RATETAB_ENT(650, 21, 0), +#endif /* CONFIG_CW1200_HT_SUPPORT */ }; #define cw1200_a_rates (cw1200_rates + 4) @@ -148,6 +150,7 @@ static struct ieee80211_supported_band cw1200_band_2ghz = { .n_channels = ARRAY_SIZE(cw1200_2ghz_chantable), .bitrates = cw1200_g_rates, .n_bitrates = cw1200_g_rates_size, +#if defined(CONFIG_CW1200_HT_SUPPORT) .ht_cap = { .cap = IEEE80211_HT_CAP_SM_PS | IEEE80211_HT_CAP_GRN_FLD | @@ -166,6 +169,7 @@ static struct ieee80211_supported_band cw1200_band_2ghz = { .tx_params = IEEE80211_HT_MCS_TX_DEFINED, }, }, +#endif /* CONFIG_CW1200_HT_SUPPORT */ }; static struct ieee80211_supported_band cw1200_band_5ghz = { @@ -173,6 +177,7 @@ static struct ieee80211_supported_band cw1200_band_5ghz = { .n_channels = ARRAY_SIZE(cw1200_5ghz_chantable), .bitrates = cw1200_a_rates, .n_bitrates = cw1200_a_rates_size, +#if defined(CONFIG_CW1200_HT_SUPPORT) .ht_cap = { .cap = IEEE80211_HT_CAP_SM_PS | IEEE80211_HT_CAP_GRN_FLD | @@ -191,6 +196,7 @@ static struct ieee80211_supported_band cw1200_band_5ghz = { .tx_params = IEEE80211_HT_MCS_TX_DEFINED, }, }, +#endif /* CONFIG_CW1200_HT_SUPPORT */ }; static const struct ieee80211_ops cw1200_ops = { @@ -211,6 +217,7 @@ static const struct ieee80211_ops cw1200_ops = { .configure_filter = cw1200_configure_filter, .conf_tx = cw1200_conf_tx, .get_stats = cw1200_get_stats, + .ampdu_action = cw1200_ampdu_action, /* .get_tx_stats = cw1200_get_tx_stats */ }; -- cgit v1.2.3 From 2719bb206aebe487c63635de9c375d9675009dc8 Mon Sep 17 00:00:00 2001 From: Vitaly Wool Date: Sun, 8 May 2011 16:36:21 +0200 Subject: cw1200: Set up max_scan_ie_len Drivers that use hardware scan should take care of setting max_scan_ie_len themselves. cw1200 driver should do that as well. Signed-off-by: Vitaly Wool Change-Id: I749a978675bcfb4fd8dbf56c9e52d4d42541a0cc Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/22857 Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Ajit Pal SINGH Reviewed-by: Philippe LANGLAIS Tested-by: Philippe LANGLAIS --- drivers/staging/cw1200/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 37bcbda9779..9cf3a3fba9e 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -273,6 +273,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &cw1200_band_5ghz; hw->wiphy->max_scan_ssids = 2; + hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; SET_IEEE80211_PERM_ADDR(hw, cw1200_mac_template); -- cgit v1.2.3 From a65d5cfa106108c5a9aee0d78d64cab724dcaccd Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 19 May 2011 17:15:11 +0200 Subject: cw1200: Workaround against a bug in Code Sourcery arm2010q1. CS arm2010q1 was producing wrong code (did not preserve registers during inline expansion). Always reproducible crash was observed after device wakeup. Signed-off-by: Dmitry Tarnyagin Change-Id: I13e58db29b3f41d169b2a2956fdaec21a1b62c7a Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23471 Reviewed-by: Philippe LANGLAIS Reviewed-by: Janusz DZIEDZIC --- drivers/staging/cw1200/bh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index b382753b5aa..5f92caa2708 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -156,7 +156,7 @@ static void cw1200_put_skb(struct cw1200_common *priv, struct sk_buff *skb) priv->skb_cache = skb; } -static inline int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv, +static int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv, u16 *ctrl_reg) { int ret; -- cgit v1.2.3 From 38a0554ee66ad32e0d8ef25ac2908fd45978fb38 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Sat, 21 May 2011 01:10:33 +0200 Subject: cw1200: Workaround against a problem with DMA on ux500 platform. ux500 incorrectly handles some DMA sizes. It was observed that multiple of transfers of multiple of 32 bytes are always working. Some other transfer sizes (at least 208 and 228) cause hang in sdio_memcpy_xxx(). The workaround aligns DMA transfer sizes to 32 bytes boundary. Change-Id: I34aad24b4f20ba342cf709cac41c55535b688894 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23601 Reviewed-by: Janusz DZIEDZIC Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/cw1200_sdio.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index d2b63368a00..5c84975b361 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -320,7 +320,14 @@ static int cw1200_sdio_reset(struct sbus_priv *self) static size_t cw1200_align_size(struct sbus_priv *self, size_t size) { - return sdio_align_size(self->func, size); + size_t aligned = sdio_align_size(self->func, size); + /* HACK!!! Problems with DMA size on u8500 platform */ + if ((aligned & 0x1F) && (aligned & ~0x1F)) { + aligned &= ~0x1F; + aligned += 0x20; + } + + return aligned; } static struct sbus_ops cw1200_sdio_sbus_ops = { -- cgit v1.2.3 From 764d14ba5b6920acd08c43af8d1160494cde6617 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 12 Oct 2011 16:22:25 +0200 Subject: cw1200: Integration with mach-ux500 CW1200 platform data is defined and set in arch/arm/mach-ux500. TODO: WLAN regulators are defined but not handled. Waiting for support in mach-ux500. Signed-off-by: Dmitry Tarnyagin Change-Id: If368398af1ca22366bb44c3bb8c7e3b1484cab1b Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23353 Reviewed-by: Janusz DZIEDZIC Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Philippe LANGLAIS --- arch/arm/mach-ux500/board-mop500-wlan.c | 108 +++++++++++++++++++++++++ arch/arm/mach-ux500/board-mop500-wlan.h | 17 ++++ drivers/staging/cw1200/cw1200_plat.h | 19 +++++ drivers/staging/cw1200/cw1200_sdio.c | 135 +++++++++++--------------------- 4 files changed, 190 insertions(+), 89 deletions(-) create mode 100644 arch/arm/mach-ux500/board-mop500-wlan.c create mode 100644 arch/arm/mach-ux500/board-mop500-wlan.h create mode 100644 drivers/staging/cw1200/cw1200_plat.h diff --git a/arch/arm/mach-ux500/board-mop500-wlan.c b/arch/arm/mach-ux500/board-mop500-wlan.c new file mode 100644 index 00000000000..c24069600ca --- /dev/null +++ b/arch/arm/mach-ux500/board-mop500-wlan.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Dmitry Tarnyagin + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include "../drivers/staging/cw1200/cw1200_plat.h" + +static void cw1200_release(struct device *dev); + +static struct resource cw1200_href_resources[] = { + { + .start = 215, + .end = 215, + .flags = IORESOURCE_IO, + .name = "cw1200_reset", + }, +#ifdef CONFIG_CW1200_USE_GPIO_IRQ + { + .start = NOMADIK_GPIO_TO_IRQ(216), + .end = NOMADIK_GPIO_TO_IRQ(216), + .flags = IORESOURCE_IRQ, + .name = "cw1200_irq", + }, +#endif /* CONFIG_CW1200_USE_GPIO_IRQ */ +}; + +static struct resource cw1200_href60_resources[] = { + { + .start = 85, + .end = 85, + .flags = IORESOURCE_IO, + .name = "cw1200_reset", + }, +#ifdef CONFIG_CW1200_USE_GPIO_IRQ + { + .start = NOMADIK_GPIO_TO_IRQ(4), + .end = NOMADIK_GPIO_TO_IRQ(4), + .flags = IORESOURCE_IRQ, + .name = "cw1200_irq", + }, +#endif /* CONFIG_CW1200_USE_GPIO_IRQ */ +}; + +static struct cw1200_platform_data cw1200_platform_data = { + .regulator_vdd = "vdd", + .regulator_vio = "vio", +}; + +static struct platform_device cw1200_device = { + .name = "cw1200", + .dev = { + .platform_data = &cw1200_platform_data, + .release = cw1200_release, + }, +}; + +const struct cw1200_platform_data *cw1200_get_platform_data(void) +{ + return &cw1200_platform_data; +} +EXPORT_SYMBOL_GPL(cw1200_get_platform_data); + +int __init mop500_wlan_init(void) +{ + if (machine_is_snowball() || + machine_is_u8500() || + machine_is_u5500() || + machine_is_nomadik()) { + cw1200_device.num_resources = + ARRAY_SIZE(cw1200_href_resources); + cw1200_device.resource = cw1200_href_resources; + } else if (machine_is_hrefv60()) { + cw1200_device.num_resources = + ARRAY_SIZE(cw1200_href60_resources); + cw1200_device.resource = cw1200_href60_resources; + } else { + dev_err(&cw1200_device.dev, + "Unsupported mach type %d " + "(check mach-types.h)\n", + __machine_arch_type); + return -ENOTSUPP; + } + + if (machine_is_snowball()) + cw1200_platform_data.mmc_id = "mmc2"; + else + cw1200_platform_data.mmc_id = "mmc3"; + + cw1200_platform_data.reset = &cw1200_device.resource[0]; +#ifdef CONFIG_CW1200_USE_GPIO_IRQ + cw1200_platform_data.irq = &cw1200_device.resource[1]; +#endif /* #ifdef CONFIG_CW1200_USE_GPIO_IRQ */ + + cw1200_device.dev.release = cw1200_release; + + return platform_device_register(&cw1200_device); +} + +static void cw1200_release(struct device *dev) +{ + /* Do nothing: release is handled by SDIO stack */ +} diff --git a/arch/arm/mach-ux500/board-mop500-wlan.h b/arch/arm/mach-ux500/board-mop500-wlan.h new file mode 100644 index 00000000000..c6788adc46f --- /dev/null +++ b/arch/arm/mach-ux500/board-mop500-wlan.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * License terms: GNU General Public License (GPL), version 2 + * + * U8500 board specific cw1200 (WLAN device) initialization. + * + * Author: Dmitry Tarnyagin + * + */ + +#ifndef __BOARD_MOP500_WLAN_H +#define __BOARD_MOP500_WLAN_H + +int mop500_wlan_init(void); + +#endif diff --git a/drivers/staging/cw1200/cw1200_plat.h b/drivers/staging/cw1200/cw1200_plat.h new file mode 100644 index 00000000000..3b6acaff305 --- /dev/null +++ b/drivers/staging/cw1200/cw1200_plat.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Dmitry Tarnyagin + * License terms: GNU General Public License (GPL) version 2 + */ + +#include + +struct cw1200_platform_data { + const char *regulator_vdd; + const char *regulator_vio; + const char *mmc_id; + const struct resource *irq; + const struct resource *reset; +}; + +/* Declaration only. Should be implemented in arch/xxx/mach-yyy */ +const struct cw1200_platform_data *cw1200_get_platform_data(void); diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index 5c84975b361..2b7d0a1eaf7 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -19,43 +19,24 @@ #include #include #include - #include #include "cw1200.h" #include "sbus.h" +#include "cw1200_plat.h" MODULE_AUTHOR("Dmitry Tarnyagin "); MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SDIO driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("cw1200_wlan"); - -/* Module parameter for MMC interface to probe the driver on. */ -static char device_name[10] = "mmc3"; -module_param_string(device, device_name, sizeof(device_name), S_IRUGO); -MODULE_PARM_DESC(device, "SDIO interface device is connected to"); - -#define CW1200_GPIO_LOW (0) -#define CW1200_GPIO_HIGH (1) - -enum cw1200_plat_t { - cw1200_plat_href = 0, - cw1200_plat_hrefv60, - cw1200_plat_snowball, -}; - -struct cw1200_gpio { - int reset; - int irq; -}; +MODULE_ALIAS("cw1200"); struct sbus_priv { struct sdio_func *func; struct cw1200_common *core; + const struct cw1200_platform_data *pdata; spinlock_t lock; sbus_irq_handler irq_handler; void *irq_priv; - enum cw1200_plat_t plat; }; static const struct sdio_device_id if_sdio_ids[] = { @@ -63,22 +44,6 @@ static const struct sdio_device_id if_sdio_ids[] = { { /* end: all zeroes */ }, }; -/* TODO: Move to the platform data */ -static const struct cw1200_gpio cw1200_gpio_table[] = { - [cw1200_plat_href] = { - .reset = 215, - .irq = 216, - }, - [cw1200_plat_hrefv60] = { - .reset = 85, - .irq = 4, - }, - [cw1200_plat_snowball] = { - .reset = 215, - .irq = 216, - }, -}; - /* sbus_ops implemetation */ static int cw1200_sdio_memcpy_fromio(struct sbus_priv *self, @@ -138,24 +103,15 @@ static int cw1200_request_irq(struct sbus_priv *self, { int ret; int func_num; - int gpio = cw1200_gpio_table[self->plat].irq; - int irq = gpio_to_irq(gpio); + const struct resource *irq = self->pdata->irq; u8 cccr; - ret = gpio_request(gpio, "cw1200_irq"); - if (WARN_ON(ret)) - goto exit; - - ret = gpio_direction_input(gpio); - if (WARN_ON(ret)) - goto free_gpio; - - ret = request_any_context_irq(irq, handler, - IRQF_TRIGGER_RISING, "cw1200_irq", self); + ret = request_any_context_irq(irq->start, handler, + IRQF_TRIGGER_RISING, irq->name, self); if (WARN_ON(ret < 0)) - goto free_gpio; + goto exit; - ret = enable_irq_wake(irq); + ret = enable_irq_wake(irq->start); if (WARN_ON(ret)) goto free_irq; @@ -183,11 +139,9 @@ static int cw1200_request_irq(struct sbus_priv *self, set_func: self->func->num = func_num; - disable_irq_wake(irq); + disable_irq_wake(irq->start); free_irq: - free_irq(irq, self); -free_gpio: - gpio_free(gpio); + free_irq(irq->start, self); exit: return ret; } @@ -224,8 +178,7 @@ static int cw1200_sdio_irq_unsubscribe(struct sbus_priv *self) int ret = 0; unsigned long flags; #ifdef CONFIG_CW1200_USE_GPIO_IRQ - int gpio = cw1200_gpio_table[self->plat].irq; - int irq = gpio_to_irq(gpio); + const struct resource *irq = self->pdata->irq; #endif WARN_ON(!self->irq_handler); @@ -238,9 +191,8 @@ static int cw1200_sdio_irq_unsubscribe(struct sbus_priv *self) ret = sdio_release_irq(self->func); sdio_release_host(self->func); #else - disable_irq_wake(irq); - free_irq(irq, self); - gpio_free(gpio); + disable_irq_wake(irq->start); + free_irq(irq->start, self); #endif spin_lock_irqsave(&self->lock, flags); @@ -251,7 +203,7 @@ static int cw1200_sdio_irq_unsubscribe(struct sbus_priv *self) return ret; } -static int cw1200_detect_card(void) +static int cw1200_detect_card(const struct cw1200_platform_data *pdata) { /* HACK!!! * Rely on mmc->class_dev.class set in mmc_alloc_host @@ -273,8 +225,8 @@ static int cw1200_detect_card(void) for (;;) { dev = class_dev_iter_next(&iter); if (!dev) { - printk(KERN_ERR "CW1200: %s is not found.\n", - device_name); + printk(KERN_ERR "cw1200: %s is not found.\n", + pdata->mmc_id); break; } else { struct mmc_host *host = container_of(dev, @@ -282,7 +234,7 @@ static int cw1200_detect_card(void) if (dev_name(&host->class_dev) && strcmp(dev_name(&host->class_dev), - device_name)) + pdata->mmc_id)) continue; mmc_detect_change(host, 10); @@ -293,28 +245,30 @@ static int cw1200_detect_card(void) return 0; } -static int cw1200_sdio_off(void) +static int cw1200_sdio_off(const struct cw1200_platform_data *pdata) { - gpio_set_value(215, CW1200_GPIO_LOW); - cw1200_detect_card(); - gpio_free(215); + const struct resource *reset = pdata->reset; + gpio_set_value(reset->start, 0); + cw1200_detect_card(pdata); + gpio_free(reset->start); return 0; } -static int cw1200_sdio_on(void) +static int cw1200_sdio_on(const struct cw1200_platform_data *pdata) { - gpio_request(215, "cw1200_sdio"); - gpio_direction_output(215, 1); - gpio_set_value(215, CW1200_GPIO_HIGH); - cw1200_detect_card(); + const struct resource *reset = pdata->reset; + gpio_request(reset->start, reset->name); + gpio_direction_output(reset->start, 1); + gpio_set_value(reset->start, 1); + cw1200_detect_card(pdata); return 0; } static int cw1200_sdio_reset(struct sbus_priv *self) { - cw1200_sdio_off(); + cw1200_sdio_off(self->pdata); mdelay(1000); - cw1200_sdio_on(); + cw1200_sdio_on(self->pdata); return 0; } @@ -356,14 +310,8 @@ static int cw1200_sdio_probe(struct sdio_func *func, return -ENOMEM; } - if (machine_is_snowball()) - self->plat = cw1200_plat_snowball; - else if (machine_is_hrefv60()) - self->plat = cw1200_plat_hrefv60; - else - self->plat = cw1200_plat_href; - spin_lock_init(&self->lock); + self->pdata = cw1200_get_platform_data(); self->func = func; sdio_set_drvdata(func, self); sdio_claim_host(func); @@ -402,7 +350,7 @@ static void cw1200_sdio_disconnect(struct sdio_func *func) } static struct sdio_driver sdio_driver = { - .name = "cw1200_wlan", + .name = "cw1200", .id_table = if_sdio_ids, .probe = cw1200_sdio_probe, .remove = cw1200_sdio_disconnect, @@ -411,12 +359,21 @@ static struct sdio_driver sdio_driver = { /* Init Module function -> Called by insmod */ static int __init cw1200_sdio_init(void) { - int ret = sdio_register_driver(&sdio_driver); + int ret; + + ret = sdio_register_driver(&sdio_driver); if (ret) - return ret; - ret = cw1200_sdio_on(); + goto err_reg; + + ret = cw1200_sdio_on(cw1200_get_platform_data()); if (ret) - sdio_unregister_driver(&sdio_driver); + goto err_on; + + return 0; + +err_on: + sdio_unregister_driver(&sdio_driver); +err_reg: return ret; } @@ -424,7 +381,7 @@ static int __init cw1200_sdio_init(void) static void __exit cw1200_sdio_exit(void) { sdio_unregister_driver(&sdio_driver); - cw1200_sdio_off(); + cw1200_sdio_off(cw1200_get_platform_data()); } -- cgit v1.2.3 From 946f99cf8dc597e22b071121ba68e140476abef7 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 19 May 2011 22:47:37 +0200 Subject: cw1200: Spin lock usage review. Expensive spin_lock_irqsave calls are replaced by more adequate spin_lock / spin_lock_bh. Locking strategy: queue: spin_lock_bh event_queue: spin_lock tx_policy: spin_lock_bh wsm_cmd: spin_lock sdio handlers: spin_lock_irqsave Signed-off-by: Dmitry Tarnyagin Change-Id: I3a13ef337ac125c0533de00651e54a4b4a45ba16 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23603 Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/queue.c | 58 ++++++++++++++++-------------------------- drivers/staging/cw1200/sta.c | 20 ++++++--------- drivers/staging/cw1200/txrx.c | 20 ++++++--------- drivers/staging/cw1200/wsm.c | 37 ++++++++++++--------------- 4 files changed, 54 insertions(+), 81 deletions(-) diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c index f21ff8f2f99..3adf4fc3072 100644 --- a/drivers/staging/cw1200/queue.c +++ b/drivers/staging/cw1200/queue.c @@ -106,9 +106,7 @@ int cw1200_queue_init(struct cw1200_queue *queue, u8 queue_id, int cw1200_queue_clear(struct cw1200_queue *queue) { - unsigned long flags; - - spin_lock_irqsave(&queue->lock, flags); + spin_lock_bh(&queue->lock); queue->generation++; list_splice_tail_init(&queue->queue, &queue->pending); while (!list_empty(&queue->pending)) { @@ -125,7 +123,7 @@ int cw1200_queue_clear(struct cw1200_queue *queue) queue->num_pending = 0; queue->num_sent = 0; memset(queue->link_map_cache, 0, sizeof(int[queue->map_capacity])); - spin_unlock_irqrestore(&queue->lock, flags); + spin_unlock_bh(&queue->lock); return 0; } @@ -144,14 +142,13 @@ int cw1200_queue_deinit(struct cw1200_queue *queue) size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, u32 allowed_mask) { - unsigned long flags; size_t ret; int i, bit; if (!allowed_mask) return 0; - spin_lock_irqsave(&queue->lock, flags); + spin_lock_bh(&queue->lock); if (likely(allowed_mask == (u32) -1)) ret = queue->num_queued - queue->num_pending; else { @@ -161,7 +158,7 @@ size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, ret += queue->link_map_cache[i]; } } - spin_unlock_irqrestore(&queue->lock, flags); + spin_unlock_bh(&queue->lock); return ret; } @@ -169,7 +166,6 @@ int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, struct sk_buff *skb, u8 link_id) { int ret; - unsigned long flags; struct wsm_tx *wsm; wsm = (struct wsm_tx *)skb_push(skb, sizeof(struct wsm_tx)); @@ -180,7 +176,7 @@ int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, if (link_id >= queue->map_capacity) return -EINVAL; - spin_lock_irqsave(&queue->lock, flags); + spin_lock_bh(&queue->lock); if (!WARN_ON(list_empty(&queue->free_pool))) { struct cw1200_queue_item *item = list_first_entry( &queue->free_pool, struct cw1200_queue_item, head); @@ -204,7 +200,7 @@ int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, } else { ret = -ENOENT; } - spin_unlock_irqrestore(&queue->lock, flags); + spin_unlock_bh(&queue->lock); return ret; } @@ -214,10 +210,9 @@ int cw1200_queue_get(struct cw1200_queue *queue, struct ieee80211_tx_info **tx_info) { int ret = -ENOENT; - unsigned long flags; struct cw1200_queue_item *item; - spin_lock_irqsave(&queue->lock, flags); + spin_lock_bh(&queue->lock); list_for_each_entry(item, &queue->queue, head) { if (allowed_mask & (1 << item->link_id)) { ret = 0; @@ -232,14 +227,13 @@ int cw1200_queue_get(struct cw1200_queue *queue, ++queue->num_pending; --queue->link_map_cache[item->link_id]; } - spin_unlock_irqrestore(&queue->lock, flags); + spin_unlock_bh(&queue->lock); return ret; } int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID) { int ret = 0; - unsigned long flags; u8 queue_generation, queue_id, item_generation, item_id; struct cw1200_queue_item *item; cw1200_queue_parse_id(packetID, &queue_generation, &queue_id, @@ -247,7 +241,7 @@ int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID) item = &queue->pool[item_id]; - spin_lock_irqsave(&queue->lock, flags); + spin_lock_bh(&queue->lock); BUG_ON(queue_id != queue->queue_id); if (unlikely(queue_generation != queue->generation)) { ret = -ENOENT; @@ -267,15 +261,13 @@ int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID) wsm->packetID = __cpu_to_le32(item->packetID); list_move_tail(&item->head, &queue->queue); } - spin_unlock_irqrestore(&queue->lock, flags); + spin_unlock_bh(&queue->lock); return ret; } int cw1200_queue_requeue_all(struct cw1200_queue *queue) { - unsigned long flags; - - spin_lock_irqsave(&queue->lock, flags); + spin_lock_bh(&queue->lock); while (!list_empty(&queue->pending)) { struct cw1200_queue_item *item = list_first_entry( &queue->pending, struct cw1200_queue_item, head); @@ -290,7 +282,7 @@ int cw1200_queue_requeue_all(struct cw1200_queue *queue) wsm->packetID = __cpu_to_le32(item->packetID); list_move_tail(&item->head, &queue->queue); } - spin_unlock_irqrestore(&queue->lock, flags); + spin_unlock_bh(&queue->lock); return 0; } @@ -299,7 +291,6 @@ int cw1200_queue_remove(struct cw1200_queue *queue, struct cw1200_common *priv, u32 packetID) { int ret = 0; - unsigned long flags; u8 queue_generation, queue_id, item_generation, item_id; struct cw1200_queue_item *item; struct sk_buff *skb_to_free = NULL; @@ -308,7 +299,7 @@ int cw1200_queue_remove(struct cw1200_queue *queue, struct cw1200_common *priv, item = &queue->pool[item_id]; - spin_lock_irqsave(&queue->lock, flags); + spin_lock_bh(&queue->lock); BUG_ON(queue_id != queue->queue_id); if (unlikely(queue_generation != queue->generation)) { ret = -ENOENT; @@ -336,7 +327,7 @@ int cw1200_queue_remove(struct cw1200_queue *queue, struct cw1200_common *priv, __cw1200_queue_unlock(queue, priv); } } - spin_unlock_irqrestore(&queue->lock, flags); + spin_unlock_bh(&queue->lock); if (skb_to_free) dev_kfree_skb_any(item->skb); @@ -348,7 +339,6 @@ int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, struct sk_buff **skb) { int ret = 0; - unsigned long flags; u8 queue_generation, queue_id, item_generation, item_id; struct cw1200_queue_item *item; cw1200_queue_parse_id(packetID, &queue_generation, &queue_id, @@ -356,7 +346,7 @@ int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, item = &queue->pool[item_id]; - spin_lock_irqsave(&queue->lock, flags); + spin_lock_bh(&queue->lock); BUG_ON(queue_id != queue->queue_id); if (unlikely(queue_generation != queue->generation)) { ret = -ENOENT; @@ -370,38 +360,34 @@ int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, *skb = item->skb; item->skb = NULL; } - spin_unlock_irqrestore(&queue->lock, flags); + spin_unlock_bh(&queue->lock); return ret; } void cw1200_queue_lock(struct cw1200_queue *queue, struct cw1200_common *cw1200) { - unsigned long flags; - spin_lock_irqsave(&queue->lock, flags); + spin_lock_bh(&queue->lock); __cw1200_queue_lock(queue, cw1200); - spin_unlock_irqrestore(&queue->lock, flags); + spin_unlock_bh(&queue->lock); } void cw1200_queue_unlock(struct cw1200_queue *queue, struct cw1200_common *cw1200) { - unsigned long flags; - spin_lock_irqsave(&queue->lock, flags); + spin_lock_bh(&queue->lock); __cw1200_queue_unlock(queue, cw1200); - spin_unlock_irqrestore(&queue->lock, flags); + spin_unlock_bh(&queue->lock); } /* int cw1200_queue_get_stats(struct cw1200_queue *queue, struct ieee80211_tx_queue_stats *stats) { - unsigned long flags; - - spin_lock_irqsave(&queue->lock, flags); + spin_lock_bh(&queue->lock); stats->len = queue->num_queued; stats->limit = queue->capacity; stats->count = queue->num_sent; - spin_unlock_irqrestore(&queue->lock, flags); + spin_unlock_bh(&queue->lock); return 0; } diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 9a99f032d3f..6cebf530f40 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -80,7 +80,6 @@ out: void cw1200_stop(struct ieee80211_hw *dev) { struct cw1200_common *priv = dev->priv; - unsigned long flags; LIST_HEAD(list); int i; @@ -132,9 +131,9 @@ void cw1200_stop(struct ieee80211_hw *dev) priv->softled_state = 0; /* cw1200_set_leds(priv); */ - spin_lock_irqsave(&priv->event_queue_lock, flags); + spin_lock(&priv->event_queue_lock); list_splice_init(&priv->event_queue, &list); - spin_unlock_irqrestore(&priv->event_queue_lock, flags); + spin_unlock(&priv->event_queue_lock); __cw1200_free_event_queue(&list); priv->delayed_link_loss = 0; @@ -294,18 +293,17 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) } if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) { - unsigned long flags; sta_printk(KERN_DEBUG "[STA] Retry limits: %d (long), " \ "%d (short).\n", conf->long_frame_max_tx_count, conf->short_frame_max_tx_count); - spin_lock_irqsave(&priv->tx_policy_cache.lock, flags); + spin_lock_bh(&priv->tx_policy_cache.lock); priv->long_frame_max_tx_count = conf->long_frame_max_tx_count; priv->short_frame_max_tx_count = (conf->short_frame_max_tx_count < 0x0F) ? conf->short_frame_max_tx_count : 0x0F; priv->hw->max_rate_tries = priv->short_frame_max_tx_count; - spin_unlock_irqrestore(&priv->tx_policy_cache.lock, flags); + spin_unlock_bh(&priv->tx_policy_cache.lock); /* TBD: I think we don't need tx_policy_force_upload(). * Outdated policies will leave cache in a normal way. */ /* WARN_ON(tx_policy_force_upload(priv)); */ @@ -634,27 +632,25 @@ void cw1200_rx_cb(struct cw1200_common *priv, void cw1200_free_event_queue(struct cw1200_common *priv) { - unsigned long flags; LIST_HEAD(list); - spin_lock_irqsave(&priv->event_queue_lock, flags); + spin_lock(&priv->event_queue_lock); list_splice_init(&priv->event_queue, &list); - spin_unlock_irqrestore(&priv->event_queue_lock, flags); + spin_unlock(&priv->event_queue_lock); __cw1200_free_event_queue(&list); } void cw1200_event_handler(struct work_struct *work) { - unsigned long flags; struct cw1200_common *priv = container_of(work, struct cw1200_common, event_handler); struct cw1200_wsm_event *event; LIST_HEAD(list); - spin_lock_irqsave(&priv->event_queue_lock, flags); + spin_lock(&priv->event_queue_lock); list_splice_init(&priv->event_queue, &list); - spin_unlock_irqrestore(&priv->event_queue_lock, flags); + spin_unlock(&priv->event_queue_lock); list_for_each_entry(event, &list, link) { switch (event->evt.eventId) { diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index cf52d18f318..5f3c859fb9e 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -205,11 +205,10 @@ static int tx_policy_get(struct cw1200_common *priv, int idx; struct tx_policy_cache *cache = &priv->tx_policy_cache; struct tx_policy wanted; - unsigned long flags; tx_policy_build(priv, &wanted, rates, count); - spin_lock_irqsave(&cache->lock, flags); + spin_lock_bh(&cache->lock); BUG_ON(list_empty(&cache->free)); idx = tx_policy_find(cache, &wanted); if (idx >= 0) { @@ -234,7 +233,7 @@ static int tx_policy_get(struct cw1200_common *priv, /* Lock TX queues. */ cw1200_tx_queues_lock(priv); } - spin_unlock_irqrestore(&cache->lock, flags); + spin_unlock_bh(&cache->lock); return idx; } @@ -242,27 +241,25 @@ void tx_policy_put(struct cw1200_common *priv, int idx) { int usage, locked; struct tx_policy_cache *cache = &priv->tx_policy_cache; - unsigned long flags; - spin_lock_irqsave(&cache->lock, flags); + spin_lock_bh(&cache->lock); locked = list_empty(&cache->free); usage = tx_policy_release(cache, &cache->cache[idx]); if (unlikely(locked) && !usage) { /* Unlock TX queues. */ cw1200_tx_queues_unlock(priv); } - spin_unlock_irqrestore(&cache->lock, flags); + spin_unlock_bh(&cache->lock); } /* bool tx_policy_cache_full(struct cw1200_common *priv) { bool ret; - unsigned long flags; struct tx_policy_cache *cache = &priv->tx_policy_cache; - spin_lock_irqsave(&cache->lock, flags); + spin_lock_bh(&cache->lock); ret = list_empty(&cache->free); - spin_unlock_irqrestore(&cache->lock, flags); + spin_unlock_bh(&cache->lock); return ret; } */ @@ -270,14 +267,13 @@ bool tx_policy_cache_full(struct cw1200_common *priv) static int tx_policy_upload(struct cw1200_common *priv) { struct tx_policy_cache *cache = &priv->tx_policy_cache; - unsigned long flags; int i; struct wsm_set_tx_rate_retry_policy arg = { .hdr = { .numTxRatePolicies = 0, } }; - spin_lock_irqsave(&cache->lock, flags); + spin_lock_bh(&cache->lock); /* Upload only modified entries. */ for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) { @@ -302,7 +298,7 @@ static int tx_policy_upload(struct cw1200_common *priv) ++arg.hdr.numTxRatePolicies; } } - spin_unlock_irqrestore(&cache->lock, flags); + spin_unlock_bh(&cache->lock); tx_policy_printk(KERN_DEBUG "[TX policy] Upload %d policies\n", arg.hdr.numTxRatePolicies); return wsm_set_tx_rate_retry_policy(priv, &arg); diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 3442c71eb25..2a07e7e93f8 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -834,7 +834,6 @@ underflow: static int wsm_event_indication(struct cw1200_common *priv, struct wsm_buf *buf) { int first; - unsigned long flags; struct cw1200_wsm_event *event; if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { @@ -850,10 +849,10 @@ static int wsm_event_indication(struct cw1200_common *priv, struct wsm_buf *buf) wsm_printk(KERN_DEBUG "[WSM] Event: %d(%d)\n", event->evt.eventId, event->evt.eventData); - spin_lock_irqsave(&priv->event_queue_lock, flags); + spin_lock(&priv->event_queue_lock); first = list_empty(&priv->event_queue); list_add_tail(&event->link, &priv->event_queue); - spin_unlock_irqrestore(&priv->event_queue_lock, flags); + spin_unlock(&priv->event_queue_lock); if (first) queue_work(priv->workqueue, &priv->event_handler); @@ -941,7 +940,6 @@ int wsm_cmd_send(struct cw1200_common *priv, struct wsm_buf *buf, void *arg, u16 cmd, long tmo) { - unsigned long flags; size_t buf_len = buf->data - buf->begin; int ret; @@ -957,14 +955,14 @@ int wsm_cmd_send(struct cw1200_common *priv, ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len); ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd); - spin_lock_irqsave(&priv->wsm_cmd.lock, flags); + spin_lock(&priv->wsm_cmd.lock); BUG_ON(priv->wsm_cmd.ptr); priv->wsm_cmd.done = 0; priv->wsm_cmd.ptr = buf->begin; priv->wsm_cmd.len = buf_len; priv->wsm_cmd.arg = arg; priv->wsm_cmd.cmd = cmd; - spin_unlock_irqrestore(&priv->wsm_cmd.lock, flags); + spin_unlock(&priv->wsm_cmd.lock); cw1200_bh_wakeup(priv); @@ -991,11 +989,11 @@ int wsm_cmd_send(struct cw1200_common *priv, if (unlikely(ret == 0)) { u16 raceCheck; - spin_lock_irqsave(&priv->wsm_cmd.lock, flags); + spin_lock(&priv->wsm_cmd.lock); raceCheck = priv->wsm_cmd.cmd; priv->wsm_cmd.arg = NULL; priv->wsm_cmd.ptr = NULL; - spin_unlock_irqrestore(&priv->wsm_cmd.lock, flags); + spin_unlock(&priv->wsm_cmd.lock); /* Race condition check to make sure _confirm is not called * after exit of _send */ @@ -1009,10 +1007,10 @@ int wsm_cmd_send(struct cw1200_common *priv, } ret = -ETIMEDOUT; } else { - spin_lock_irqsave(&priv->wsm_cmd.lock, flags); + spin_lock(&priv->wsm_cmd.lock); BUG_ON(!priv->wsm_cmd.done); ret = priv->wsm_cmd.ret; - spin_unlock_irqrestore(&priv->wsm_cmd.lock, flags); + spin_unlock(&priv->wsm_cmd.lock); } wsm_buf_reset(buf); return ret; @@ -1070,7 +1068,6 @@ int wsm_handle_rx(struct cw1200_common *priv, int id, struct wsm_hdr *wsm, struct sk_buff **skb_p) { int ret = 0; - unsigned long flags; struct wsm_buf wsm_buf; int link_id = (id >> 6) & 0x0F; @@ -1093,11 +1090,11 @@ int wsm_handle_rx(struct cw1200_common *priv, int id, /* Do not trust FW too much. Protection against repeated * response and race condition removal (see above). */ - spin_lock_irqsave(&priv->wsm_cmd.lock, flags); + spin_lock(&priv->wsm_cmd.lock); wsm_arg = priv->wsm_cmd.arg; wsm_cmd = priv->wsm_cmd.cmd & ~(0x0F << 6); priv->wsm_cmd.cmd = 0xFFFF; - spin_unlock_irqrestore(&priv->wsm_cmd.lock, flags); + spin_unlock(&priv->wsm_cmd.lock); if (WARN_ON((id & ~0x0400) != wsm_cmd)) { /* Note that any non-zero is a fatal retcode. */ @@ -1154,10 +1151,10 @@ int wsm_handle_rx(struct cw1200_common *priv, int id, BUG_ON(1); } - spin_lock_irqsave(&priv->wsm_cmd.lock, flags); + spin_lock(&priv->wsm_cmd.lock); priv->wsm_cmd.ret = ret; priv->wsm_cmd.done = 1; - spin_unlock_irqrestore(&priv->wsm_cmd.lock, flags); + spin_unlock(&priv->wsm_cmd.lock); ret = 0; /* Error response from device should ne stop BH. */ wake_up_interruptible(&priv->wsm_cmd_wq); @@ -1418,7 +1415,6 @@ found: int wsm_get_tx(struct cw1200_common *priv, u8 **data, size_t *tx_len) { - unsigned long flags; struct wsm_tx *wsm = NULL; struct ieee80211_tx_info *tx_info; struct cw1200_queue *queue; @@ -1437,11 +1433,11 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, if (priv->wsm_cmd.ptr) { ++count; - spin_lock_irqsave(&priv->wsm_cmd.lock, flags); + spin_lock(&priv->wsm_cmd.lock); BUG_ON(!priv->wsm_cmd.ptr); *data = priv->wsm_cmd.ptr; *tx_len = priv->wsm_cmd.len; - spin_unlock_irqrestore(&priv->wsm_cmd.lock, flags); + spin_unlock(&priv->wsm_cmd.lock); } else { for (;;) { if (atomic_add_return(0, &priv->tx_lock)) @@ -1498,11 +1494,10 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, void wsm_txed(struct cw1200_common *priv, u8 *data) { - unsigned long flags; if (data == priv->wsm_cmd.ptr) { - spin_lock_irqsave(&priv->wsm_cmd.lock, flags); + spin_lock(&priv->wsm_cmd.lock); priv->wsm_cmd.ptr = NULL; - spin_unlock_irqrestore(&priv->wsm_cmd.lock, flags); + spin_unlock(&priv->wsm_cmd.lock); } /* TODO: data queues */ -- cgit v1.2.3 From ec3237330965118fcaf542befb965bb051f7cab1 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Mon, 16 May 2011 09:41:37 +0200 Subject: wlan: cw1200: .flush mac80211 API is implemented. - .flush mac80211 API is implemented (expected to be called from mac80211 off_channel code). - TX queue statistics is implemented. Signed-off-by: Dmitry Tarnyagin Change-Id: If228ceccd856b699236999122c9989d0eca1e98d Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23604 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/cw1200.h | 1 + drivers/staging/cw1200/main.c | 24 ++++++-- drivers/staging/cw1200/queue.c | 130 +++++++++++++++++++++++++++++++++------- drivers/staging/cw1200/queue.h | 30 ++++++++-- drivers/staging/cw1200/sta.c | 68 +++++++++++++++++++-- drivers/staging/cw1200/sta.h | 4 +- drivers/staging/cw1200/wsm.c | 2 - 7 files changed, 221 insertions(+), 38 deletions(-) diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index c4e85893c28..248ca79a404 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -52,6 +52,7 @@ enum cw1200_join_status { struct cw1200_common { struct cw1200_queue tx_queue[4]; + struct cw1200_queue_stats tx_queue_stats; struct ieee80211_hw *hw; struct ieee80211_vif *vif; diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 9cf3a3fba9e..a09832c7343 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -218,7 +218,11 @@ static const struct ieee80211_ops cw1200_ops = { .conf_tx = cw1200_conf_tx, .get_stats = cw1200_get_stats, .ampdu_action = cw1200_ampdu_action, - /* .get_tx_stats = cw1200_get_tx_stats */ + .flush = cw1200_flush, + /* Intentionally not offloaded: */ + /*.channel_switch = cw1200_channel_switch, */ + /*.remain_on_channel = cw1200_remain_on_channel, */ + /*.cancel_remain_on_channel = cw1200_cancel_remain_on_channel, */ }; struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) @@ -308,11 +312,18 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work); INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work); + if (unlikely(cw1200_queue_stats_init(&priv->tx_queue_stats, + CW1200_LINK_ID_AFTER_DTIM + 1))) { + ieee80211_free_hw(hw); + return NULL; + } + for (i = 0; i < 4; ++i) { - if (unlikely(cw1200_queue_init(&priv->tx_queue[i], i, - 16, CW1200_LINK_ID_AFTER_DTIM + 1))) { + if (unlikely(cw1200_queue_init(&priv->tx_queue[i], + &priv->tx_queue_stats, i, 16))) { for (; i > 0; i--) cw1200_queue_deinit(&priv->tx_queue[i - 1]); + cw1200_queue_stats_deinit(&priv->tx_queue_stats); ieee80211_free_hw(hw); return NULL; } @@ -364,6 +375,7 @@ EXPORT_SYMBOL_GPL(cw1200_free_common); void cw1200_unregister_common(struct ieee80211_hw *dev) { struct cw1200_common *priv = dev->priv; + int i; priv->sbus_ops->irq_unsubscribe(priv->sbus_priv); cw1200_unregister_bh(priv); @@ -388,6 +400,10 @@ void cw1200_unregister_common(struct ieee80211_hw *dev) dev_kfree_skb(priv->skb_cache); priv->skb_cache = NULL; } + + for (i = 0; i < 4; ++i) + cw1200_queue_deinit(&priv->tx_queue[i]); + cw1200_queue_stats_deinit(&priv->tx_queue_stats); } EXPORT_SYMBOL_GPL(cw1200_unregister_common); @@ -421,7 +437,7 @@ int cw1200_probe(const struct sbus_ops *sbus_ops, priv->wsm_cbc.rx = cw1200_rx_cb; priv->wsm_cbc.suspend_resume = cw1200_suspend_resume; /* priv->wsm_cbc.set_pm_complete = cw1200_set_pm_complete_cb; */ - /* priv->wsm_cbc.channel_switch = cw1200_channel_switch_cb; */ + priv->wsm_cbc.channel_switch = cw1200_channel_switch_cb; err = cw1200_register_bh(priv); if (err) diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c index 3adf4fc3072..f81e46cd4f4 100644 --- a/drivers/staging/cw1200/queue.c +++ b/drivers/staging/cw1200/queue.c @@ -21,8 +21,9 @@ struct sk_buff *skb; u32 packetID; /* For safety purposes only. I do not trust device too much. - * It was observed that it indicates TX for a packet several times, - * so it's not enough to use offset or address as an uniquie ID in a + * It was observed (last time quite long time ago) that it + * indicates TX for a packet several times, so it was not enough + * to use offset or address as an uniquie ID in a * queue. */ u8 generation; @@ -71,14 +72,32 @@ static inline u32 cw1200_queue_make_packet_id(u8 queue_generation, u8 queue_id, ((u32)queue_generation << 24); } -int cw1200_queue_init(struct cw1200_queue *queue, u8 queue_id, - size_t capacity, size_t map_capacity) +int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, + size_t map_capacity) +{ + memset(stats, 0, sizeof(*stats)); + stats->map_capacity = map_capacity; + spin_lock_init(&stats->lock); + init_waitqueue_head(&stats->wait_link_id_empty); + + stats->link_map_cache = kzalloc(sizeof(int[map_capacity]), + GFP_KERNEL); + if (!stats->link_map_cache) + return -ENOMEM; + + return 0; +} + +int cw1200_queue_init(struct cw1200_queue *queue, + struct cw1200_queue_stats *stats, + u8 queue_id, + size_t capacity) { size_t i; memset(queue, 0, sizeof(*queue)); + queue->stats = stats; queue->capacity = capacity; - queue->map_capacity = map_capacity; queue->queue_id = queue_id; INIT_LIST_HEAD(&queue->queue); INIT_LIST_HEAD(&queue->pending); @@ -86,12 +105,12 @@ int cw1200_queue_init(struct cw1200_queue *queue, u8 queue_id, spin_lock_init(&queue->lock); queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity, - GFP_KERNEL); + GFP_KERNEL); if (!queue->pool) return -ENOMEM; - queue->link_map_cache = kzalloc(sizeof(int[map_capacity]), - GFP_KERNEL); + queue->link_map_cache = kzalloc(sizeof(int[stats->map_capacity]), + GFP_KERNEL); if (!queue->link_map_cache) { kfree(queue->pool); queue->pool = NULL; @@ -106,6 +125,9 @@ int cw1200_queue_init(struct cw1200_queue *queue, u8 queue_id, int cw1200_queue_clear(struct cw1200_queue *queue) { + int i; + struct cw1200_queue_stats *stats = queue->stats; + spin_lock_bh(&queue->lock); queue->generation++; list_splice_tail_init(&queue->queue, &queue->pending); @@ -122,39 +144,53 @@ int cw1200_queue_clear(struct cw1200_queue *queue) queue->num_queued = 0; queue->num_pending = 0; queue->num_sent = 0; - memset(queue->link_map_cache, 0, sizeof(int[queue->map_capacity])); + + spin_lock_bh(&stats->lock); + for (i = 0; i < stats->map_capacity; ++i) { + stats->num_queued -= queue->link_map_cache[i]; + stats->link_map_cache[i] -= queue->link_map_cache[i]; + queue->link_map_cache[i] = 0; + } + spin_unlock_bh(&stats->lock); spin_unlock_bh(&queue->lock); + wake_up_interruptible(&stats->wait_link_id_empty); return 0; } -int cw1200_queue_deinit(struct cw1200_queue *queue) +void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats) +{ + kfree(stats->link_map_cache); + stats->link_map_cache = NULL; +} + +void cw1200_queue_deinit(struct cw1200_queue *queue) { cw1200_queue_clear(queue); INIT_LIST_HEAD(&queue->free_pool); kfree(queue->pool); kfree(queue->link_map_cache); queue->pool = NULL; + queue->link_map_cache = NULL; queue->capacity = 0; - - return 0; } size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, - u32 allowed_mask) + u32 link_id_map) { size_t ret; int i, bit; + size_t map_capacity = queue->stats->map_capacity; - if (!allowed_mask) + if (!link_id_map) return 0; spin_lock_bh(&queue->lock); - if (likely(allowed_mask == (u32) -1)) + if (likely(link_id_map == (u32) -1)) ret = queue->num_queued - queue->num_pending; else { ret = 0; - for (i = 0, bit = 1; i < queue->map_capacity; ++i, bit <<= 1) { - if (allowed_mask & bit) + for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) { + if (link_id_map & bit) ret += queue->link_map_cache[i]; } } @@ -167,13 +203,14 @@ int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, { int ret; struct wsm_tx *wsm; + struct cw1200_queue_stats *stats = queue->stats; wsm = (struct wsm_tx *)skb_push(skb, sizeof(struct wsm_tx)); ret = cw1200_skb_to_wsm(priv, skb, wsm); if (ret) return ret; - if (link_id >= queue->map_capacity) + if (link_id >= queue->stats->map_capacity) return -EINVAL; spin_lock_bh(&queue->lock); @@ -193,6 +230,11 @@ int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, ++queue->num_queued; ++queue->link_map_cache[link_id]; + spin_lock_bh(&stats->lock); + ++stats->num_queued; + ++stats->link_map_cache[link_id]; + spin_unlock_bh(&stats->lock); + if (queue->num_queued >= queue->capacity) { queue->overfull = true; __cw1200_queue_lock(queue, priv); @@ -205,16 +247,18 @@ int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, } int cw1200_queue_get(struct cw1200_queue *queue, - u32 allowed_mask, + u32 link_id_map, struct wsm_tx **tx, struct ieee80211_tx_info **tx_info) { int ret = -ENOENT; struct cw1200_queue_item *item; + struct cw1200_queue_stats *stats = queue->stats; + bool wakeup_stats = false; spin_lock_bh(&queue->lock); list_for_each_entry(item, &queue->queue, head) { - if (allowed_mask & (1 << item->link_id)) { + if (link_id_map & BIT(item->link_id)) { ret = 0; break; } @@ -226,8 +270,16 @@ int cw1200_queue_get(struct cw1200_queue *queue, list_move_tail(&item->head, &queue->pending); ++queue->num_pending; --queue->link_map_cache[item->link_id]; + + spin_lock_bh(&stats->lock); + --stats->num_queued; + if (!--stats->link_map_cache[item->link_id]) + wakeup_stats = true; + spin_unlock_bh(&stats->lock); } spin_unlock_bh(&queue->lock); + if (wakeup_stats) + wake_up_interruptible(&stats->wait_link_id_empty); return ret; } @@ -236,6 +288,8 @@ int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID) int ret = 0; u8 queue_generation, queue_id, item_generation, item_id; struct cw1200_queue_item *item; + struct cw1200_queue_stats *stats = queue->stats; + cw1200_queue_parse_id(packetID, &queue_generation, &queue_id, &item_generation, &item_id); @@ -255,6 +309,12 @@ int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID) struct wsm_tx *wsm = (struct wsm_tx *)item->skb->data; --queue->num_pending; ++queue->link_map_cache[item->link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued; + ++stats->link_map_cache[item->link_id]; + spin_unlock_bh(&stats->lock); + item->generation = ++item_generation; item->packetID = cw1200_queue_make_packet_id( queue_generation, queue_id, item_generation, item_id); @@ -267,6 +327,7 @@ int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID) int cw1200_queue_requeue_all(struct cw1200_queue *queue) { + struct cw1200_queue_stats *stats = queue->stats; spin_lock_bh(&queue->lock); while (!list_empty(&queue->pending)) { struct cw1200_queue_item *item = list_first_entry( @@ -275,6 +336,12 @@ int cw1200_queue_requeue_all(struct cw1200_queue *queue) --queue->num_pending; ++queue->link_map_cache[item->link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued; + ++stats->link_map_cache[item->link_id]; + spin_unlock_bh(&stats->lock); + ++item->generation; item->packetID = cw1200_queue_make_packet_id( queue->generation, queue->queue_id, @@ -393,3 +460,26 @@ int cw1200_queue_get_stats(struct cw1200_queue *queue, } */ +bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, + u32 link_id_map) +{ + bool empty = true; + + spin_lock_bh(&stats->lock); + if (link_id_map == (u32)-1) + empty = stats->num_queued == 0; + else { + int i; + for (i = 0; i < stats->map_capacity; ++i) { + if (link_id_map & BIT(i)) { + if (stats->link_map_cache[i]) { + empty = false; + break; + } + } + } + } + spin_unlock_bh(&stats->lock); + + return empty; +} diff --git a/drivers/staging/cw1200/queue.h b/drivers/staging/cw1200/queue.h index a87505defba..fc5d4613efb 100644 --- a/drivers/staging/cw1200/queue.h +++ b/drivers/staging/cw1200/queue.h @@ -19,7 +19,10 @@ /* extern */ struct cw1200_common; /* extern */ struct ieee80211_tx_queue_stats; +/* forward */ struct cw1200_queue_stats; + struct cw1200_queue { + struct cw1200_queue_stats *stats; size_t capacity; size_t num_queued; size_t num_pending; @@ -30,24 +33,36 @@ struct cw1200_queue { struct list_head pending; int tx_locked_cnt; int *link_map_cache; - size_t map_capacity; bool overfull; spinlock_t lock; u8 queue_id; u8 generation; }; -int cw1200_queue_init(struct cw1200_queue *queue, u8 queue_id, - size_t capacity, size_t map_capacity); +struct cw1200_queue_stats { + spinlock_t lock; + int *link_map_cache; + int num_queued; + size_t map_capacity; + wait_queue_head_t wait_link_id_empty; +}; + +int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, + size_t map_capacity); +int cw1200_queue_init(struct cw1200_queue *queue, + struct cw1200_queue_stats *stats, + u8 queue_id, + size_t capacity); int cw1200_queue_clear(struct cw1200_queue *queue); -int cw1200_queue_deinit(struct cw1200_queue *queue); +void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats); +void cw1200_queue_deinit(struct cw1200_queue *queue); size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, - u32 allowed_mask); + u32 link_id_map); int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *cw1200, struct sk_buff *skb, u8 link_id); int cw1200_queue_get(struct cw1200_queue *queue, - u32 allowed_mask, + u32 link_id_map, struct wsm_tx **tx, struct ieee80211_tx_info **tx_info); int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID); @@ -61,6 +76,9 @@ void cw1200_queue_lock(struct cw1200_queue *queue, void cw1200_queue_unlock(struct cw1200_queue *queue, struct cw1200_common *cw1200); +bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, + u32 link_id_map); + /* int cw1200_queue_get_stats(struct cw1200_queue *queue, struct ieee80211_tx_queue_stats *stats); */ diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 6cebf530f40..4cdcd4a43a6 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -26,6 +26,7 @@ static int cw1200_cancel_scan(struct cw1200_common *priv); +static int __cw1200_flush(struct cw1200_common *priv, bool drop); static inline void __cw1200_free_event_queue(struct list_head *list) { @@ -251,18 +252,26 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) && - (priv->channel != conf->channel)) { + (priv->channel != conf->channel)) { + struct ieee80211_channel *ch = conf->channel; struct wsm_switch_channel channel = { - .newChannelNumber = conf->channel->hw_value + .newChannelNumber = ch->hw_value, }; cw1200_cancel_scan(priv); sta_printk(KERN_DEBUG "[STA] Freq %d (wsm ch: %d).\n", - conf->channel->center_freq, conf->channel->hw_value); + ch->center_freq, ch->hw_value); WARN_ON(wait_event_interruptible_timeout( priv->channel_switch_done, !priv->channel_switch_in_progress, 3 * HZ) <= 0); - WARN_ON(wsm_switch_channel(priv, &channel)); - priv->channel = conf->channel; + + ret = WARN_ON(__cw1200_flush(priv, false)); + if (!ret) { + ret = WARN_ON(wsm_switch_channel(priv, &channel)); + if (!ret) + priv->channel = ch; + else + wsm_unlock_tx(priv); + } } if (changed & IEEE80211_CONF_CHANGE_PS) { @@ -567,6 +576,50 @@ int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value) return ret; } +static int __cw1200_flush(struct cw1200_common *priv, bool drop) +{ + int i, ret; + + if (drop) { + for (i = 0; i < 4; ++i) + cw1200_queue_clear(&priv->tx_queue[i]); + } + + for (;;) { + ret = wait_event_interruptible_timeout( + priv->tx_queue_stats.wait_link_id_empty, + cw1200_queue_stats_is_empty( + &priv->tx_queue_stats, -1), + 10 * HZ); + + if (unlikely(ret <= 0)) { + if (!ret) + ret = -ETIMEDOUT; + break; + } + + wsm_lock_tx(priv); + if (unlikely(!cw1200_queue_stats_is_empty( + &priv->tx_queue_stats, -1))) { + /* Highly unlekely: WSM requeued frames. */ + wsm_unlock_tx(priv); + continue; + } + break; + } + return ret; +} + +void cw1200_flush(struct ieee80211_hw *hw, bool drop) +{ + struct cw1200_common *priv = hw->priv; + + if (!WARN_ON(__cw1200_flush(priv, drop))) + wsm_unlock_tx(priv); + + return; +} + /* ******************************************************************** */ /* WSM callbacks */ @@ -630,6 +683,11 @@ void cw1200_rx_cb(struct cw1200_common *priv, *skb_p = NULL; } +void cw1200_channel_switch_cb(struct cw1200_common *priv) +{ + wsm_unlock_tx(priv); +} + void cw1200_free_event_queue(struct cw1200_common *priv) { LIST_HEAD(list); diff --git a/drivers/staging/cw1200/sta.h b/drivers/staging/cw1200/sta.h index a36127b3843..63cc1793151 100644 --- a/drivers/staging/cw1200/sta.h +++ b/drivers/staging/cw1200/sta.h @@ -40,6 +40,8 @@ int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value); +void cw1200_flush(struct ieee80211_hw *hw, bool drop); + /* ******************************************************************** */ /* WSM callbacks */ @@ -48,7 +50,7 @@ void cw1200_rx_cb(struct cw1200_common *priv, struct sk_buff **skb_p); /* void cw1200_set_pm_complete_cb(struct cw1200_common *priv, struct wsm_set_pm_complete *arg); */ -/* void cw1200_channel_switch_cb(struct cw1200_common *priv); */ +void cw1200_channel_switch_cb(struct cw1200_common *priv); /* ******************************************************************** */ /* WSM events */ diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 2a07e7e93f8..5c3bd5c12f3 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1499,8 +1499,6 @@ void wsm_txed(struct cw1200_common *priv, u8 *data) priv->wsm_cmd.ptr = NULL; spin_unlock(&priv->wsm_cmd.lock); } - - /* TODO: data queues */ } /* ******************************************************************** */ -- cgit v1.2.3 From 4a2c33eba2353dcdf6cb7436091fef93dce6ff2e Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Tue, 17 May 2011 17:17:57 +0200 Subject: wlan: cw1200: Reenable filtering. Filtering is enabled again (was broken in earlier firmware). Added support for FIF_PROBE_REQ for management frames. New WSM API wsm_set_bssid_filtering is used. Signed-off-by: Dmitry Tarnyagin Change-Id: Id8a44424a8ae2b0ba7d4082a096c88344e7a1a78 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23605 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/sta.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 4cdcd4a43a6..540439863dd 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -326,11 +326,11 @@ void cw1200_configure_filter(struct ieee80211_hw *dev, unsigned int *total_flags, u64 multicast) { -#if 0 struct cw1200_common *priv = dev->priv; struct wsm_rx_filter filter = { .promiscuous = (*total_flags & FIF_PROMISC_IN_BSS) ? 1 : 0, - .bssid = (*total_flags & FIF_OTHER_BSS) ? 1 : 0, + .bssid = (*total_flags & (FIF_OTHER_BSS | FIF_PROBE_REQ)) ? + 1 : 0, .fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0, }; struct wsm_beacon_filter_control bf_control = { @@ -339,18 +339,16 @@ void cw1200_configure_filter(struct ieee80211_hw *dev, (FIF_BCN_PRBRESP_PROMISC | FIF_PROMISC_IN_BSS)) ? 1 : 0, }; -#endif *total_flags &= FIF_PROMISC_IN_BSS | FIF_OTHER_BSS | FIF_FCSFAIL | - FIF_BCN_PRBRESP_PROMISC; + FIF_BCN_PRBRESP_PROMISC | + FIF_PROBE_REQ; -#if 0 - /* FIXME: FW behaves strange if promiscuous mode is enabled. */ WARN_ON(wsm_set_rx_filter(priv, &filter)); WARN_ON(wsm_beacon_filter_control(priv, &bf_control)); -#endif + WARN_ON(wsm_set_bssid_filtering(priv, !filter.bssid)); } int cw1200_conf_tx(struct ieee80211_hw *dev, u16 queue, @@ -596,6 +594,8 @@ static int __cw1200_flush(struct cw1200_common *priv, bool drop) if (!ret) ret = -ETIMEDOUT; break; + } else { + ret = 0; } wsm_lock_tx(priv); -- cgit v1.2.3 From 78ce7d0b22780e3ac0eb85d32fddcc450691aa45 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Sun, 22 May 2011 23:22:42 +0200 Subject: cw1200: Request SDIO pins at device registration. ux500 platform requires hardware pins to be explicitly requested. Change-Id: I739b6badfb5d19e6e1c49eb8232df3b8aaa958e9 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23620 Reviewed-by: Par-Gunnar HJALMDAHL Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Janusz DZIEDZIC Reviewed-by: Philippe LANGLAIS --- arch/arm/mach-ux500/board-mop500-wlan.c | 42 +++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-wlan.c b/arch/arm/mach-ux500/board-mop500-wlan.c index c24069600ca..90b17e70e23 100644 --- a/arch/arm/mach-ux500/board-mop500-wlan.c +++ b/arch/arm/mach-ux500/board-mop500-wlan.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include "pins.h" #include "../drivers/staging/cw1200/cw1200_plat.h" static void cw1200_release(struct device *dev); @@ -66,8 +68,36 @@ const struct cw1200_platform_data *cw1200_get_platform_data(void) } EXPORT_SYMBOL_GPL(cw1200_get_platform_data); +static int cw1200_pins_enable(bool enable) +{ + struct ux500_pins *pins; + int ret = 0; + + pins = ux500_pins_get("sdi1"); + if (!pins) { + printk(KERN_ERR "cw1200: Pins are not found. " + "Check platform data.\n"); + return -ENOENT; + } + + if (enable) + ret = ux500_pins_enable(pins); + else + ret = ux500_pins_disable(pins); + + if (ret) + printk(KERN_ERR "cw1200: Pins can not be %s: %d.\n", + enable ? "enabled" : "disabled", + ret); + + ux500_pins_put(pins); + + return ret; +} + int __init mop500_wlan_init(void) { + int ret; if (machine_is_snowball() || machine_is_u8500() || machine_is_u5500() || @@ -99,10 +129,18 @@ int __init mop500_wlan_init(void) cw1200_device.dev.release = cw1200_release; - return platform_device_register(&cw1200_device); + ret = cw1200_pins_enable(true); + if (WARN_ON(ret)) + return ret; + + ret = platform_device_register(&cw1200_device); + if (ret) + cw1200_pins_enable(false); + + return ret; } static void cw1200_release(struct device *dev) { - /* Do nothing: release is handled by SDIO stack */ + cw1200_pins_enable(false); } -- cgit v1.2.3 From eb5282ad6df8229865820c92a08e5a57f15e8c98 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Tue, 24 May 2011 14:41:49 +0200 Subject: wlan: cw1200: Add cut2.2 support Added support for cw1200 cut2.2 chip and firmware Change-Id: I996d0666498d5fa344000313debe876d760a9b9f Signed-off-by: Janusz Dziedzic Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23739 Reviewed-by: Dmitry TARNYAGIN Tested-by: Dmitry TARNYAGIN Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/fwio.c | 40 ++++++++++++++++++++++++++++++++++++++-- drivers/staging/cw1200/fwio.h | 3 +++ drivers/staging/cw1200/hwio.h | 4 ++++ drivers/staging/cw1200/sta.c | 3 +++ 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/drivers/staging/cw1200/fwio.c b/drivers/staging/cw1200/fwio.c index e4c9684bc18..da6d3705e6c 100644 --- a/drivers/staging/cw1200/fwio.c +++ b/drivers/staging/cw1200/fwio.c @@ -125,6 +125,9 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) case CW1200_HW_REV_CUT20: fw_path = FIRMWARE_CUT20; break; + case CW1200_HW_REV_CUT22: + fw_path = FIRMWARE_CUT22; + break; default: cw1200_dbg(CW1200_DBG_ERROR, "%s: invalid silicon revision %d.\n", @@ -422,8 +425,41 @@ int cw1200_load_firmware(struct cw1200_common *priv) break; } } else if (major_revision == 2) { - cw1200_dbg(CW1200_DBG_MSG, "Cut 2.0 silicon is detected.\n"); - priv->hw_revision = CW1200_HW_REV_CUT20; + u32 ar1, ar2, ar3; + cw1200_dbg(CW1200_DBG_MSG, "Cut 2.x silicon is detected.\n"); + + ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR, &ar1); + if (ret) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: (1) HW detection: can't read CUT ID.\n", + __func__); + goto out; + } + ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 4, &ar2); + if (ret) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: (2) HW detection: can't read CUT ID.\n", + __func__); + goto out; + } + + ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 8, &ar3); + if (ret) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: (3) HW detection: can't read CUT ID.\n", + __func__); + goto out; + } + + if (ar1 == CW1200_CUT_22_ID_STR1 && + ar2 == CW1200_CUT_22_ID_STR2 && + ar3 == CW1200_CUT_22_ID_STR3) { + cw1200_dbg(CW1200_DBG_MSG, "Cut 2.2 detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT22; + } else { + cw1200_dbg(CW1200_DBG_MSG, "Cut 2.0 detected.\n"); + priv->hw_revision = CW1200_HW_REV_CUT20; + } } else { cw1200_dbg(CW1200_DBG_ERROR, "%s: unsupported silicon major revision %d.\n", diff --git a/drivers/staging/cw1200/fwio.h b/drivers/staging/cw1200/fwio.h index 4f5e612cf10..cb91b8dc481 100644 --- a/drivers/staging/cw1200/fwio.h +++ b/drivers/staging/cw1200/fwio.h @@ -17,9 +17,11 @@ #ifndef FWIO_H_INCLUDED #define FWIO_H_INCLUDED +#define FIRMWARE_CUT22 ("cw1200/wsm_22.bin") #define FIRMWARE_CUT20 ("cw1200/wsm_20.bin") #define FIRMWARE_CUT11 ("cw1200/wsm_11.bin") #define FIRMWARE_CUT10 ("cw1200/wsm_10.bin") +#define SDD_FILE_22 ("cw1200/sdd_22.bin") #define SDD_FILE_20 ("cw1200/sdd_20.bin") #define SDD_FILE_11 ("cw1200/sdd_11.bin") #define SDD_FILE_10 ("cw1200/sdd_10.bin") @@ -27,6 +29,7 @@ #define CW1200_HW_REV_CUT10 (10) #define CW1200_HW_REV_CUT11 (11) #define CW1200_HW_REV_CUT20 (20) +#define CW1200_HW_REV_CUT22 (22) int cw1200_load_firmware(struct cw1200_common *priv); diff --git a/drivers/staging/cw1200/hwio.h b/drivers/staging/cw1200/hwio.h index 38b5c94edf8..427b75e0523 100644 --- a/drivers/staging/cw1200/hwio.h +++ b/drivers/staging/cw1200/hwio.h @@ -29,7 +29,11 @@ #define HIF_9000_SILICON_VERSTAILE (2) #define CW1200_CUT_11_ID_STR (0x302E3830) +#define CW1200_CUT_22_ID_STR1 (0x302e3132) +#define CW1200_CUT_22_ID_STR2 (0x32302e30) +#define CW1200_CUT_22_ID_STR3 (0x3335) #define CW1200_CUT_ID_ADDR (0xFFF17F90) +#define CW1200_CUT2_ID_ADDR (0xFFF1FF90) /* Download control area */ /* boot loader start address in SRAM */ diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 540439863dd..7b054687228 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -877,6 +877,9 @@ int cw1200_setup_mac(struct cw1200_common *priv) case CW1200_HW_REV_CUT20: sdd_path = SDD_FILE_20; break; + case CW1200_HW_REV_CUT22: + sdd_path = SDD_FILE_22; + break; default: BUG_ON(1); } -- cgit v1.2.3 From 76406f8575de3a51332240665e16201283da6762 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 19 May 2011 00:13:26 +0200 Subject: cw1200: listening mode implementation Listening mode is required for off-channel operations (P2P use-cases). Listening is implemented as WSM Start with WSM_START_MODE_P2P_DEV. Change-Id: I1dd90c433a0eb557ec39b2684912e4c937fb84cf Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23804 Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 5 +- drivers/staging/cw1200/cw1200.h | 6 +- drivers/staging/cw1200/sta.c | 128 +++++++++++++++++++++++++++++++++------- drivers/staging/cw1200/sta.h | 2 + drivers/staging/cw1200/wsm.c | 4 +- drivers/staging/cw1200/wsm.h | 3 +- 6 files changed, 122 insertions(+), 26 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index fbf7ba78a6e..7fa863c6f20 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -605,8 +605,10 @@ static int cw1200_start_ap(struct cw1200_common *priv) ret = WARN_ON(cw1200_upload_keys(priv)); if (!ret) ret = WARN_ON(wsm_beacon_transmit(priv, &transmit)); - if (!ret) + if (!ret) { priv->join_status = CW1200_JOIN_STATUS_AP; + cw1200_update_filtering(priv); + } return ret; } @@ -620,6 +622,7 @@ static int cw1200_update_beaconing(struct cw1200_common *priv) if (priv->mode == NL80211_IFTYPE_AP) { ap_printk(KERN_DEBUG "[AP] %s.\n", __func__); WARN_ON(wsm_reset(priv, &reset)); + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; WARN_ON(cw1200_start_ap(priv)); } return 0; diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 248ca79a404..23c201ad2a4 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -45,7 +45,8 @@ #define CW1200_LINK_ID_AFTER_DTIM (CW1200_MAX_STA_IN_AP_MODE + 1) enum cw1200_join_status { - CW1200_JOIN_STATUS_MONITOR = 0, + CW1200_JOIN_STATUS_PASSIVE = 0, + CW1200_JOIN_STATUS_MONITOR, CW1200_JOIN_STATUS_STA, CW1200_JOIN_STATUS_AP, }; @@ -101,6 +102,9 @@ struct cw1200_common { bool enable_beacon; size_t ssid_length; u8 ssid[IEEE80211_MAX_SSID_LEN]; + bool listening; + struct wsm_rx_filter rx_filter; + struct wsm_beacon_filter_control bf_control; /* BH */ atomic_t bh_rx; diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 7b054687228..5f42e25c2fa 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -100,6 +100,7 @@ void cw1200_stop(struct ieee80211_hw *dev) mutex_lock(&priv->conf_mutex); cw1200_free_keys(priv); priv->mode = NL80211_IFTYPE_UNSPECIFIED; + priv->listening = false; mutex_unlock(&priv->conf_mutex); cancel_delayed_work_sync(&priv->scan.probe_work); @@ -110,8 +111,11 @@ void cw1200_stop(struct ieee80211_hw *dev) #if defined(CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) cancel_delayed_work_sync(&priv->keep_alive_work); #endif /* CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ + + mutex_lock(&priv->conf_mutex); switch (priv->join_status) { case CW1200_JOIN_STATUS_STA: + wsm_lock_tx(priv); queue_work(priv->workqueue, &priv->unjoin_work); break; case CW1200_JOIN_STATUS_AP: @@ -121,11 +125,15 @@ void cw1200_stop(struct ieee80211_hw *dev) priv->sta_asleep_mask = 0; priv->suspend_multicast = false; wsm_reset(priv, &reset); - wsm_unlock_tx(priv); + break; + case CW1200_JOIN_STATUS_MONITOR: + cw1200_update_listening(priv, false); break; default: - wsm_unlock_tx(priv); + break; } + mutex_unlock(&priv->conf_mutex); + flush_workqueue(priv->workqueue); mutex_lock(&priv->conf_mutex); @@ -140,9 +148,7 @@ void cw1200_stop(struct ieee80211_hw *dev) priv->delayed_link_loss = 0; priv->link_id_map = 0; - priv->join_status = CW1200_JOIN_STATUS_MONITOR; - - /* TODO: Complete deinitialization */ + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; for (i = 0; i < 4; i++) cw1200_queue_clear(&priv->tx_queue[i]); @@ -215,6 +221,8 @@ void cw1200_remove_interface(struct ieee80211_hw *dev, WARN_ON(wsm_reset(priv, &reset)); cw1200_free_keys(priv); cw1200_setup_mac(priv); + priv->listening = false; + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; mutex_unlock(&priv->conf_mutex); } @@ -321,24 +329,36 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) return ret; } +void cw1200_update_filtering(struct cw1200_common *priv) +{ + int ret; + + if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) + return; + + ret = wsm_set_rx_filter(priv, &priv->rx_filter); + if (!ret) + ret = wsm_beacon_filter_control(priv, &priv->bf_control); + if (!ret) + ret = wsm_set_bssid_filtering(priv, !priv->rx_filter.bssid); + if (ret) + wiphy_err(priv->hw->wiphy, + "%s: Update filtering failed: %d.\n", + __func__, ret); + return; +} + void cw1200_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) { struct cw1200_common *priv = dev->priv; - struct wsm_rx_filter filter = { - .promiscuous = (*total_flags & FIF_PROMISC_IN_BSS) ? 1 : 0, - .bssid = (*total_flags & (FIF_OTHER_BSS | FIF_PROBE_REQ)) ? - 1 : 0, - .fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0, - }; - struct wsm_beacon_filter_control bf_control = { - .enabled = 0, - .bcn_count = (*total_flags & - (FIF_BCN_PRBRESP_PROMISC | FIF_PROMISC_IN_BSS)) ? - 1 : 0, - }; + bool listening = !!(*total_flags & + (FIF_PROMISC_IN_BSS | + FIF_OTHER_BSS | + FIF_BCN_PRBRESP_PROMISC | + FIF_PROBE_REQ)); *total_flags &= FIF_PROMISC_IN_BSS | FIF_OTHER_BSS | @@ -346,9 +366,23 @@ void cw1200_configure_filter(struct ieee80211_hw *dev, FIF_BCN_PRBRESP_PROMISC | FIF_PROBE_REQ; - WARN_ON(wsm_set_rx_filter(priv, &filter)); - WARN_ON(wsm_beacon_filter_control(priv, &bf_control)); - WARN_ON(wsm_set_bssid_filtering(priv, !filter.bssid)); + mutex_lock(&priv->conf_mutex); + + priv->rx_filter.promiscuous = (*total_flags & FIF_PROMISC_IN_BSS) + ? 1 : 0; + priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS | + FIF_PROBE_REQ)) ? 1 : 0; + priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0; + priv->bf_control.bcn_count = (*total_flags & + (FIF_BCN_PRBRESP_PROMISC | + FIF_PROMISC_IN_BSS | + FIF_PROBE_REQ)) ? 1 : 0; + if (priv->listening ^ listening) { + priv->listening = listening; + cw1200_update_listening(priv, listening); + } + cw1200_update_filtering(priv); + mutex_unlock(&priv->conf_mutex); } int cw1200_conf_tx(struct ieee80211_hw *dev, u16 queue, @@ -597,6 +631,7 @@ static int __cw1200_flush(struct cw1200_common *priv, bool drop) } else { ret = 0; } + ret = 0; wsm_lock_tx(priv); if (unlikely(!cw1200_queue_stats_is_empty( @@ -1011,6 +1046,7 @@ void cw1200_join_work(struct work_struct *work) queue_delayed_work(priv->workqueue, &priv->join_timeout, 3 * HZ); + cw1200_update_listening(priv, false); if (wsm_join(priv, &join)) { memset(&priv->join_bssid[0], 0, sizeof(priv->join_bssid)); @@ -1020,6 +1056,7 @@ void cw1200_join_work(struct work_struct *work) #if defined(CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) cancel_delayed_work_sync(&priv->keep_alive_work); #endif /* CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ + cw1200_update_listening(priv, priv->listening); } else { WARN_ON(cw1200_upload_keys(priv)); #if !defined(CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) @@ -1028,6 +1065,7 @@ void cw1200_join_work(struct work_struct *work) cw1200_queue_requeue(&priv->tx_queue[queueId], __le32_to_cpu(wsm->packetID)); } + cw1200_update_filtering(priv); } mutex_unlock(&priv->conf_mutex); cfg80211_put_bss(bss); @@ -1056,7 +1094,7 @@ void cw1200_unjoin_work(struct work_struct *work) priv->join_status != CW1200_JOIN_STATUS_STA); if (priv->join_status == CW1200_JOIN_STATUS_STA) { memset(&priv->join_bssid[0], 0, sizeof(priv->join_bssid)); - priv->join_status = CW1200_JOIN_STATUS_MONITOR; + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; /* Unjoin is a reset. */ wsm_flush_tx(priv); @@ -1069,12 +1107,60 @@ void cw1200_unjoin_work(struct work_struct *work) #if defined(CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) cancel_delayed_work_sync(&priv->keep_alive_work); #endif /* CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ + cw1200_update_listening(priv, priv->listening); + cw1200_update_filtering(priv); sta_printk(KERN_DEBUG "[STA] Unjoin.\n"); } mutex_unlock(&priv->conf_mutex); wsm_unlock_tx(priv); } +static inline int cw1200_enable_listening(struct cw1200_common *priv) +{ + struct wsm_start start = { + .mode = WSM_START_MODE_P2P_DEV, + .band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G, + .channelNumber = priv->channel->hw_value, + .beaconInterval = 100, + .DTIMPeriod = 1, + .probeDelay = 0, + .basicRateSet = 0x0F, + }; + return wsm_start(priv, &start); +} + +static inline int cw1200_disable_listening(struct cw1200_common *priv) +{ + struct wsm_reset reset = { + .reset_statistics = true, + }; + return wsm_reset(priv, &reset); +} + +void cw1200_update_listening(struct cw1200_common *priv, bool enabled) +{ + if (enabled) { + switch (priv->join_status) { + case CW1200_JOIN_STATUS_PASSIVE: + if (!WARN_ON(cw1200_enable_listening(priv))) + priv->join_status = CW1200_JOIN_STATUS_MONITOR; + break; + default: + break; + } + } else { + switch (priv->join_status) { + case CW1200_JOIN_STATUS_MONITOR: + if (!WARN_ON(cw1200_disable_listening(priv))) + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + default: + break; + } + } +} + + /* ******************************************************************** */ /* STA privates */ diff --git a/drivers/staging/cw1200/sta.h b/drivers/staging/cw1200/sta.h index 63cc1793151..998afeedf45 100644 --- a/drivers/staging/cw1200/sta.h +++ b/drivers/staging/cw1200/sta.h @@ -70,5 +70,7 @@ void cw1200_join_work(struct work_struct *work); void cw1200_join_timeout(struct work_struct *work); void cw1200_unjoin_work(struct work_struct *work); void cw1200_wep_key_work(struct work_struct *work); +void cw1200_update_listening(struct cw1200_common *priv, bool enabled); +void cw1200_update_filtering(struct cw1200_common *priv); #endif diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 5c3bd5c12f3..fa7fb2c246a 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -388,7 +388,7 @@ static int wsm_join_confirm(struct cw1200_common *priv, struct wsm_buf *buf) { if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS)) { - priv->join_status = CW1200_JOIN_STATUS_MONITOR; + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; wsm_unlock_tx(priv); return -EINVAL; } @@ -402,7 +402,7 @@ static int wsm_join_confirm(struct cw1200_common *priv, underflow: WARN_ON(1); - priv->join_status = CW1200_JOIN_STATUS_MONITOR; + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; wsm_unlock_tx(priv); return -EINVAL; } diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index f80e60fdc89..fd84b147fde 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -305,7 +305,8 @@ struct cw1200_common; /* Start modes */ #define WSM_START_MODE_AP (0) /* Mini AP */ -#define WSM_START_MODE_P2P (1) /* P2P */ +#define WSM_START_MODE_P2P_GO (1) /* P2P GO */ +#define WSM_START_MODE_P2P_DEV (2) /* P2P device */ /* SetAssociationMode MIB flags */ #define WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE (BIT(0)) -- cgit v1.2.3 From 9a9939e761b53676c1f01ce5cb08591f3c8bafc5 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 25 May 2011 01:57:34 +0200 Subject: cw1200: Align TX buffers for DMA. u8500 platform requires that DMA buffers should be 4 bytes aligned. In most cases skb's passed by network stack are already aligned. Known exception: TCP connect. Patch checks alignment of skb->data and move it to the 4 bytes boundary if not aligned. Change-Id: I3ce7ba9baa42e6956b2a5cff04ad6a0aacc9f257 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23803 Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/txrx.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 5f3c859fb9e..3435a7b59da 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -430,9 +430,10 @@ int cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) icv_len += 8; /* MIC */ } - if (WARN_ON(skb_headroom(skb) < iv_len + WSM_TX_EXTRA_HEADROOM - || skb_tailroom(skb) < icv_len)) { - printk(KERN_ERR "Bug: no space allocated " + if (skb_headroom(skb) < iv_len + WSM_TX_EXTRA_HEADROOM + || skb_tailroom(skb) < icv_len) { + wiphy_err(priv->hw->wiphy, + "Bug: no space allocated " "for crypto headers.\n" "headroom: %d, tailroom: %d, " "req_headroom: %d, req_tailroom: %d\n" @@ -449,6 +450,22 @@ int cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) memset(icv, 0, icv_len); } + if ((size_t)skb->data & 3) { + size_t offset = (size_t)skb->data & 3; + u8 *p; + if (skb_headroom(skb) < 4) { + wiphy_err(priv->hw->wiphy, + "Bug: no space allocated " + "for DMA alignment.\n" + "headroom: %d\n", + skb_headroom(skb)); + goto err; + } + p = skb_push(skb, offset); + memmove(p, &p[offset], skb->len - offset); + skb_trim(skb, skb->len - offset); + } + ret = cw1200_queue_put(&priv->tx_queue[queue], priv, skb, link_id); if (!WARN_ON(ret)) -- cgit v1.2.3 From 831df9acc277af959b0e8b3d53a55fe8fc714060 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 25 May 2011 23:22:35 +0200 Subject: cw1200: Add regulator support Add regulator support needed on Snowball HW. Change-Id: I12a88e7f968e00de256c7f331f5baede2d410ab7 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25139 Reviewed-by: Robert MARKLUND Reviewed-by: Philippe LANGLAIS --- arch/arm/mach-ux500/board-mop500-wlan.c | 44 +++++++++++++++++++++++++++++---- drivers/staging/cw1200/cw1200_plat.h | 5 ++-- drivers/staging/cw1200/cw1200_sdio.c | 29 ++++++++++++++++++---- 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-wlan.c b/arch/arm/mach-ux500/board-mop500-wlan.c index 90b17e70e23..e73a35e7c76 100644 --- a/arch/arm/mach-ux500/board-mop500-wlan.c +++ b/arch/arm/mach-ux500/board-mop500-wlan.c @@ -6,7 +6,9 @@ */ #include +#include #include +#include #include #include #include @@ -14,6 +16,8 @@ #include "../drivers/staging/cw1200/cw1200_plat.h" static void cw1200_release(struct device *dev); +static int cw1200_power_ctrl(const struct cw1200_platform_data *pdata, + bool enable); static struct resource cw1200_href_resources[] = { { @@ -49,16 +53,14 @@ static struct resource cw1200_href60_resources[] = { #endif /* CONFIG_CW1200_USE_GPIO_IRQ */ }; -static struct cw1200_platform_data cw1200_platform_data = { - .regulator_vdd = "vdd", - .regulator_vio = "vio", -}; +static struct cw1200_platform_data cw1200_platform_data = { 0 }; static struct platform_device cw1200_device = { - .name = "cw1200", + .name = "cw1200_wlan", .dev = { .platform_data = &cw1200_platform_data, .release = cw1200_release, + .init_name = "cw1200_wlan", }, }; @@ -95,6 +97,36 @@ static int cw1200_pins_enable(bool enable) return ret; } +static int cw1200_power_ctrl(const struct cw1200_platform_data *pdata, + bool enable) +{ + static const char *vdd_name = "vdd"; + struct regulator *vdd; + int ret = 0; + + vdd = regulator_get(&cw1200_device.dev, vdd_name); + if (IS_ERR(vdd)) { + ret = PTR_ERR(vdd); + dev_warn(&cw1200_device.dev, + "%s: Failed to get regulator '%s': %d\n", + __func__, vdd_name, ret); + } else { + if (enable) + ret = regulator_enable(vdd); + else + ret = regulator_disable(vdd); + + if (ret) { + dev_warn(&cw1200_device.dev, + "%s: Failed to %s regulator '%s': %d\n", + __func__, enable ? "enable" : "disable", + vdd_name, ret); + } + regulator_put(vdd); + } + return ret; +} + int __init mop500_wlan_init(void) { int ret; @@ -128,6 +160,8 @@ int __init mop500_wlan_init(void) #endif /* #ifdef CONFIG_CW1200_USE_GPIO_IRQ */ cw1200_device.dev.release = cw1200_release; + if (machine_is_snowball()) + cw1200_platform_data.power_ctrl = cw1200_power_ctrl; ret = cw1200_pins_enable(true); if (WARN_ON(ret)) diff --git a/drivers/staging/cw1200/cw1200_plat.h b/drivers/staging/cw1200/cw1200_plat.h index 3b6acaff305..573bc06ac7f 100644 --- a/drivers/staging/cw1200/cw1200_plat.h +++ b/drivers/staging/cw1200/cw1200_plat.h @@ -8,11 +8,12 @@ #include struct cw1200_platform_data { - const char *regulator_vdd; - const char *regulator_vio; + struct platform_device *device; const char *mmc_id; const struct resource *irq; const struct resource *reset; + int (*power_ctrl)(const struct cw1200_platform_data *pdata, + bool enable); }; /* Declaration only. Should be implemented in arch/xxx/mach-yyy */ diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index 2b7d0a1eaf7..235ac67cb75 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -28,7 +28,7 @@ MODULE_AUTHOR("Dmitry Tarnyagin "); MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SDIO driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("cw1200"); +MODULE_ALIAS("cw1200_wlan"); struct sbus_priv { struct sdio_func *func; @@ -259,6 +259,9 @@ static int cw1200_sdio_on(const struct cw1200_platform_data *pdata) const struct resource *reset = pdata->reset; gpio_request(reset->start, reset->name); gpio_direction_output(reset->start, 1); + msleep(100); + gpio_set_value(reset->start, 0); + msleep(100); gpio_set_value(reset->start, 1); cw1200_detect_card(pdata); return 0; @@ -267,7 +270,7 @@ static int cw1200_sdio_on(const struct cw1200_platform_data *pdata) static int cw1200_sdio_reset(struct sbus_priv *self) { cw1200_sdio_off(self->pdata); - mdelay(1000); + msleep(1000); cw1200_sdio_on(self->pdata); return 0; } @@ -350,7 +353,7 @@ static void cw1200_sdio_disconnect(struct sdio_func *func) } static struct sdio_driver sdio_driver = { - .name = "cw1200", + .name = "cw1200_wlan", .id_table = if_sdio_ids, .probe = cw1200_sdio_probe, .remove = cw1200_sdio_disconnect, @@ -359,19 +362,31 @@ static struct sdio_driver sdio_driver = { /* Init Module function -> Called by insmod */ static int __init cw1200_sdio_init(void) { + const struct cw1200_platform_data *pdata; int ret; + pdata = cw1200_get_platform_data(); + ret = sdio_register_driver(&sdio_driver); if (ret) goto err_reg; - ret = cw1200_sdio_on(cw1200_get_platform_data()); + if (pdata->power_ctrl) { + ret = pdata->power_ctrl(pdata, true); + if (ret) + goto err_power; + } + + ret = cw1200_sdio_on(pdata); if (ret) goto err_on; return 0; err_on: + if (pdata->power_ctrl) + pdata->power_ctrl(pdata, false); +err_power: sdio_unregister_driver(&sdio_driver); err_reg: return ret; @@ -380,8 +395,12 @@ err_reg: /* Called at Driver Unloading */ static void __exit cw1200_sdio_exit(void) { + const struct cw1200_platform_data *pdata; + pdata = cw1200_get_platform_data(); sdio_unregister_driver(&sdio_driver); - cw1200_sdio_off(cw1200_get_platform_data()); + cw1200_sdio_off(pdata); + if (pdata->power_ctrl) + pdata->power_ctrl(pdata, false); } -- cgit v1.2.3 From 3af28b5f37358faa59dc15cfa3e5e17fa1c6a81c Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 14 Oct 2011 14:51:51 +0200 Subject: ux500: irq: wlan: Fix multi boards support & put board common parts to mach/irqs.h Signed-off-by: Philippe Langlais --- arch/arm/mach-ux500/board-mop500-wlan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/board-mop500-wlan.c b/arch/arm/mach-ux500/board-mop500-wlan.c index e73a35e7c76..65863efc78c 100644 --- a/arch/arm/mach-ux500/board-mop500-wlan.c +++ b/arch/arm/mach-ux500/board-mop500-wlan.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include "pins.h" #include "../drivers/staging/cw1200/cw1200_plat.h" -- cgit v1.2.3 From 9872c88ff6979c5b1454bf2bcd96c5ec7a4ff76d Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 26 May 2011 17:49:37 +0200 Subject: cw1200: Expose driver internals on DebugFS. Change-Id: I8dc24f029e78c9c2026dbe03f4a7111746316d70 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25613 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/Kconfig | 3 + drivers/staging/cw1200/Makefile | 21 ++- drivers/staging/cw1200/cw1200.h | 4 +- drivers/staging/cw1200/debug.c | 307 ++++++++++++++++++++++++++++++++++++++++ drivers/staging/cw1200/debug.h | 24 ++++ drivers/staging/cw1200/main.c | 7 +- 6 files changed, 360 insertions(+), 6 deletions(-) create mode 100644 drivers/staging/cw1200/debug.c create mode 100644 drivers/staging/cw1200/debug.h diff --git a/drivers/staging/cw1200/Kconfig b/drivers/staging/cw1200/Kconfig index 80a7d13682d..ed2d23e5d47 100644 --- a/drivers/staging/cw1200/Kconfig +++ b/drivers/staging/cw1200/Kconfig @@ -50,6 +50,9 @@ config CW1200_HT_SUPPORT menu "Driver debug features" depends on CW1200 +config CW1200_DEBUGFS + bool "Expose driver internals to DebugFS (DEVELOPMENT)" + config CW1200_BH_DEBUG bool "Enable low-level device communication logs (DEVELOPMENT)" diff --git a/drivers/staging/cw1200/Makefile b/drivers/staging/cw1200/Makefile index 01e35c2e03e..fae984d10ce 100644 --- a/drivers/staging/cw1200/Makefile +++ b/drivers/staging/cw1200/Makefile @@ -1,5 +1,18 @@ -cw1200_core-objs := fwio.o txrx.o main.o queue.o hwio.o bh.o wsm.o sta.o ap.o scan.o -cw1200_wlan-objs := cw1200_sdio.o +cw1200_core-y := \ + fwio.o \ + txrx.o \ + main.o \ + queue.o \ + hwio.o \ + bh.o \ + wsm.o \ + sta.o \ + ap.o \ + scan.o +cw1200_core-$(CONFIG_CW1200_DEBUGFS) += debug.o + +cw1200_wlan-y := cw1200_sdio.o + +obj-$(CONFIG_CW1200) += cw1200_core.o +obj-$(CONFIG_CW1200) += cw1200_wlan.o -obj-$(CONFIG_CW1200) += cw1200_core.o -obj-$(CONFIG_CW1200) += cw1200_wlan.o diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 23c201ad2a4..0c88bfa48da 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -32,6 +32,7 @@ /* extern */ struct sbus_ops; /* extern */ struct task_struct; +/* extern */ struct cw1200_debug_priv; #if defined(CONFIG_CW1200_TXRX_DEBUG) #define txrx_printk(...) printk(__VA_ARGS__) @@ -54,6 +55,7 @@ enum cw1200_join_status { struct cw1200_common { struct cw1200_queue tx_queue[4]; struct cw1200_queue_stats tx_queue_stats; + struct cw1200_debug_priv *debug; struct ieee80211_hw *hw; struct ieee80211_vif *vif; @@ -81,7 +83,7 @@ struct cw1200_common { /* BBP/MAC state */ struct ieee80211_rate *rates; u8 mac_addr[ETH_ALEN]; - struct ieee80211_channel *channel; + struct ieee80211_channel *channel; u8 bssid[ETH_ALEN]; struct wsm_edca_params edca; struct wsm_association_mode association_mode; diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c new file mode 100644 index 00000000000..c54e6391746 --- /dev/null +++ b/drivers/staging/cw1200/debug.c @@ -0,0 +1,307 @@ +/* + * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers + * DebugFS code + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#include +#include +#include "cw1200.h" +#include "debug.h" + +/* private */ +struct cw1200_debug_priv { + struct dentry *debugfs_phy; +}; + +/* join_status */ +static const char * const cw1200_debug_join_status[] = { + "passive", + "monitor", + "station", + "access point", +}; + +/* WSM_JOIN_PREAMBLE_... */ +static const char * const cw1200_debug_preamble[] = { + "long", + "short", + "long on 1 and 2 Mbps", +}; + +static const char * const cw1200_debug_fw_types[] = { + "ETF", + "WFM", + "WSM", + "HI test", + "Platform test", +}; + + +static const char *cw1200_debug_mode(int mode) +{ + switch (mode) { + case NL80211_IFTYPE_UNSPECIFIED: + return "unspecified"; + case NL80211_IFTYPE_MONITOR: + return "monitor"; + case NL80211_IFTYPE_STATION: + return "station"; + case NL80211_IFTYPE_ADHOC: + return "ad-hok"; + case NL80211_IFTYPE_MESH_POINT: + return "mesh point"; + case NL80211_IFTYPE_AP: + return "access point"; + case NL80211_IFTYPE_P2P_CLIENT: + return "p2p client"; + case NL80211_IFTYPE_P2P_GO: + return "p2p go"; + default: + return "unsupported"; + } +} + +static void cw1200_queue_status_show(struct seq_file *seq, + struct cw1200_queue *q) +{ + int i; + seq_printf(seq, "Queue %d:\n", q->queue_id); + seq_printf(seq, " capacity: %d\n", q->capacity); + seq_printf(seq, " queued: %d\n", q->num_queued); + seq_printf(seq, " pending: %d\n", q->num_pending); + seq_printf(seq, " sent: %d\n", q->num_sent); + seq_printf(seq, " locked: %s\n", q->tx_locked_cnt ? "yes" : "no"); + seq_printf(seq, " overfull: %s\n", q->overfull ? "yes" : "no"); + seq_puts(seq, " link map: 0-> "); + for (i = 0; i < q->stats->map_capacity; ++i) + seq_printf(seq, "%.2d ", q->link_map_cache[i]); + seq_printf(seq, "<-%d\n", q->stats->map_capacity); +} + +static void cw1200_debug_print_map(struct seq_file *seq, + struct cw1200_common *priv, + const char *label, + u32 map) +{ + int i; + seq_printf(seq, "%s0-> ", label); + for (i = 0; i < priv->tx_queue_stats.map_capacity; ++i) + seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : ".."); + seq_printf(seq, "<-%d\n", priv->tx_queue_stats.map_capacity); +} + +static int cw1200_status_show(struct seq_file *seq, void *v) +{ + int i; + struct cw1200_common *priv = seq->private; + seq_puts(seq, "CW1200 Wireless LAN driver status\n"); + seq_printf(seq, "Hardware: %d.%d\n", + priv->wsm_caps.hardwareId, + priv->wsm_caps.hardwareSubId); + seq_printf(seq, "Firmware: %s %d.%d\n", + cw1200_debug_fw_types[priv->wsm_caps.firmwareType], + priv->wsm_caps.firmwareVersion, + priv->wsm_caps.firmwareBuildNumber); + seq_printf(seq, "FW API: %d\n", + priv->wsm_caps.firmwareApiVer); + seq_printf(seq, "FW caps: 0x%.4X\n", + priv->wsm_caps.firmwareCap); + seq_printf(seq, "Mode: %s%s\n", + cw1200_debug_mode(priv->mode), + priv->listening ? " (listening)" : ""); + seq_printf(seq, "Assoc: %s\n", + cw1200_debug_join_status[priv->join_status]); + if (priv->channel) + seq_printf(seq, "Channel: %d%s\n", + priv->channel->hw_value, + priv->channel_switch_in_progress ? + " (switching)" : ""); + if (priv->rx_filter.promiscuous) + seq_puts(seq, "Filter: promisc\n"); + else if (priv->rx_filter.fcs) + seq_puts(seq, "Filter: fcs\n"); + if (priv->rx_filter.bssid) + seq_puts(seq, "Filter: bssid\n"); + if (priv->bf_control.bcn_count) + seq_puts(seq, "Filter: beacons\n"); + + if (priv->enable_beacon || + priv->mode == NL80211_IFTYPE_AP || + priv->mode == NL80211_IFTYPE_ADHOC || + priv->mode == NL80211_IFTYPE_MESH_POINT || + priv->mode == NL80211_IFTYPE_P2P_GO) + seq_printf(seq, "Beaconing: %s\n", + priv->enable_beacon ? + "enabled" : "disabled"); + if (priv->ssid_length || + priv->mode == NL80211_IFTYPE_AP || + priv->mode == NL80211_IFTYPE_ADHOC || + priv->mode == NL80211_IFTYPE_MESH_POINT || + priv->mode == NL80211_IFTYPE_P2P_GO) + seq_printf(seq, "SSID: %.*s\n", + priv->ssid_length, priv->ssid); + + for (i = 0; i < 4; ++i) { + seq_printf(seq, "EDCA(%d): %d, %d, %d, %d, %d\n", i, + priv->edca.params[i].cwMin, + priv->edca.params[i].cwMax, + priv->edca.params[i].aifns, + priv->edca.params[i].txOpLimit, + priv->edca.params[i].maxReceiveLifetime); + } + if (priv->join_status == CW1200_JOIN_STATUS_STA) { + seq_printf(seq, "Preamble: %s\n", + cw1200_debug_preamble[ + priv->association_mode.preambleType]); + seq_printf(seq, "AMPDU spcn: %d\n", + priv->association_mode.mpduStartSpacing); + seq_printf(seq, "Basic rate: 0x%.8X\n", + le32_to_cpu(priv->association_mode.basicRateSet)); + seq_printf(seq, "Bss lost: %d beacons\n", + priv->bss_params.beaconLostCount); + seq_printf(seq, "AID: %d\n", + priv->bss_params.aid); + seq_printf(seq, "Rates: 0x%.8X\n", + priv->bss_params.operationalRateSet); + seq_printf(seq, "Powersave: %s\n", + priv->powersave_mode.pmMode ? "off" : "on"); + } + seq_printf(seq, "HT: %s\n", + cw1200_is_ht(&priv->ht_info) ? "on" : "off"); + if (cw1200_is_ht(&priv->ht_info)) { + seq_printf(seq, "Greenfield: %s\n", + cw1200_ht_greenfield(&priv->ht_info) ? "yes" : "no"); + seq_printf(seq, "AMPDU dens: %d\n", + cw1200_ht_ampdu_density(&priv->ht_info)); + } + seq_printf(seq, "RSSI thold: %d\n", + priv->cqm_rssi_thold); + seq_printf(seq, "RSSI hyst: %d\n", + priv->cqm_rssi_hyst); + seq_printf(seq, "TXFL thold: %d\n", + priv->cqm_tx_failure_thold); + seq_printf(seq, "Linkloss: %d\n", + priv->cqm_link_loss_count); + seq_printf(seq, "Bcnloss: %d\n", + priv->cqm_beacon_loss_count); + seq_printf(seq, "Long retr: %d\n", + priv->long_frame_max_tx_count); + seq_printf(seq, "Short retr: %d\n", + priv->short_frame_max_tx_count); + + seq_puts(seq, "\n"); + for (i = 0; i < 4; ++i) { + char buf[32]; + snprintf(buf, sizeof(buf), "TX lock(%d): ", i); + cw1200_queue_status_show(seq, &priv->tx_queue[i]); + cw1200_debug_print_map(seq, priv, buf, + priv->tx_suspend_mask[i]); + seq_puts(seq, "\n"); + } + + cw1200_debug_print_map(seq, priv, "Link map: ", + priv->link_id_map); + cw1200_debug_print_map(seq, priv, "Asleep map: ", + priv->sta_asleep_mask); + + seq_puts(seq, "\n"); + + seq_printf(seq, "BH status: %s\n", + atomic_read(&priv->bh_term) ? "terminated" : "alive"); + seq_printf(seq, "Pending RX: %d\n", + atomic_read(&priv->bh_rx)); + seq_printf(seq, "Pending TX: %d\n", + atomic_read(&priv->bh_tx)); + if (priv->bh_error) + seq_printf(seq, "BH errcode: %d\n", + priv->bh_error); + seq_printf(seq, "TX bufs: %d x %d bytes\n", + priv->wsm_caps.numInpChBufs, + priv->wsm_caps.sizeInpChBuf); + seq_printf(seq, "Used bufs: %d\n", + priv->hw_bufs_used); + seq_printf(seq, "Powermgmt: %s\n", + priv->powersave_enabled ? "on" : "off"); + seq_printf(seq, "Device: %s\n", + priv->device_can_sleep ? "alseep" : "awake"); + + spin_lock(&priv->wsm_cmd.lock); + seq_printf(seq, "WSM status: %s\n", + priv->wsm_cmd.done ? "idle" : "active"); + seq_printf(seq, "WSM cmd: 0x%.4X (%d bytes)\n", + priv->wsm_cmd.cmd, priv->wsm_cmd.len); + seq_printf(seq, "WSM retval: %d\n", + priv->wsm_cmd.ret); + spin_unlock(&priv->wsm_cmd.lock); + + seq_printf(seq, "Datapath: %s\n", + atomic_read(&priv->tx_lock) ? "locked" : "unlocked"); + if (atomic_read(&priv->tx_lock)) + seq_printf(seq, "TXlock cnt: %d\n", + atomic_read(&priv->tx_lock)); + + seq_printf(seq, "Scan: %s\n", + atomic_read(&priv->scan.in_progress) ? "active" : "idle"); + seq_printf(seq, "Led state: 0x%.2X\n", + priv->softled_state); + + return 0; +} + +static int cw1200_status_open(struct inode *inode, struct file *file) +{ + return single_open(file, &cw1200_status_show, + inode->i_private); +} + +static const struct file_operations fops_status = { + .open = cw1200_status_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +int cw1200_debug_init(struct cw1200_common *priv) +{ + struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv), + GFP_KERNEL); + priv->debug = d; + if (!d) + return -ENOMEM; + + d->debugfs_phy = debugfs_create_dir("cw1200", + priv->hw->wiphy->debugfsdir); + if (!d->debugfs_phy) + goto err; + + if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy, + priv, &fops_status)) + goto err; + + return 0; + +err: + priv->debug = NULL; + debugfs_remove_recursive(d->debugfs_phy); + kfree(d); + return -ENOMEM; +} + +void cw1200_debug_release(struct cw1200_common *priv) +{ + struct cw1200_debug_priv *d = priv->debug; + priv->debug = NULL; + + if (d) { + debugfs_remove_recursive(d->debugfs_phy); + kfree(d); + } +} diff --git a/drivers/staging/cw1200/debug.h b/drivers/staging/cw1200/debug.h new file mode 100644 index 00000000000..3d34bcee541 --- /dev/null +++ b/drivers/staging/cw1200/debug.h @@ -0,0 +1,24 @@ +#ifndef CW1200_DEBUG_H_INCLUDED +#define CW1200_DEBUG_H_INCLUDED + +struct cw200_common; + +#ifdef CONFIG_CW1200_DEBUGFS + +int cw1200_debug_init(struct cw1200_common *priv); +void cw1200_debug_release(struct cw1200_common *priv); + +#else /* CONFIG_CW1200_DEBUGFS */ + +static inline int cw1200_debug_init(struct cw1200_common *priv) +{ + return 0; +} + +static inline void cw1200_debug_release(struct cw1200_common *priv) +{ +} + +#endif /* CONFIG_CW1200_DEBUGFS */ + +#endif /* CW1200_DEBUG_H_INCLUDED */ diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index a09832c7343..047a7422767 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -38,6 +38,7 @@ #include "sta.h" #include "ap.h" #include "scan.h" +#include "debug.h" MODULE_AUTHOR("Dmitry Tarnyagin "); MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code"); @@ -341,7 +342,7 @@ EXPORT_SYMBOL_GPL(cw1200_init_common); int cw1200_register_common(struct ieee80211_hw *dev) { - /* struct cw1200_common *priv = dev->priv; */ + struct cw1200_common *priv = dev->priv; int err; err = ieee80211_register_hw(dev); @@ -357,6 +358,8 @@ int cw1200_register_common(struct ieee80211_hw *dev) return err; #endif /* CONFIG_CW1200_LEDS */ + cw1200_debug_init(priv); + cw1200_dbg(CW1200_DBG_MSG, "is registered as '%s'\n", wiphy_name(dev->wiphy)); return 0; @@ -377,6 +380,8 @@ void cw1200_unregister_common(struct ieee80211_hw *dev) struct cw1200_common *priv = dev->priv; int i; + cw1200_debug_release(priv); + priv->sbus_ops->irq_unsubscribe(priv->sbus_priv); cw1200_unregister_bh(priv); -- cgit v1.2.3 From e475ca203beafd81adec4ff8693ab6691e81122e Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Mon, 30 May 2011 08:03:34 +0200 Subject: cw1200: throughput optimization. * An expencive workaround against a bug in really old hardware is removed. * IEEE80211_HW_AMPDU_AGGREGATION is set in the device capability flags. * Michael MIC is stripped by the driver: avoiding double-check by the mac80211 stack. Signed-off-by: Dmitry Tarnyagin Change-Id: Iaf07e3f675685208c1c66a5faa6219ccbc893238 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25614 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/bh.c | 8 -------- drivers/staging/cw1200/main.c | 3 +++ drivers/staging/cw1200/sta.c | 13 ++++++++++--- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index 5f92caa2708..cc27e8b4aa5 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -359,14 +359,6 @@ rx: } read_len = 0; - - { - /* HACK!!! */ - /* Read CONFIG Register Value - HW BUG */ - u32 val32; - WARN_ON(cw1200_reg_read_32(priv, - ST90TDS_CONFIG_REG_ID, &val32)); - } } tx: diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 047a7422767..da61ba165cf 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -246,6 +246,9 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) /* IEEE80211_HW_SUPPORTS_UAPSD | */ IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_CQM_RSSI | +#if defined(CONFIG_CW1200_HT_SUPPORT) + IEEE80211_HW_AMPDU_AGGREGATION | +#endif #if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) IEEE80211_HW_SUPPORTS_CQM_BEACON_MISS | IEEE80211_HW_SUPPORTS_CQM_TX_FAIL | diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 5f42e25c2fa..5f30cb4c338 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -696,13 +696,20 @@ void cw1200_rx_cb(struct cw1200_common *priv, hdr->signal = (s8)arg->rcpiRssi; hdr->antenna = 0; - if (arg->flags & 0x07) + if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { hdr->flag |= RX_FLAG_DECRYPTED; - if (arg->flags & BIT(14)) + if (!arg->status && + (WSM_RX_STATUS_ENCRYPTION(arg->flags) == + WSM_RX_STATUS_TKIP)) { + hdr->flag |= RX_FLAG_MMIC_STRIPPED; + skb_trim(skb, skb->len - 8 /*MICHAEL_MIC_LEN*/); + } + } + if (arg->flags & WSM_RX_STATUS_HT) hdr->flag |= RX_FLAG_HT; #if 0 /* Wrong: ACK could be disable for this ACL */ - if (arg->flags & BIT(16)) + if (arg->flags & WSM_RX_STATUS_ADDRESS1) priv->last_activity_time = jiffies; #endif -- cgit v1.2.3 From 1fd7746f0813a878a1b3310ddd3ccca3bcd05603 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Mon, 30 May 2011 08:15:00 +0200 Subject: cw1200: fix for locked datapath after deauth. When AP sent more than one deauth requests in a row, datapath was remaining locked after deauthentication/unjoin. Result code of queue_work() was not checked and datapath was not unlocked if unjoin_work was already pending execution. Signed-off-by: Dmitry Tarnyagin Change-Id: I9c8394acd75e1aa828fad04c33bda118893ba031 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25615 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/sta.c | 3 ++- drivers/staging/cw1200/wsm.c | 14 ++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 5f30cb4c338..8f1c59a6766 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -116,7 +116,8 @@ void cw1200_stop(struct ieee80211_hw *dev) switch (priv->join_status) { case CW1200_JOIN_STATUS_STA: wsm_lock_tx(priv); - queue_work(priv->workqueue, &priv->unjoin_work); + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); break; case CW1200_JOIN_STATUS_AP: /* If you see this warning please change the code to iterate diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index fa7fb2c246a..028e668f137 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -818,7 +818,9 @@ static int wsm_receive_indication(struct cw1200_common *priv, wsm_printk(KERN_DEBUG \ "[WSM] Issue unjoin command (RX).\n"); wsm_lock_tx_async(priv); - queue_work(priv->workqueue, &priv->unjoin_work); + if (queue_work(priv->workqueue, + &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); } } priv->wsm_cbc.rx(priv, &rx, skb_p); @@ -1310,7 +1312,8 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, wsm_lock_tx_async(priv); BUG_ON(priv->join_pending_frame); priv->join_pending_frame = wsm; - queue_work(priv->workqueue, &priv->join_work); + if (queue_work(priv->workqueue, &priv->join_work) <= 0) + wsm_unlock_tx(priv); handled = true; } break; @@ -1319,7 +1322,8 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, wsm_printk(KERN_DEBUG "[WSM] Issue set_default_wep_key.\n"); wsm_lock_tx_async(priv); priv->wep_default_key_id = tx_info->control.hw_key->keyidx; - queue_work(priv->workqueue, &priv->wep_key_work); + if (queue_work(priv->workqueue, &priv->wep_key_work) <= 0) + wsm_unlock_tx(priv); handled = true; } break; @@ -1357,7 +1361,9 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, wsm->more = 0; #endif /* 0 */ wsm_lock_tx_async(priv); - queue_work(priv->workqueue, &priv->unjoin_work); + if (queue_work(priv->workqueue, + &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); } } break; -- cgit v1.2.3 From c4a23e151cf34bd77ca90dd0ca632bf846b23465 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Mon, 30 May 2011 16:21:05 +0200 Subject: cw1200: Support of WSM293+ firmware in STA mode. WSM299 firmware is more strict in terms of when driver is allowed to send "Set PM" request. Change is done to move power management after join. Change-Id: I211ab9cf9ad3dc40f551e1b09f7b3d9179e02c74 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24297 Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Janusz DZIEDZIC Reviewed-by: Robert MARKLUND Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25616 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/sta.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 8f1c59a6766..2de6156e100 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -287,7 +287,8 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) priv->powersave_mode.pmMode = (conf->flags & IEEE80211_CONF_PS) ? WSM_PSM_PS : WSM_PSM_ACTIVE; - WARN_ON(wsm_set_pm(priv, &priv->powersave_mode)); + if (priv->join_status == CW1200_JOIN_STATUS_STA) + WARN_ON(wsm_set_pm(priv, &priv->powersave_mode)); } if (changed & IEEE80211_CONF_CHANGE_MONITOR) { @@ -1065,6 +1066,7 @@ void cw1200_join_work(struct work_struct *work) cancel_delayed_work_sync(&priv->keep_alive_work); #endif /* CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ cw1200_update_listening(priv, priv->listening); + WARN_ON(wsm_set_pm(priv, &priv->powersave_mode)); } else { WARN_ON(cw1200_upload_keys(priv)); #if !defined(CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) -- cgit v1.2.3 From 4f82de7cf8b9649e34bafd9a48b3ad04cf743305 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 2 Jun 2011 23:33:42 +0200 Subject: cw1200: AMSDU debug statistics MSDU aggregation statistics are exposed to DebugFS. Change-Id: Ifb99ab3a206d50a3f86b791fbc505de0e9b64371 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24477 Reviewed-by: Robert MARKLUND Tested-by: Robert MARKLUND Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25617 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/debug.c | 17 +++++++++---- drivers/staging/cw1200/debug.h | 54 ++++++++++++++++++++++++++++++++++++++++++ drivers/staging/cw1200/sta.c | 5 ++++ drivers/staging/cw1200/txrx.c | 6 +++++ 4 files changed, 77 insertions(+), 5 deletions(-) diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index c54e6391746..47e230f460d 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -15,11 +15,6 @@ #include "cw1200.h" #include "debug.h" -/* private */ -struct cw1200_debug_priv { - struct dentry *debugfs_phy; -}; - /* join_status */ static const char * const cw1200_debug_join_status[] = { "passive", @@ -101,6 +96,7 @@ static int cw1200_status_show(struct seq_file *seq, void *v) { int i; struct cw1200_common *priv = seq->private; + struct cw1200_debug_priv *d = priv->debug; seq_puts(seq, "CW1200 Wireless LAN driver status\n"); seq_printf(seq, "Hardware: %d.%d\n", priv->wsm_caps.hardwareId, @@ -247,6 +243,17 @@ static int cw1200_status_show(struct seq_file *seq, void *v) seq_printf(seq, "TXlock cnt: %d\n", atomic_read(&priv->tx_lock)); + seq_printf(seq, "TXed: %d\n", + d->tx); + seq_printf(seq, "AGG TXed: %d\n", + d->tx_agg); + seq_printf(seq, "MORE TXed: %d\n", + d->tx_more); + seq_printf(seq, "RXed: %d\n", + d->rx); + seq_printf(seq, "AGG RXed: %d\n", + d->rx_agg); + seq_printf(seq, "Scan: %s\n", atomic_read(&priv->scan.in_progress) ? "active" : "idle"); seq_printf(seq, "Led state: 0x%.2X\n", diff --git a/drivers/staging/cw1200/debug.h b/drivers/staging/cw1200/debug.h index 3d34bcee541..c813c54f86b 100644 --- a/drivers/staging/cw1200/debug.h +++ b/drivers/staging/cw1200/debug.h @@ -5,9 +5,43 @@ struct cw200_common; #ifdef CONFIG_CW1200_DEBUGFS +struct cw1200_debug_priv { + struct dentry *debugfs_phy; + int tx; + int tx_agg; + int tx_more; + int rx; + int rx_agg; +}; + int cw1200_debug_init(struct cw1200_common *priv); void cw1200_debug_release(struct cw1200_common *priv); +static inline void cw1200_debug_txed(struct cw1200_common *priv) +{ + ++priv->debug->tx; +} + +static inline void cw1200_debug_txed_agg(struct cw1200_common *priv) +{ + ++priv->debug->tx_agg; +} + +static inline void cw1200_debug_txed_more(struct cw1200_common *priv) +{ + ++priv->debug->tx_more; +} + +static inline void cw1200_debug_rxed(struct cw1200_common *priv) +{ + ++priv->debug->rx; +} + +static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv) +{ + ++priv->debug->rx_agg; +} + #else /* CONFIG_CW1200_DEBUGFS */ static inline int cw1200_debug_init(struct cw1200_common *priv) @@ -19,6 +53,26 @@ static inline void cw1200_debug_release(struct cw1200_common *priv) { } +static inline void cw1200_debug_txed(struct cw1200_common *priv) +{ +} + +static inline void cw1200_debug_txed_agg(struct cw1200_common *priv) +{ +} + +static inline void cw1200_debug_txed_more(struct cw1200_common *priv) +{ +} + +static inline void cw1200_debug_rxed(struct cw1200_common *priv) +{ +} + +static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv) +{ +} + #endif /* CONFIG_CW1200_DEBUGFS */ #endif /* CW1200_DEBUG_H_INCLUDED */ diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 2de6156e100..af6eabf2d49 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -17,6 +17,7 @@ #include "sta.h" #include "fwio.h" #include "bh.h" +#include "debug.h" #if defined(CONFIG_CW1200_STA_DEBUG) #define sta_printk(...) printk(__VA_ARGS__) @@ -709,6 +710,10 @@ void cw1200_rx_cb(struct cw1200_common *priv, } if (arg->flags & WSM_RX_STATUS_HT) hdr->flag |= RX_FLAG_HT; + + cw1200_debug_rxed(priv); + if (arg->flags & WSM_RX_STATUS_AGGREGATE) + cw1200_debug_rxed_agg(priv); #if 0 /* Wrong: ACK could be disable for this ACL */ if (arg->flags & WSM_RX_STATUS_ADDRESS1) diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 3435a7b59da..fd04156dcb5 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -14,6 +14,7 @@ #include "cw1200.h" #include "wsm.h" #include "bh.h" +#include "debug.h" #if defined(CONFIG_CW1200_TX_POLICY_DEBUG) #define tx_policy_printk(...) printk(__VA_ARGS__) @@ -524,6 +525,11 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, #endif /* CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ priv->cqm_tx_failure_count = 0; ++tx_count; + cw1200_debug_txed(priv); + if (arg->flags & WSM_TX_STATUS_AGGREGATION) { + tx->flags |= IEEE80211_TX_STAT_AMPDU; + cw1200_debug_txed_agg(priv); + } } else { /* TODO: Update TX failure counters */ if (unlikely(priv->cqm_tx_failure_thold && -- cgit v1.2.3 From fd96b811932ab8394dcf115ce216c0daf668a53c Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Sat, 4 Jun 2011 14:55:21 +0200 Subject: cw1200: Multi-tx confirmation is implemented. Multi-tx offloads SDIO interface by reducing number of TX confirm messages. Multiple PDUs are acknowledged by a single multi-tx confirm message. Change-Id: Ie152a2dc9fc3ca18e2a8042965f626a6c2ec6409 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24478 Reviewed-by: Robert MARKLUND Tested-by: Robert MARKLUND Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25618 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/bh.c | 20 +++++++-------- drivers/staging/cw1200/bh.h | 1 + drivers/staging/cw1200/debug.c | 2 ++ drivers/staging/cw1200/debug.h | 14 +++++++++++ drivers/staging/cw1200/main.c | 3 +++ drivers/staging/cw1200/sta.c | 3 ++- drivers/staging/cw1200/wsm.c | 57 +++++++++++++++++++++++++++++++++--------- drivers/staging/cw1200/wsm.h | 14 +++++++++++ 8 files changed, 90 insertions(+), 24 deletions(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index cc27e8b4aa5..177274a00d2 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -112,15 +112,17 @@ static inline void wsm_alloc_tx_buffer(struct cw1200_common *priv) ++priv->hw_bufs_used; } -static int wsm_release_tx_buffer(struct cw1200_common *priv) +int wsm_release_tx_buffer(struct cw1200_common *priv, int count) { int ret = 0; - int hw_bufs_used = priv->hw_bufs_used--; - if (WARN_ON(!hw_bufs_used)) + int hw_bufs_used = priv->hw_bufs_used; + + priv->hw_bufs_used -= count; + if (WARN_ON(priv->hw_bufs_used < 0)) ret = -1; else if (hw_bufs_used >= priv->wsm_caps.numInpChBufs - 1) ret = 1; - else if (hw_bufs_used == 1) + if (!priv->hw_bufs_used) wake_up_interruptible(&priv->hw_bufs_used_wq); return ret; } @@ -338,11 +340,7 @@ rx: rx_resync = 0; if (wsm_id & 0x0400) { - int rc = wsm_release_tx_buffer(priv); - /* TODO: 3.60 Multi-transmit confirmation - * requires special handling. - * Not supported yet. */ - BUG_ON((wsm_id & 0x3F) == 0x1E); + int rc = wsm_release_tx_buffer(priv, 1); if (WARN_ON(rc < 0)) break; else if (rc > 0) @@ -390,7 +388,7 @@ tx: wsm_alloc_tx_buffer(priv); ret = wsm_get_tx(priv, &data, &tx_len); if (ret <= 0) { - wsm_release_tx_buffer(priv); + wsm_release_tx_buffer(priv, 1); if (WARN_ON(ret < 0)) break; } else { @@ -425,7 +423,7 @@ tx: if (WARN_ON(cw1200_data_write(priv, data, tx_len))) { - wsm_release_tx_buffer(priv); + wsm_release_tx_buffer(priv, 1); break; } diff --git a/drivers/staging/cw1200/bh.h b/drivers/staging/cw1200/bh.h index af71a5a1066..14b641fbe1d 100644 --- a/drivers/staging/cw1200/bh.h +++ b/drivers/staging/cw1200/bh.h @@ -25,5 +25,6 @@ void cw1200_bh_wakeup(struct cw1200_common *priv); /* Must be called from BH thread. */ void cw1200_enable_powersave(struct cw1200_common *priv, bool enable); +int wsm_release_tx_buffer(struct cw1200_common *priv, int count); #endif /* CW1200_BH_H */ diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 47e230f460d..498f072da78 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -249,6 +249,8 @@ static int cw1200_status_show(struct seq_file *seq, void *v) d->tx_agg); seq_printf(seq, "MORE TXed: %d\n", d->tx_more); + seq_printf(seq, "MULTI TXed: %d (%d)\n", + d->tx_multi, d->tx_multi_frames); seq_printf(seq, "RXed: %d\n", d->rx); seq_printf(seq, "AGG RXed: %d\n", diff --git a/drivers/staging/cw1200/debug.h b/drivers/staging/cw1200/debug.h index c813c54f86b..0c9ef1252c4 100644 --- a/drivers/staging/cw1200/debug.h +++ b/drivers/staging/cw1200/debug.h @@ -12,6 +12,8 @@ struct cw1200_debug_priv { int tx_more; int rx; int rx_agg; + int tx_multi; + int tx_multi_frames; }; int cw1200_debug_init(struct cw1200_common *priv); @@ -32,6 +34,13 @@ static inline void cw1200_debug_txed_more(struct cw1200_common *priv) ++priv->debug->tx_more; } +static inline void cw1200_debug_txed_multi(struct cw1200_common *priv, + int count) +{ + ++priv->debug->tx_multi; + priv->debug->tx_multi_frames += count; +} + static inline void cw1200_debug_rxed(struct cw1200_common *priv) { ++priv->debug->rx; @@ -65,6 +74,11 @@ static inline void cw1200_debug_txed_more(struct cw1200_common *priv) { } +static inline void cw1200_debug_txed_multi(struct cw1200_common *priv, + int count) +{ +} + static inline void cw1200_debug_rxed(struct cw1200_common *priv) { } diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index da61ba165cf..0a1193b8b41 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -465,6 +465,9 @@ int cw1200_probe(const struct sbus_ops *sbus_ops, /* Set low-power mode. */ WARN_ON(wsm_set_operational_mode(priv, &mode)); + /* Enable multi-TX confirmation */ + WARN_ON(wsm_use_multi_tx_conf(priv, true)); + err = cw1200_register_common(dev); if (err) { priv->sbus_ops->irq_unsubscribe(priv->sbus_priv); diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index af6eabf2d49..145a370dc50 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -728,7 +728,7 @@ void cw1200_rx_cb(struct cw1200_common *priv, /* Not that we really need _irqsafe variant here, * but it offloads realtime bh thread and improve * system performance. */ - ieee80211_rx_irqsafe(priv->hw, skb); + ieee80211_rx(priv->hw, skb); *skb_p = NULL; } @@ -1073,6 +1073,7 @@ void cw1200_join_work(struct work_struct *work) cw1200_update_listening(priv, priv->listening); WARN_ON(wsm_set_pm(priv, &priv->powersave_mode)); } else { + /* Upload keys */ WARN_ON(cw1200_upload_keys(priv)); #if !defined(CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) WARN_ON(wsm_keep_alive_period(priv, 30 /* sec */)); diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 028e668f137..c562f982477 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -19,6 +19,7 @@ #include "cw1200.h" #include "wsm.h" #include "bh.h" +#include "debug.h" #if defined(CONFIG_CW1200_WSM_DEBUG) #define wsm_printk(...) printk(__VA_ARGS__) @@ -362,18 +363,18 @@ int wsm_stop_scan(struct cw1200_common *priv) static int wsm_tx_confirm(struct cw1200_common *priv, struct wsm_buf *buf) { - if (priv->wsm_cbc.tx_confirm) { - struct wsm_tx_confirm tx_confirm; - - tx_confirm.packetID = WSM_GET32(buf); - tx_confirm.status = WSM_GET32(buf); - tx_confirm.txedRate = WSM_GET8(buf); - tx_confirm.ackFailures = WSM_GET8(buf); - tx_confirm.flags = WSM_GET16(buf); - tx_confirm.mediaDelay = WSM_GET32(buf); - tx_confirm.txQueueDelay = WSM_GET32(buf); + struct wsm_tx_confirm tx_confirm; + + tx_confirm.packetID = WSM_GET32(buf); + tx_confirm.status = WSM_GET32(buf); + tx_confirm.txedRate = WSM_GET8(buf); + tx_confirm.ackFailures = WSM_GET8(buf); + tx_confirm.flags = WSM_GET16(buf); + tx_confirm.mediaDelay = WSM_GET32(buf); + tx_confirm.txQueueDelay = WSM_GET32(buf); + + if (priv->wsm_cbc.tx_confirm) priv->wsm_cbc.tx_confirm(priv, &tx_confirm); - } return 0; underflow: @@ -381,6 +382,37 @@ underflow: return -EINVAL; } +static int wsm_multi_tx_confirm(struct cw1200_common *priv, + struct wsm_buf *buf) +{ + int ret; + int count; + int i; + + count = WSM_GET32(buf); + if (WARN_ON(count <= 0)) + return -EINVAL; + else if (count > 1) { + ret = wsm_release_tx_buffer(priv, count - 1); + if (ret < 0) + return ret; + else if (ret > 0) + cw1200_bh_wakeup(priv); + } + + cw1200_debug_txed_multi(priv, count); + for (i = 0; i < count; ++i) { + ret = wsm_tx_confirm(priv, buf); + if (ret) + return ret; + } + return ret; + +underflow: + WARN_ON(1); + return -EINVAL; +} + /* ******************************************************************** */ static int wsm_join_confirm(struct cw1200_common *priv, @@ -1085,7 +1117,8 @@ int wsm_handle_rx(struct cw1200_common *priv, int id, if (id == 0x404) { ret = wsm_tx_confirm(priv, &wsm_buf); - cw1200_bh_wakeup(priv); + } else if (id == 0x41E) { + ret = wsm_multi_tx_confirm(priv, &wsm_buf); } else if (id & 0x0400) { void *wsm_arg; u16 wsm_cmd; diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index fd84b147fde..4d446e9b924 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -471,6 +471,9 @@ struct cw1200_common; /* Test Purposes Only */ #define WSM_MIB_ID_BLOCK_ACK_INFO 0x100D +/* 4.42 UseMultiTxConfMessage */ +#define WSM_MIB_USE_MULTI_TX_CONF 0x1024 + /* 4.43 Keep-alive period */ #define WSM_MIB_ID_KEEP_ALIVE_PERIOD 0x1025 @@ -1443,6 +1446,17 @@ static inline int wsm_set_bssid_filtering(struct cw1200_common *priv, &arg, sizeof(arg)); } +/* UseMultiTxConfMessage */ + +static inline int wsm_use_multi_tx_conf(struct cw1200_common *priv, + bool enabled) +{ + __le32 arg = enabled ? __cpu_to_le32(1) : 0; + + return wsm_write_mib(priv, WSM_MIB_USE_MULTI_TX_CONF, + &arg, sizeof(arg)); +} + /* ******************************************************************** */ /* WSM TX port control */ -- cgit v1.2.3 From 9fec5d6da4cd356ed76513f9261b27f7af0da782 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Sat, 4 Jun 2011 17:08:40 +0200 Subject: cw1200: stylistic change: move RX callback to txrx.c WSM RX callback is moved form sta.c to txrx. Change-Id: Id56e07c3b71d8a2fb52b3b974feaed8c48364390 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24479 Reviewed-by: Robert MARKLUND Tested-by: Robert MARKLUND Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25619 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/sta.c | 71 ------------------------------------------- drivers/staging/cw1200/sta.h | 3 -- drivers/staging/cw1200/txrx.c | 60 ++++++++++++++++++++++++++++++++++++ drivers/staging/cw1200/txrx.h | 3 ++ 4 files changed, 63 insertions(+), 74 deletions(-) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 145a370dc50..60a3c57100b 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -661,77 +661,6 @@ void cw1200_flush(struct ieee80211_hw *hw, bool drop) /* ******************************************************************** */ /* WSM callbacks */ -/* TODO: move to txrx.c */ -void cw1200_rx_cb(struct cw1200_common *priv, - struct wsm_rx *arg, - struct sk_buff **skb_p) -{ - struct sk_buff *skb = *skb_p; - struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); - hdr->flag = 0; - - if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { - /* STA is stopped. */ - return; - } - - if (unlikely(arg->status)) { - if (arg->status == WSM_STATUS_MICFAILURE) { - sta_printk(KERN_DEBUG "[RX] MIC failure.\n"); - hdr->flag |= RX_FLAG_MMIC_ERROR; - } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) { - sta_printk(KERN_DEBUG "[RX] No key found.\n"); - return; - } else { - sta_printk(KERN_DEBUG "[RX] Receive failure: %d.\n", - arg->status); - return; - } - } - - hdr->mactime = 0; /* Not supported by WSM */ - hdr->freq = ieee80211_channel_to_frequency(arg->channelNumber); - hdr->band = (hdr->freq >= 5000) ? - IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; - hdr->rate_idx = arg->rxedRate; - if (hdr->rate_idx >= 4) /* TODO: Use common convert function. */ - hdr->rate_idx -= 2; - hdr->signal = (s8)arg->rcpiRssi; - hdr->antenna = 0; - - if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { - hdr->flag |= RX_FLAG_DECRYPTED; - if (!arg->status && - (WSM_RX_STATUS_ENCRYPTION(arg->flags) == - WSM_RX_STATUS_TKIP)) { - hdr->flag |= RX_FLAG_MMIC_STRIPPED; - skb_trim(skb, skb->len - 8 /*MICHAEL_MIC_LEN*/); - } - } - if (arg->flags & WSM_RX_STATUS_HT) - hdr->flag |= RX_FLAG_HT; - - cw1200_debug_rxed(priv); - if (arg->flags & WSM_RX_STATUS_AGGREGATE) - cw1200_debug_rxed_agg(priv); -#if 0 - /* Wrong: ACK could be disable for this ACL */ - if (arg->flags & WSM_RX_STATUS_ADDRESS1) - priv->last_activity_time = jiffies; -#endif - -#if 0 - print_hex_dump_bytes("RX: ", DUMP_PREFIX_NONE, - skb->data, skb->len); -#endif - - /* Not that we really need _irqsafe variant here, - * but it offloads realtime bh thread and improve - * system performance. */ - ieee80211_rx(priv->hw, skb); - *skb_p = NULL; -} - void cw1200_channel_switch_cb(struct cw1200_common *priv) { wsm_unlock_tx(priv); diff --git a/drivers/staging/cw1200/sta.h b/drivers/staging/cw1200/sta.h index 998afeedf45..2a3c8836c0b 100644 --- a/drivers/staging/cw1200/sta.h +++ b/drivers/staging/cw1200/sta.h @@ -45,9 +45,6 @@ void cw1200_flush(struct ieee80211_hw *hw, bool drop); /* ******************************************************************** */ /* WSM callbacks */ -void cw1200_rx_cb(struct cw1200_common *priv, - struct wsm_rx *arg, - struct sk_buff **skb_p); /* void cw1200_set_pm_complete_cb(struct cw1200_common *priv, struct wsm_set_pm_complete *arg); */ void cw1200_channel_switch_cb(struct cw1200_common *priv); diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index fd04156dcb5..005654eabe8 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -563,6 +563,66 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, } } +void cw1200_rx_cb(struct cw1200_common *priv, + struct wsm_rx *arg, + struct sk_buff **skb_p) +{ + struct sk_buff *skb = *skb_p; + struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); + hdr->flag = 0; + + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { + /* STA is stopped. */ + return; + } + + if (unlikely(arg->status)) { + if (arg->status == WSM_STATUS_MICFAILURE) { + txrx_printk(KERN_DEBUG "[RX] MIC failure.\n"); + hdr->flag |= RX_FLAG_MMIC_ERROR; + } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) { + txrx_printk(KERN_DEBUG "[RX] No key found.\n"); + return; + } else { + txrx_printk(KERN_DEBUG "[RX] Receive failure: %d.\n", + arg->status); + return; + } + } + + hdr->mactime = 0; /* Not supported by WSM */ + hdr->freq = ieee80211_channel_to_frequency(arg->channelNumber); + hdr->band = (hdr->freq >= 5000) ? + IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; + hdr->rate_idx = arg->rxedRate; + if (hdr->rate_idx >= 4) /* TODO: Use common convert function. */ + hdr->rate_idx -= 2; + hdr->signal = (s8)arg->rcpiRssi; + hdr->antenna = 0; + + if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { + hdr->flag |= RX_FLAG_DECRYPTED; + if (!arg->status && + (WSM_RX_STATUS_ENCRYPTION(arg->flags) == + WSM_RX_STATUS_TKIP)) { + hdr->flag |= RX_FLAG_MMIC_STRIPPED; + skb_trim(skb, skb->len - 8 /*MICHAEL_MIC_LEN*/); + } + } + if (arg->flags & WSM_RX_STATUS_HT) + hdr->flag |= RX_FLAG_HT; + + cw1200_debug_rxed(priv); + if (arg->flags & WSM_RX_STATUS_AGGREGATE) + cw1200_debug_rxed_agg(priv); + + /* Not that we really need _irqsafe variant here, + * but it offloads realtime bh thread and improve + * system performance. */ + ieee80211_rx_irqsafe(priv->hw, skb); + *skb_p = NULL; +} + /* ******************************************************************** */ /* Security */ diff --git a/drivers/staging/cw1200/txrx.h b/drivers/staging/cw1200/txrx.h index 2be3af99d03..12a9b49028b 100644 --- a/drivers/staging/cw1200/txrx.h +++ b/drivers/staging/cw1200/txrx.h @@ -68,6 +68,9 @@ int cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb); void cw1200_tx_confirm_cb(struct cw1200_common *priv, struct wsm_tx_confirm *arg); +void cw1200_rx_cb(struct cw1200_common *priv, + struct wsm_rx *arg, + struct sk_buff **skb_p); /* ******************************************************************** */ /* Timeout */ -- cgit v1.2.3 From 66c192c4e4f0ced20a4a1e0d4e4887d3456b162d Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Mon, 6 Jun 2011 11:20:24 +0200 Subject: cw1200: 11n verification and bugfixing. * MCS rate indexes were wrongly interpreted by the driver as legacy rate offsets in both TX and RX directions. * HT rates have not been marked as MCS rates in rate description table. * Driver should not expose AMPDU aggregation capability to the mac80211 stack, aggrehation is fully controlled by the firmware. Firmware takes care of block ACK negotiation. * Block ACK action frames are filtered by driver: mac80211 layer is not involved into BA dialog. * Block ACK in TX direction is enabled. * Block ACK in SoftAP mode is enabled. * RX'ed frames should not be marked as "aggregated" for the mac80211 stack, it confuses rate control algorithm quite a lot. * CONFIG_CW1200_HT_SUPPORT option is removed: drivers always supports HT. TODO: - Modify minstrel rate policy "distillation" to prioritize higher bitrates. - Verify greenfield mode. Change-Id: I9288a2b99984785ae97d85de98ea79d3a49ea64f Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24480 Reviewed-by: Robert MARKLUND Tested-by: Robert MARKLUND Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25620 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/Kconfig | 9 --- drivers/staging/cw1200/ap.c | 32 +++------- drivers/staging/cw1200/cw1200.h | 4 +- drivers/staging/cw1200/main.c | 37 +++++------- drivers/staging/cw1200/sta.c | 8 +-- drivers/staging/cw1200/txrx.c | 131 +++++++++++++++++++++++++++++++++------- 6 files changed, 138 insertions(+), 83 deletions(-) diff --git a/drivers/staging/cw1200/Kconfig b/drivers/staging/cw1200/Kconfig index ed2d23e5d47..8be1b909f6d 100644 --- a/drivers/staging/cw1200/Kconfig +++ b/drivers/staging/cw1200/Kconfig @@ -38,15 +38,6 @@ config CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE If unsure, say N. -config CW1200_HT_SUPPORT - bool "11n support (DEVELOPMENT)" - depends on CW1200 - help - Say Y if you want to enable 11n support in the driver. - Note that 11n support is not 100% verified, status is unknown. - - If unsure, say N. - menu "Driver debug features" depends on CW1200 diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 7fa863c6f20..561dab3f94f 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -427,31 +427,11 @@ int cw1200_ampdu_action(struct ieee80211_hw *hw, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn) { - int ret = 0; - - switch (action) { - /* - * The hw itself takes care of setting up BlockAck mechanisms. - * So, we only have to allow mac80211 to nagotiate a BlockAck - * agreement. Once that is done, the hw will BlockAck incoming - * AMPDUs without further setup. - */ - case IEEE80211_AMPDU_RX_START: - case IEEE80211_AMPDU_RX_STOP: - break; - case IEEE80211_AMPDU_TX_START: - ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; - case IEEE80211_AMPDU_TX_STOP: - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; - case IEEE80211_AMPDU_TX_OPERATIONAL: - break; - default: - ret = -ENOTSUPP; - } - - return ret; + /* Aggregation is implemented fully in firmware, + * including block ack negotiation. Do not allow + * mac80211 stack to do anything: it interferes with + * the firmware. */ + return -ENOTSUPP; } /* ******************************************************************** */ @@ -606,6 +586,8 @@ static int cw1200_start_ap(struct cw1200_common *priv) if (!ret) ret = WARN_ON(wsm_beacon_transmit(priv, &transmit)); if (!ret) { + WARN_ON(wsm_set_block_ack_policy(priv, + priv->ba_tid_mask, priv->ba_tid_mask)); priv->join_status = CW1200_JOIN_STATUS_AP; cw1200_update_filtering(priv); } diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 0c88bfa48da..cd5061d1097 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -81,7 +81,8 @@ struct cw1200_common { /* calibration, output power limit and rssi<->dBm conversation data */ /* BBP/MAC state */ - struct ieee80211_rate *rates; + struct ieee80211_rate *rates; + struct ieee80211_rate *mcs_rates; u8 mac_addr[ETH_ALEN]; struct ieee80211_channel *channel; u8 bssid[ETH_ALEN]; @@ -107,6 +108,7 @@ struct cw1200_common { bool listening; struct wsm_rx_filter rx_filter; struct wsm_beacon_filter_control bf_control; + u8 ba_tid_mask; /* BH */ atomic_t bh_rx; diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 0a1193b8b41..e22fbf2e22e 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -71,22 +71,22 @@ static struct ieee80211_rate cw1200_rates[] = { RATETAB_ENT(360, 11, 0), RATETAB_ENT(480, 12, 0), RATETAB_ENT(540, 13, 0), -#if defined(CONFIG_CW1200_HT_SUPPORT) - RATETAB_ENT(65, 14, 0), - RATETAB_ENT(130, 15, 0), - RATETAB_ENT(195, 16, 0), - RATETAB_ENT(260, 17, 0), - RATETAB_ENT(390, 18, 0), - RATETAB_ENT(520, 19, 0), - RATETAB_ENT(585, 20, 0), - RATETAB_ENT(650, 21, 0), -#endif /* CONFIG_CW1200_HT_SUPPORT */ + RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS), + RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS), + RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS), + RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS), + RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS), + RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS), + RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS), + RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS), }; #define cw1200_a_rates (cw1200_rates + 4) #define cw1200_a_rates_size (ARRAY_SIZE(cw1200_rates) - 4) #define cw1200_g_rates (cw1200_rates + 0) #define cw1200_g_rates_size (ARRAY_SIZE(cw1200_rates)) +#define cw1200_n_rates (cw1200_rates + 12) +#define cw1200_n_rates_size (ARRAY_SIZE(cw1200_rates) - 12) #define CHAN2G(_channel, _freq, _flags) { \ @@ -151,7 +151,6 @@ static struct ieee80211_supported_band cw1200_band_2ghz = { .n_channels = ARRAY_SIZE(cw1200_2ghz_chantable), .bitrates = cw1200_g_rates, .n_bitrates = cw1200_g_rates_size, -#if defined(CONFIG_CW1200_HT_SUPPORT) .ht_cap = { .cap = IEEE80211_HT_CAP_SM_PS | IEEE80211_HT_CAP_GRN_FLD | @@ -160,8 +159,6 @@ static struct ieee80211_supported_band cw1200_band_2ghz = { IEEE80211_HT_CAP_DELAY_BA | IEEE80211_HT_CAP_MAX_AMSDU, .ht_supported = 1, - /* TODO: It was 4K for cut 1.1 HW, if I remember - * it correctly. Needs to be verified on cut 2 HW. */ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, .mcs = { @@ -170,7 +167,6 @@ static struct ieee80211_supported_band cw1200_band_2ghz = { .tx_params = IEEE80211_HT_MCS_TX_DEFINED, }, }, -#endif /* CONFIG_CW1200_HT_SUPPORT */ }; static struct ieee80211_supported_band cw1200_band_5ghz = { @@ -178,7 +174,6 @@ static struct ieee80211_supported_band cw1200_band_5ghz = { .n_channels = ARRAY_SIZE(cw1200_5ghz_chantable), .bitrates = cw1200_a_rates, .n_bitrates = cw1200_a_rates_size, -#if defined(CONFIG_CW1200_HT_SUPPORT) .ht_cap = { .cap = IEEE80211_HT_CAP_SM_PS | IEEE80211_HT_CAP_GRN_FLD | @@ -187,8 +182,6 @@ static struct ieee80211_supported_band cw1200_band_5ghz = { IEEE80211_HT_CAP_DELAY_BA | IEEE80211_HT_CAP_MAX_AMSDU, .ht_supported = 1, - /* TODO: It was 4K for cut 1.1 HW, if I remember - * it correctly. Needs to be verified on cut 2 HW. */ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, .mcs = { @@ -197,7 +190,6 @@ static struct ieee80211_supported_band cw1200_band_5ghz = { .tx_params = IEEE80211_HT_MCS_TX_DEFINED, }, }, -#endif /* CONFIG_CW1200_HT_SUPPORT */ }; static const struct ieee80211_ops cw1200_ops = { @@ -240,15 +232,18 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) priv->hw = hw; priv->mode = NL80211_IFTYPE_UNSPECIFIED; priv->rates = cw1200_rates; /* TODO: fetch from FW */ + priv->mcs_rates = cw1200_n_rates; + /* Enable block ACK for every TID but voice. */ + priv->ba_tid_mask = 0x3F; hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | /* IEEE80211_HW_SUPPORTS_UAPSD | */ IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_CQM_RSSI | -#if defined(CONFIG_CW1200_HT_SUPPORT) - IEEE80211_HW_AMPDU_AGGREGATION | -#endif + /* Aggregation is fully controlled by firmware. + * Do not need any support from the mac80211 stack */ + /* IEEE80211_HW_AMPDU_AGGREGATION | */ #if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) IEEE80211_HW_SUPPORTS_CQM_BEACON_MISS | IEEE80211_HW_SUPPORTS_CQM_TX_FAIL | diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 60a3c57100b..65df33ac61f 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -972,12 +972,8 @@ void cw1200_join_work(struct work_struct *work) wsm_flush_tx(priv); - /* TX block_ack can use only 3 TX buffers, - * which is /slightly/ :) unefficient => disabled. - * RX block ACK is enabled for everything but voice. - * TODO: Verify video lags, change 0x3F -> 0x0F - * if necessary. */ - WARN_ON(wsm_set_block_ack_policy(priv, 0x00, 0x3F)); + WARN_ON(wsm_set_block_ack_policy(priv, + priv->ba_tid_mask, priv->ba_tid_mask)); #if defined(CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) priv->last_activity_time = jiffies; diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 005654eabe8..16a85b39b99 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -22,6 +22,14 @@ #define tx_policy_printk(...) #endif +static int cw1200_handle_action_rx(struct cw1200_common *priv, + struct sk_buff *skb); +static int cw1200_handle_action_tx(struct cw1200_common *priv, + struct sk_buff *skb); +static const struct ieee80211_rate * +cw1200_get_tx_rate(const struct cw1200_common *priv, + const struct ieee80211_tx_rate *rate); + /* ******************************************************************** */ /* TX queue lock / unlock */ @@ -67,8 +75,7 @@ static void tx_policy_build(const struct cw1200_common *priv, /* [out] */ struct tx_policy *policy, struct ieee80211_tx_rate *rates, size_t count) { - int i; - const struct ieee80211_rate *rates_tbl = priv->rates; + int i, j; unsigned limit = priv->short_frame_max_tx_count; unsigned total = 0; BUG_ON(rates[0].idx < 0); @@ -76,22 +83,34 @@ static void tx_policy_build(const struct cw1200_common *priv, /* minstrel is buggy a little bit, so distille * incoming rates first. */ - for (i = 0; i < count; ++i) { - if (rates[i].idx < 0) - break; - /* minstrel is buggy a little bit. */ - if (i && (rates[i].idx == rates[i - 1].idx)) { - rates[i - 1].count += rates[i].count; + for (i = 1; i < count; ++i) { + if (rates[i].idx < 0) { + count = i; break; } - total += rates[i].count; - if (i && (rates[i].idx > rates[i - 1].idx)) { + if (rates[i].idx > rates[i - 1].idx) { struct ieee80211_tx_rate tmp = rates[i - 1]; rates[i - 1] = rates[i]; rates[i] = tmp; } } - count = i; + + total = rates[0].count; + for (i = 0, j = 1; j < count; ++j) { + if (rates[j].idx == rates[i].idx) { + rates[i].count += rates[j].count; + } else if (rates[j].idx > rates[i].idx) { + break; + } else { + ++i; + if (i != j) + rates[i] = rates[j]; + } + total += rates[j].count; + } + if (i + 1 < count) + count = i + 1; + if (limit < count) limit = count; @@ -102,12 +121,12 @@ static void tx_policy_build(const struct cw1200_common *priv, limit -= rates[i].count; } } - policy->defined = rates_tbl[rates[0].idx].hw_value + 1; + policy->defined = cw1200_get_tx_rate(priv, &rates[0])->hw_value + 1; for (i = 0; i < count; ++i) { register unsigned rateid, off, shift, retries; - rateid = rates_tbl[rates[i].idx].hw_value; + rateid = cw1200_get_tx_rate(priv, &rates[i])->hw_value; off = rateid >> 3; /* eq. rateid / 8 */ shift = (rateid & 0x07) << 2; /* eq. (rateid % 8) * 4 */ @@ -331,13 +350,26 @@ u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates) return ret; } +static const struct ieee80211_rate * +cw1200_get_tx_rate(const struct cw1200_common *priv, + const struct ieee80211_tx_rate *rate) +{ + if (rate->idx < 0) + return NULL; + if (rate->flags & IEEE80211_TX_RC_MCS) + return &priv->mcs_rates[rate->idx]; + return &priv->hw->wiphy->bands[priv->channel->band]-> + bitrates[rate->idx]; +} + /* NOTE: cw1200_skb_to_wsm executes in atomic context. */ int cw1200_skb_to_wsm(struct cw1200_common *priv, struct sk_buff *skb, struct wsm_tx *wsm) { bool tx_policy_renew = false; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ieee80211_rate *rate = ieee80211_get_tx_rate(priv->hw, tx_info); + const struct ieee80211_rate *rate = cw1200_get_tx_rate(priv, + &tx_info->control.rates[0]); memset(wsm, 0, sizeof(*wsm)); wsm->hdr.len = __cpu_to_le16(skb->len); @@ -467,6 +499,10 @@ int cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) skb_trim(skb, skb->len - offset); } + if (ieee80211_is_action(hdr->frame_control)) + if (cw1200_handle_action_tx(priv, skb)) + goto drop; + ret = cw1200_queue_put(&priv->tx_queue[queue], priv, skb, link_id); if (!WARN_ON(ret)) @@ -480,12 +516,42 @@ err: /* TODO: Update TX failure counters */ dev_kfree_skb_any(skb); return NETDEV_TX_OK; + +drop: + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +/* ******************************************************************** */ + +static int cw1200_handle_action_rx(struct cw1200_common *priv, + struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt = (void *)skb->data; + + /* Filter block ACK negotiation: fully controlled by firmware */ + if (mgmt->u.action.category == WLAN_CATEGORY_BACK) + return 1; + + return 0; +} + +static int cw1200_handle_action_tx(struct cw1200_common *priv, + struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt = (void *)skb->data; + + /* Filter block ACK negotiation: fully controlled by firmware */ + if (mgmt->u.action.category == WLAN_CATEGORY_BACK) + return 1; + + return 0; } /* ******************************************************************** */ void cw1200_tx_confirm_cb(struct cw1200_common *priv, - struct wsm_tx_confirm *arg) + struct wsm_tx_confirm *arg) { u8 queue_id = cw1200_queue_get_queue_id(arg->packetID); struct cw1200_queue *queue = &priv->tx_queue[queue_id]; @@ -527,7 +593,9 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, ++tx_count; cw1200_debug_txed(priv); if (arg->flags & WSM_TX_STATUS_AGGREGATION) { - tx->flags |= IEEE80211_TX_STAT_AMPDU; + /* Do not report aggregation to mac80211: + * it confuses minstrel a lot. */ + /* tx->flags |= IEEE80211_TX_STAT_AMPDU; */ cw1200_debug_txed_agg(priv); } } else { @@ -569,6 +637,8 @@ void cw1200_rx_cb(struct cw1200_common *priv, { struct sk_buff *skb = *skb_p; struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); + const struct ieee80211_rate *rate; + __le16 frame_control; hdr->flag = 0; if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { @@ -590,13 +660,29 @@ void cw1200_rx_cb(struct cw1200_common *priv, } } + if (skb->len < sizeof(struct ieee80211_hdr_3addr)) { + wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed.\n"); + return; + } + + frame_control = *(__le16*)skb->data; hdr->mactime = 0; /* Not supported by WSM */ hdr->freq = ieee80211_channel_to_frequency(arg->channelNumber); hdr->band = (hdr->freq >= 5000) ? IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; - hdr->rate_idx = arg->rxedRate; - if (hdr->rate_idx >= 4) /* TODO: Use common convert function. */ - hdr->rate_idx -= 2; + + if (arg->rxedRate >= 4) + rate = &priv->rates[arg->rxedRate - 2]; + else + rate = &priv->rates[arg->rxedRate]; + + if (rate >= priv->mcs_rates) { + hdr->rate_idx = rate - priv->mcs_rates; + hdr->flag |= RX_FLAG_HT; + } else { + hdr->rate_idx = rate - priv->rates; + } + hdr->signal = (s8)arg->rcpiRssi; hdr->antenna = 0; @@ -609,13 +695,16 @@ void cw1200_rx_cb(struct cw1200_common *priv, skb_trim(skb, skb->len - 8 /*MICHAEL_MIC_LEN*/); } } - if (arg->flags & WSM_RX_STATUS_HT) - hdr->flag |= RX_FLAG_HT; cw1200_debug_rxed(priv); if (arg->flags & WSM_RX_STATUS_AGGREGATE) cw1200_debug_rxed_agg(priv); + if (ieee80211_is_action(frame_control) && + (arg->flags & WSM_RX_STATUS_ADDRESS1)) + if (cw1200_handle_action_rx(priv, skb)) + return; + /* Not that we really need _irqsafe variant here, * but it offloads realtime bh thread and improve * system performance. */ -- cgit v1.2.3 From a3a9f63337363115f24666ae3cebef485416a611 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 15 Jun 2011 11:24:22 +0200 Subject: cw1200: New option for 5GHz support. 5GHz band support is disabled by default in the driver. Use CW1200_5GHZ_SUPPORT kernel option to explicitly enable it if your hardware supports 5GHz band. Change-Id: I21c30ebd277ab19fcd92380f806111f2db668d0f Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25623 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/Kconfig | 7 +++++++ drivers/staging/cw1200/main.c | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/staging/cw1200/Kconfig b/drivers/staging/cw1200/Kconfig index 8be1b909f6d..5c6f18267e1 100644 --- a/drivers/staging/cw1200/Kconfig +++ b/drivers/staging/cw1200/Kconfig @@ -35,7 +35,14 @@ config CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE or you suspect problems with the implementation. Please finalize software the software keep-alive functionality in that case. + If unsure, say N. +config CW1200_5GHZ_SUPPORT + bool "5GHz band support" + depends on CW1200 + help + Say Y if your device supports 5GHz band. Should be disabled for + CW1100 silicon. If unsure, say N. menu "Driver debug features" diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index e22fbf2e22e..7aef0ea1c60 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -124,6 +124,7 @@ static struct ieee80211_channel cw1200_2ghz_chantable[] = { CHAN2G(14, 2484, 0), }; +#ifdef CONFIG_CW1200_5GHZ_SUPPORT static struct ieee80211_channel cw1200_5ghz_chantable[] = { CHAN5G(34, 0), CHAN5G(36, 0), CHAN5G(38, 0), CHAN5G(40, 0), @@ -145,6 +146,7 @@ static struct ieee80211_channel cw1200_5ghz_chantable[] = { CHAN5G(208, 0), CHAN5G(212, 0), CHAN5G(216, 0), }; +#endif /* CONFIG_CW1200_5GHZ_SUPPORT */ static struct ieee80211_supported_band cw1200_band_2ghz = { .channels = cw1200_2ghz_chantable, @@ -169,6 +171,7 @@ static struct ieee80211_supported_band cw1200_band_2ghz = { }, }; +#ifdef CONFIG_CW1200_5GHZ_SUPPORT static struct ieee80211_supported_band cw1200_band_5ghz = { .channels = cw1200_5ghz_chantable, .n_channels = ARRAY_SIZE(cw1200_5ghz_chantable), @@ -191,6 +194,7 @@ static struct ieee80211_supported_band cw1200_band_5ghz = { }, }, }; +#endif /* CONFIG_CW1200_5GHZ_SUPPORT */ static const struct ieee80211_ops cw1200_ops = { .start = cw1200_start, @@ -273,7 +277,9 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &cw1200_band_2ghz; +#ifdef CONFIG_CW1200_5GHZ_SUPPORT hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &cw1200_band_5ghz; +#endif /* CONFIG_CW1200_5GHZ_SUPPORT */ hw->wiphy->max_scan_ssids = 2; hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; -- cgit v1.2.3 From 6f9db37b2f325a4fc844f31512ff8d5b89bf1937 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 15 Jun 2011 15:00:52 +0200 Subject: cw1200: Document and optimize device reset sequence. Some devices have problems with reset if WRESET is active (low) when device is being powered on. Additional WRESET cycle is required for these devices. Change-Id: I117c1776e3819260ec7f4aa4e0d14174eb3f7e82 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25624 Reviewed-by: Philippe LANGLAIS Reviewed-by: Robert MARKLUND --- drivers/staging/cw1200/cw1200_sdio.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index 235ac67cb75..e106129f4a3 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -259,10 +259,18 @@ static int cw1200_sdio_on(const struct cw1200_platform_data *pdata) const struct resource *reset = pdata->reset; gpio_request(reset->start, reset->name); gpio_direction_output(reset->start, 1); - msleep(100); + /* It is not stated in the datasheet, but at least some of devices + * have problems with reset if this stage is omited. */ + msleep(50); gpio_set_value(reset->start, 0); - msleep(100); + /* A valid reset shall be obtained by maintaining WRESETN + * active (low) for at least two cycles of LP_CLK after VDDIO + * is stable within it operating range. */ + msleep(1); gpio_set_value(reset->start, 1); + /* The host should wait 30 ms after the WRESETN release + * for the on-chip LDO to stabilize */ + msleep(30); cw1200_detect_card(pdata); return 0; } -- cgit v1.2.3 From 6aee38542825ea891c851b4696dbe0c241264708 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Mon, 6 Jun 2011 17:51:35 +0200 Subject: cw1200: rate policy optimization. Try to utilize higher rates as much as possible before switching to lower rates. Change-Id: Ieaba60bc17b9c8e4e676a6ed7e5061d2ba8eddcf Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24481 Reviewed-by: Robert MARKLUND Tested-by: Robert MARKLUND Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25621 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/debug.c | 2 -- drivers/staging/cw1200/debug.h | 10 ---------- drivers/staging/cw1200/txrx.c | 16 ++++++++++------ 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 498f072da78..b4c2da23135 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -247,8 +247,6 @@ static int cw1200_status_show(struct seq_file *seq, void *v) d->tx); seq_printf(seq, "AGG TXed: %d\n", d->tx_agg); - seq_printf(seq, "MORE TXed: %d\n", - d->tx_more); seq_printf(seq, "MULTI TXed: %d (%d)\n", d->tx_multi, d->tx_multi_frames); seq_printf(seq, "RXed: %d\n", diff --git a/drivers/staging/cw1200/debug.h b/drivers/staging/cw1200/debug.h index 0c9ef1252c4..a19083a49b9 100644 --- a/drivers/staging/cw1200/debug.h +++ b/drivers/staging/cw1200/debug.h @@ -9,7 +9,6 @@ struct cw1200_debug_priv { struct dentry *debugfs_phy; int tx; int tx_agg; - int tx_more; int rx; int rx_agg; int tx_multi; @@ -29,11 +28,6 @@ static inline void cw1200_debug_txed_agg(struct cw1200_common *priv) ++priv->debug->tx_agg; } -static inline void cw1200_debug_txed_more(struct cw1200_common *priv) -{ - ++priv->debug->tx_more; -} - static inline void cw1200_debug_txed_multi(struct cw1200_common *priv, int count) { @@ -70,10 +64,6 @@ static inline void cw1200_debug_txed_agg(struct cw1200_common *priv) { } -static inline void cw1200_debug_txed_more(struct cw1200_common *priv) -{ -} - static inline void cw1200_debug_txed_multi(struct cw1200_common *priv, int count) { diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 16a85b39b99..eb2802af318 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -83,6 +83,8 @@ static void tx_policy_build(const struct cw1200_common *priv, /* minstrel is buggy a little bit, so distille * incoming rates first. */ + + /* Sort rates in descending order. */ for (i = 1; i < count; ++i) { if (rates[i].idx < 0) { count = i; @@ -95,6 +97,7 @@ static void tx_policy_build(const struct cw1200_common *priv, } } + /* Eliminate duplicates. */ total = rates[0].count; for (i = 0, j = 1; j < count; ++j) { if (rates[j].idx == rates[i].idx) { @@ -108,16 +111,17 @@ static void tx_policy_build(const struct cw1200_common *priv, } total += rates[j].count; } - if (i + 1 < count) - count = i + 1; + count = i + 1; + /* Re-fill policy trying to keep every requested rate and with + * respect to the global max tx retransmission count. */ if (limit < count) limit = count; - if (total > limit) { - for (i = count - 1; i >= 0; --i) { - if (rates[i].count > limit - i) - rates[i].count = limit - i; + for (i = 0; i < count; ++i) { + int left = count - i - 1; + if (rates[i].count > limit - left) + rates[i].count = limit - left; limit -= rates[i].count; } } -- cgit v1.2.3 From f21ed94daa73a203b5c537c18cf8d0ecfd320f5c Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 26 May 2011 14:50:06 +0200 Subject: cw1200: Remove link to platform_device from platform_data. Pointer to platform_device was added to the cw1200 platform_data in the regulator support commit. It is not needed and breaks design: cw1200 platform_devce should not be exposed to the cw1200 driver. Signed-off-by: Dmitry Tarnyagin Change-Id: Ic880da9e4bc7ee4d3cfd759bf201c6f850509142 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25140 Reviewed-by: Robert MARKLUND Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/cw1200_plat.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/cw1200/cw1200_plat.h b/drivers/staging/cw1200/cw1200_plat.h index 573bc06ac7f..ed354de0edf 100644 --- a/drivers/staging/cw1200/cw1200_plat.h +++ b/drivers/staging/cw1200/cw1200_plat.h @@ -8,7 +8,6 @@ #include struct cw1200_platform_data { - struct platform_device *device; const char *mmc_id; const struct resource *irq; const struct resource *reset; -- cgit v1.2.3 From 8baff0cf82469a4ebfabc775b56772dcd0b6fdfc Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Tue, 7 Jun 2011 09:12:07 +0200 Subject: cw1200: throughput optimization and fine-tuning. * Fixed: Do not allocate a dedicated communication channel (buffer) for WSM command stream, use it for data traffic when needed. * Additional statistics added to DebugFS: counters for TX policy cache misses and unaligned skbs memmove. * Fixed: Strip IV/ICV in the driver to prevent mac80211 layer from double-verification. Change-Id: I600664466d577df4c2bc62ff49fb22f2d3b024cc Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24482 Reviewed-by: Robert MARKLUND Tested-by: Robert MARKLUND Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25622 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/bh.c | 6 ++-- drivers/staging/cw1200/debug.c | 5 +++- drivers/staging/cw1200/debug.h | 20 ++++++++++++++ drivers/staging/cw1200/txrx.c | 62 +++++++++++++++++++++++++++++++++++------- 4 files changed, 78 insertions(+), 15 deletions(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index 177274a00d2..d49f9fdf732 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -120,7 +120,7 @@ int wsm_release_tx_buffer(struct cw1200_common *priv, int count) priv->hw_bufs_used -= count; if (WARN_ON(priv->hw_bufs_used < 0)) ret = -1; - else if (hw_bufs_used >= priv->wsm_caps.numInpChBufs - 1) + else if (hw_bufs_used >= priv->wsm_caps.numInpChBufs) ret = 1; if (!priv->hw_bufs_used) wake_up_interruptible(&priv->hw_bufs_used_wq); @@ -363,9 +363,7 @@ tx: /* HACK! One buffer is reserved for control path */ BUG_ON(priv->hw_bufs_used > priv->wsm_caps.numInpChBufs); tx_allowed = - priv->hw_bufs_used + 1 < priv->wsm_caps.numInpChBufs; - if (unlikely(!tx_allowed && priv->wsm_cmd.ptr)) - tx_allowed = 1; + priv->hw_bufs_used < priv->wsm_caps.numInpChBufs; if (tx && tx_allowed) { size_t tx_len; diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index b4c2da23135..13c4570372f 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -253,7 +253,10 @@ static int cw1200_status_show(struct seq_file *seq, void *v) d->rx); seq_printf(seq, "AGG RXed: %d\n", d->rx_agg); - + seq_printf(seq, "TX miss: %d\n", + d->tx_cache_miss); + seq_printf(seq, "TX copy: %d\n", + d->tx_copy); seq_printf(seq, "Scan: %s\n", atomic_read(&priv->scan.in_progress) ? "active" : "idle"); seq_printf(seq, "Led state: 0x%.2X\n", diff --git a/drivers/staging/cw1200/debug.h b/drivers/staging/cw1200/debug.h index a19083a49b9..e7fc4d2daef 100644 --- a/drivers/staging/cw1200/debug.h +++ b/drivers/staging/cw1200/debug.h @@ -13,6 +13,8 @@ struct cw1200_debug_priv { int rx_agg; int tx_multi; int tx_multi_frames; + int tx_cache_miss; + int tx_copy; }; int cw1200_debug_init(struct cw1200_common *priv); @@ -45,6 +47,16 @@ static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv) ++priv->debug->rx_agg; } +static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv) +{ + ++priv->debug->tx_cache_miss; +} + +static inline void cw1200_debug_tx_copy(struct cw1200_common *priv) +{ + ++priv->debug->tx_copy; +} + #else /* CONFIG_CW1200_DEBUGFS */ static inline int cw1200_debug_init(struct cw1200_common *priv) @@ -77,6 +89,14 @@ static inline void cw1200_debug_rxed_agg(struct cw1200_common *priv) { } +static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv) +{ +} + +static inline void cw1200_debug_tx_copy(struct cw1200_common *priv) +{ +} + #endif /* CONFIG_CW1200_DEBUGFS */ #endif /* CW1200_DEBUG_H_INCLUDED */ diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index eb2802af318..8616d1d2ffe 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -323,6 +323,7 @@ static int tx_policy_upload(struct cw1200_common *priv) } } spin_unlock_bh(&cache->lock); + cw1200_debug_tx_cache_miss(priv); tx_policy_printk(KERN_DEBUG "[TX policy] Upload %d policies\n", arg.hdr.numTxRatePolicies); return wsm_set_tx_rate_retry_policy(priv, &arg); @@ -501,6 +502,7 @@ int cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) p = skb_push(skb, offset); memmove(p, &p[offset], skb->len - offset); skb_trim(skb, skb->len - offset); + cw1200_debug_tx_copy(priv); } if (ieee80211_is_action(hdr->frame_control)) @@ -647,7 +649,7 @@ void cw1200_rx_cb(struct cw1200_common *priv, if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { /* STA is stopped. */ - return; + goto drop; } if (unlikely(arg->status)) { @@ -656,17 +658,18 @@ void cw1200_rx_cb(struct cw1200_common *priv, hdr->flag |= RX_FLAG_MMIC_ERROR; } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) { txrx_printk(KERN_DEBUG "[RX] No key found.\n"); - return; + goto drop; } else { txrx_printk(KERN_DEBUG "[RX] Receive failure: %d.\n", arg->status); - return; + goto drop; } } if (skb->len < sizeof(struct ieee80211_hdr_3addr)) { - wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed.\n"); - return; + wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. " + "Size is lesser than IEEE header.\n"); + goto drop; } frame_control = *(__le16*)skb->data; @@ -691,13 +694,47 @@ void cw1200_rx_cb(struct cw1200_common *priv, hdr->antenna = 0; if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { - hdr->flag |= RX_FLAG_DECRYPTED; - if (!arg->status && - (WSM_RX_STATUS_ENCRYPTION(arg->flags) == - WSM_RX_STATUS_TKIP)) { + size_t iv_len = 0, icv_len = 0; + size_t hdrlen = ieee80211_hdrlen(frame_control); + + hdr->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED; + + /* Oops... There is no fast way to ask mac80211 about + * IV/ICV lengths. Even defineas are not exposed.*/ + switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { + case WSM_RX_STATUS_WEP: + iv_len = 4 /* WEP_IV_LEN */; + icv_len = 4 /* WEP_ICV_LEN */; + break; + case WSM_RX_STATUS_TKIP: + iv_len = 8 /* TKIP_IV_LEN */; + icv_len = 4 /* TKIP_ICV_LEN */ + + 8 /*MICHAEL_MIC_LEN*/; hdr->flag |= RX_FLAG_MMIC_STRIPPED; - skb_trim(skb, skb->len - 8 /*MICHAEL_MIC_LEN*/); + break; + case WSM_RX_STATUS_AES: + iv_len = 8 /* CCMP_HDR_LEN */; + icv_len = 8 /* CCMP_MIC_LEN */; + break; + case WSM_RX_STATUS_WAPI: + iv_len = 18 /* WAPI_HDR_LEN */; + icv_len = 16 /* WAPI_MIC_LEN */; + break; + default: + WARN_ON("Unknown encryption type"); + goto drop; } + + if (skb->len < hdrlen + iv_len + icv_len) { + wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. " + "Size is lesser than crypto headers.\n"); + goto drop; + } + + /* Remove IV, ICV and MIC */ + skb_trim(skb, skb->len - icv_len); + memmove(skb->data + iv_len, skb->data, hdrlen); + skb_pull(skb, iv_len); } cw1200_debug_rxed(priv); @@ -714,6 +751,11 @@ void cw1200_rx_cb(struct cw1200_common *priv, * system performance. */ ieee80211_rx_irqsafe(priv->hw, skb); *skb_p = NULL; + return; + +drop: + /* TODO: update failure counters */ + return; } /* ******************************************************************** */ -- cgit v1.2.3 From fa7182317ed7727c26e05339c2fe52c70e80d428 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Fri, 2 Sep 2011 15:24:25 +0200 Subject: WLAN: Add cw1200_plat.h to mach-ux500 includes ST-Ericsson ID: 355582, 352334 ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson Linux next: NA Signed-off-by: Bartosz Markowski Change-Id: Icbfbbb21be76d21c4f1190d586ec8458ea6aa12d Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30084 --- arch/arm/mach-ux500/include/mach/cw1200_plat.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 arch/arm/mach-ux500/include/mach/cw1200_plat.h diff --git a/arch/arm/mach-ux500/include/mach/cw1200_plat.h b/arch/arm/mach-ux500/include/mach/cw1200_plat.h new file mode 100644 index 00000000000..e79794f42a3 --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/cw1200_plat.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Dmitry Tarnyagin + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef CW1200_PLAT_H_INCLUDED +#define CW1200_PLAT_H_INCLUDED + +#include + +struct cw1200_platform_data { + const char *mmc_id; + const struct resource *irq; + const struct resource *reset; + int (*power_ctrl)(const struct cw1200_platform_data *pdata, + bool enable); +}; + +/* Declaration only. Should be implemented in arch/xxx/mach-yyy */ +const struct cw1200_platform_data *cw1200_get_platform_data(void); + +#endif /* CW1200_PLAT_H_INCLUDED */ -- cgit v1.2.3 From cff22182b933d9ba958fa9110dadf7e70f27d461 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 7 Sep 2011 08:25:23 +0200 Subject: u8500: Update board-mop500-wlan * cw1200_plat.h shall be included from mach includes ST-Ericsson ID: 352334 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I547ab94410c30cb86e8350b16b673f90faf29836 Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30277 Reviewed-by: Ushit KUMAR Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/board-mop500-wlan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/board-mop500-wlan.c b/arch/arm/mach-ux500/board-mop500-wlan.c index 65863efc78c..ed3f9488409 100644 --- a/arch/arm/mach-ux500/board-mop500-wlan.c +++ b/arch/arm/mach-ux500/board-mop500-wlan.c @@ -13,7 +13,7 @@ #include #include #include "pins.h" -#include "../drivers/staging/cw1200/cw1200_plat.h" +#include static void cw1200_release(struct device *dev); static int cw1200_power_ctrl(const struct cw1200_platform_data *pdata, -- cgit v1.2.3 From 2139c2a7bbc09a8b0efc9eb28dba6b62cac3acd4 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Tue, 13 Sep 2011 07:47:05 +0200 Subject: WLAN: remove GPIO_IRQ conditional checks GPIO IRQs can be used also by UMAC driver, and the .config configuration is not being set then. ST-Ericsson ID: 352334 ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Bartosz Markowski Change-Id: I8f103102dd004c69725afd40453f5296ea4f20bb Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30845 Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/board-mop500-wlan.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-wlan.c b/arch/arm/mach-ux500/board-mop500-wlan.c index ed3f9488409..d17c2748386 100644 --- a/arch/arm/mach-ux500/board-mop500-wlan.c +++ b/arch/arm/mach-ux500/board-mop500-wlan.c @@ -26,14 +26,12 @@ static struct resource cw1200_href_resources[] = { .flags = IORESOURCE_IO, .name = "cw1200_reset", }, -#ifdef CONFIG_CW1200_USE_GPIO_IRQ { .start = NOMADIK_GPIO_TO_IRQ(216), .end = NOMADIK_GPIO_TO_IRQ(216), .flags = IORESOURCE_IRQ, .name = "cw1200_irq", }, -#endif /* CONFIG_CW1200_USE_GPIO_IRQ */ }; static struct resource cw1200_href60_resources[] = { @@ -43,14 +41,12 @@ static struct resource cw1200_href60_resources[] = { .flags = IORESOURCE_IO, .name = "cw1200_reset", }, -#ifdef CONFIG_CW1200_USE_GPIO_IRQ { .start = NOMADIK_GPIO_TO_IRQ(4), .end = NOMADIK_GPIO_TO_IRQ(4), .flags = IORESOURCE_IRQ, .name = "cw1200_irq", }, -#endif /* CONFIG_CW1200_USE_GPIO_IRQ */ }; static struct cw1200_platform_data cw1200_platform_data = { 0 }; @@ -155,9 +151,7 @@ int __init mop500_wlan_init(void) cw1200_platform_data.mmc_id = "mmc3"; cw1200_platform_data.reset = &cw1200_device.resource[0]; -#ifdef CONFIG_CW1200_USE_GPIO_IRQ cw1200_platform_data.irq = &cw1200_device.resource[1]; -#endif /* #ifdef CONFIG_CW1200_USE_GPIO_IRQ */ cw1200_device.dev.release = cw1200_release; if (machine_is_snowball()) -- cgit v1.2.3 From 68754d3ccb18ee1fff9c6ccad024b5cc6b534920 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 14 Sep 2011 12:24:31 +0200 Subject: WLAN: u5500: Add wlan init to u5500 board file Adds WLAN init to board-u5500 to have platform configuration in one place. ST-Ericsson ID: 352334 ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Bartosz Markowski Change-Id: Ica7a1f1e588c7beaf489bafda16a763cfb4dc929 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30924 Reviewed-by: Stefan NILSSON9 Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/board-mop500-wlan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/board-mop500-wlan.c b/arch/arm/mach-ux500/board-mop500-wlan.c index d17c2748386..16169d1cfcb 100644 --- a/arch/arm/mach-ux500/board-mop500-wlan.c +++ b/arch/arm/mach-ux500/board-mop500-wlan.c @@ -145,7 +145,8 @@ int __init mop500_wlan_init(void) return -ENOTSUPP; } - if (machine_is_snowball()) + if (machine_is_snowball() || + machine_is_u5500()) cw1200_platform_data.mmc_id = "mmc2"; else cw1200_platform_data.mmc_id = "mmc3"; -- cgit v1.2.3 From c60e43a2b31e86aee614ba7a7768b0139b9528fe Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Mon, 19 Sep 2011 15:51:26 +0200 Subject: WLAN: u5500: Add new wlan platform file for u5500 * Split board config files for u8500 and u5500 * Set sdi3 and mmc2 for u5500 WLAN ST-Ericsson ID: 352334 ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Bartosz Markowski Change-Id: I9746591fe33c3c3748a50c86d01205fc0680f5d2 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/31410 Reviewed-by: Stefan NILSSON9 Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/board-mop500-wlan.c | 52 ++------------- arch/arm/mach-ux500/board-u5500-wlan.c | 109 ++++++++++++++++++++++++++++++++ arch/arm/mach-ux500/board-u5500-wlan.h | 18 ++++++ 3 files changed, 133 insertions(+), 46 deletions(-) create mode 100644 arch/arm/mach-ux500/board-u5500-wlan.c create mode 100644 arch/arm/mach-ux500/board-u5500-wlan.h diff --git a/arch/arm/mach-ux500/board-mop500-wlan.c b/arch/arm/mach-ux500/board-mop500-wlan.c index 16169d1cfcb..e525d4fe8aa 100644 --- a/arch/arm/mach-ux500/board-mop500-wlan.c +++ b/arch/arm/mach-ux500/board-mop500-wlan.c @@ -16,8 +16,6 @@ #include static void cw1200_release(struct device *dev); -static int cw1200_power_ctrl(const struct cw1200_platform_data *pdata, - bool enable); static struct resource cw1200_href_resources[] = { { @@ -68,10 +66,11 @@ EXPORT_SYMBOL_GPL(cw1200_get_platform_data); static int cw1200_pins_enable(bool enable) { - struct ux500_pins *pins; + struct ux500_pins *pins = NULL; int ret = 0; pins = ux500_pins_get("sdi1"); + if (!pins) { printk(KERN_ERR "cw1200: Pins are not found. " "Check platform data.\n"); @@ -93,45 +92,12 @@ static int cw1200_pins_enable(bool enable) return ret; } -static int cw1200_power_ctrl(const struct cw1200_platform_data *pdata, - bool enable) -{ - static const char *vdd_name = "vdd"; - struct regulator *vdd; - int ret = 0; - - vdd = regulator_get(&cw1200_device.dev, vdd_name); - if (IS_ERR(vdd)) { - ret = PTR_ERR(vdd); - dev_warn(&cw1200_device.dev, - "%s: Failed to get regulator '%s': %d\n", - __func__, vdd_name, ret); - } else { - if (enable) - ret = regulator_enable(vdd); - else - ret = regulator_disable(vdd); - - if (ret) { - dev_warn(&cw1200_device.dev, - "%s: Failed to %s regulator '%s': %d\n", - __func__, enable ? "enable" : "disable", - vdd_name, ret); - } - regulator_put(vdd); - } - return ret; -} - int __init mop500_wlan_init(void) { int ret; - if (machine_is_snowball() || - machine_is_u8500() || - machine_is_u5500() || - machine_is_nomadik()) { - cw1200_device.num_resources = - ARRAY_SIZE(cw1200_href_resources); + + if (machine_is_u8500() || machine_is_nomadik()) { + cw1200_device.num_resources = ARRAY_SIZE(cw1200_href_resources); cw1200_device.resource = cw1200_href_resources; } else if (machine_is_hrefv60()) { cw1200_device.num_resources = @@ -145,18 +111,12 @@ int __init mop500_wlan_init(void) return -ENOTSUPP; } - if (machine_is_snowball() || - machine_is_u5500()) - cw1200_platform_data.mmc_id = "mmc2"; - else - cw1200_platform_data.mmc_id = "mmc3"; + cw1200_platform_data.mmc_id = "mmc3"; cw1200_platform_data.reset = &cw1200_device.resource[0]; cw1200_platform_data.irq = &cw1200_device.resource[1]; cw1200_device.dev.release = cw1200_release; - if (machine_is_snowball()) - cw1200_platform_data.power_ctrl = cw1200_power_ctrl; ret = cw1200_pins_enable(true); if (WARN_ON(ret)) diff --git a/arch/arm/mach-ux500/board-u5500-wlan.c b/arch/arm/mach-ux500/board-u5500-wlan.c new file mode 100644 index 00000000000..68282db06c3 --- /dev/null +++ b/arch/arm/mach-ux500/board-u5500-wlan.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Dmitry Tarnyagin + *Author: Bartosz Markowski for ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include "pins.h" +#include + +static void cw1200_release(struct device *dev); + +static struct resource cw1200_u5500_resources[] = { + { + .start = NOMADIK_GPIO_TO_IRQ(129), + .end = NOMADIK_GPIO_TO_IRQ(129), + .flags = IORESOURCE_IRQ, + .name = "cw1200_irq", + }, +}; + +static struct cw1200_platform_data cw1200_platform_data = { 0 }; + +static struct platform_device cw1200_device = { + .name = "cw1200_wlan", + .dev = { + .platform_data = &cw1200_platform_data, + .release = cw1200_release, + .init_name = "cw1200_wlan", + }, +}; + +const struct cw1200_platform_data *cw1200_get_platform_data(void) +{ + return &cw1200_platform_data; +} +EXPORT_SYMBOL_GPL(cw1200_get_platform_data); + +static int cw1200_pins_enable(bool enable) +{ + struct ux500_pins *pins = NULL; + int ret = 0; + + pins = ux500_pins_get("sdi3"); + + if (!pins) { + printk(KERN_ERR "cw1200: Pins are not found. " + "Check platform data.\n"); + return -ENOENT; + } + + if (enable) + ret = ux500_pins_enable(pins); + else + ret = ux500_pins_disable(pins); + + if (ret) + printk(KERN_ERR "cw1200: Pins can not be %s: %d.\n", + enable ? "enabled" : "disabled", + ret); + + ux500_pins_put(pins); + + return ret; +} + +int __init u5500_wlan_init(void) +{ + int ret; + + if (machine_is_u5500()) { + cw1200_device.num_resources = ARRAY_SIZE(cw1200_u5500_resources); + cw1200_device.resource = cw1200_u5500_resources; + } else { + dev_err(&cw1200_device.dev, + "Unsupported mach type %d " + "(check mach-types.h)\n", + __machine_arch_type); + return -ENOTSUPP; + } + + cw1200_platform_data.mmc_id = "mmc2"; + cw1200_platform_data.irq = &cw1200_device.resource[0]; + + cw1200_device.dev.release = cw1200_release; + + ret = cw1200_pins_enable(true); + if (WARN_ON(ret)) + return ret; + + ret = platform_device_register(&cw1200_device); + if (ret) + cw1200_pins_enable(false); + + return ret; +} + +static void cw1200_release(struct device *dev) +{ + cw1200_pins_enable(false); +} diff --git a/arch/arm/mach-ux500/board-u5500-wlan.h b/arch/arm/mach-ux500/board-u5500-wlan.h new file mode 100644 index 00000000000..89fd41166fd --- /dev/null +++ b/arch/arm/mach-ux500/board-u5500-wlan.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * License terms: GNU General Public License (GPL), version 2 + * + * U5500 board specific cw1200 (WLAN device) initialization. + * + * Author: Dmitry Tarnyagin + * Author: Bartosz Markowski for ST-Ericsson + * + */ + +#ifndef __BOARD_U5500_WLAN_H +#define __BOARD_U5500_WLAN_H + +int u5500_wlan_init(void); + +#endif -- cgit v1.2.3 From e68ead5d0fb5c01fe09105e899e48f41358d3725 Mon Sep 17 00:00:00 2001 From: Robert Marklund Date: Wed, 28 Sep 2011 09:59:06 +0200 Subject: mach-ux500: wlan: Add prefix to wlan board func Change-Id: I844d5b2a3021616f082adf496f88645f534a9382 Signed-off-by: Robert Marklund --- arch/arm/mach-ux500/board-u5500-wlan.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-ux500/board-u5500-wlan.c b/arch/arm/mach-ux500/board-u5500-wlan.c index 68282db06c3..8459ebd6aac 100644 --- a/arch/arm/mach-ux500/board-u5500-wlan.c +++ b/arch/arm/mach-ux500/board-u5500-wlan.c @@ -27,22 +27,22 @@ static struct resource cw1200_u5500_resources[] = { }, }; -static struct cw1200_platform_data cw1200_platform_data = { 0 }; +static struct cw1200_platform_data cw1200_u5500_platform_data = { 0 }; static struct platform_device cw1200_device = { .name = "cw1200_wlan", .dev = { - .platform_data = &cw1200_platform_data, + .platform_data = &cw1200_u5500_platform_data, .release = cw1200_release, .init_name = "cw1200_wlan", }, }; -const struct cw1200_platform_data *cw1200_get_platform_data(void) +const struct cw1200_platform_data *cw1200_u5500_get_platform_data(void) { - return &cw1200_platform_data; + return &cw1200_u5500_platform_data; } -EXPORT_SYMBOL_GPL(cw1200_get_platform_data); +EXPORT_SYMBOL_GPL(cw1200_u5500_get_platform_data); static int cw1200_pins_enable(bool enable) { @@ -87,8 +87,8 @@ int __init u5500_wlan_init(void) return -ENOTSUPP; } - cw1200_platform_data.mmc_id = "mmc2"; - cw1200_platform_data.irq = &cw1200_device.resource[0]; + cw1200_u5500_platform_data.mmc_id = "mmc2"; + cw1200_u5500_platform_data.irq = &cw1200_device.resource[0]; cw1200_device.dev.release = cw1200_release; -- cgit v1.2.3 From 514579c8c36ec5e0bbe9449abe8a44c3d0134fd6 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Wed, 8 Jun 2011 09:17:42 +0200 Subject: cw1200: API change for ieee80211_channel_to_frequency Update for extra parameter in band selection New compat code only! Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24589 Reviewed-by: Dmitry TARNYAGIN Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Change-Id: I19e60f69d0214641465e2eb6257beb450643b01e Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/26975 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33472 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/txrx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 8616d1d2ffe..836018a4fb9 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -674,7 +674,9 @@ void cw1200_rx_cb(struct cw1200_common *priv, frame_control = *(__le16*)skb->data; hdr->mactime = 0; /* Not supported by WSM */ - hdr->freq = ieee80211_channel_to_frequency(arg->channelNumber); + hdr->freq = ieee80211_channel_to_frequency(arg->channelNumber, + (arg->channelNumber > 14) ? + IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ); hdr->band = (hdr->freq >= 5000) ? IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; -- cgit v1.2.3 From 60502d23d35ac62fade344d2ebc5196fa7a6b16a Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Tue, 12 Jul 2011 14:04:06 +0200 Subject: cw1200: Fix wrongly reported band. Band for freqencies in range 4000-5000 MHz was incorrectly reported as 2GHz. Fix uses channel number for checking band instead of frequency. Signed-off-by: Dmitry Tarnyagin Change-Id: Id19840deb3645608e6133cf8efa59567974811a0 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27032 Reviewed-by: Janusz DZIEDZIC Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33476 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/txrx.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 836018a4fb9..65a29fef8ff 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -674,11 +674,11 @@ void cw1200_rx_cb(struct cw1200_common *priv, frame_control = *(__le16*)skb->data; hdr->mactime = 0; /* Not supported by WSM */ - hdr->freq = ieee80211_channel_to_frequency(arg->channelNumber, - (arg->channelNumber > 14) ? - IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ); - hdr->band = (hdr->freq >= 5000) ? - IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; + hdr->band = (arg->channelNumber > 14) ? + IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; + hdr->freq = ieee80211_channel_to_frequency( + arg->channelNumber, + hdr->band); if (arg->rxedRate >= 4) rate = &priv->rates[arg->rxedRate - 2]; -- cgit v1.2.3 From 8449d805bc41862c5fb26fdc4d805aa741ef6e35 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Wed, 8 Jun 2011 08:56:16 +0200 Subject: cw1200: enabling WAPI support Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24585 Reviewed-by: Dmitry TARNYAGIN Tested-by: Janusz DZIEDZIC Reviewed-by: Janusz DZIEDZIC Change-Id: I3eb462095c57d9462f07422c7cdd133189b0ab75 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/26977 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33474 --- drivers/staging/cw1200/Kconfig | 7 +++++++ drivers/staging/cw1200/sta.c | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/staging/cw1200/Kconfig b/drivers/staging/cw1200/Kconfig index 5c6f18267e1..7ad63fa9e2c 100644 --- a/drivers/staging/cw1200/Kconfig +++ b/drivers/staging/cw1200/Kconfig @@ -45,6 +45,13 @@ config CW1200_5GHZ_SUPPORT CW1100 silicon. If unsure, say N. +config CW1200_WAPI_SUPPORT + bool "WAPI support" + depends on CW1200 + help + Say Y if your compat-wireless support WAPI. + If unsure, say N. + menu "Driver debug features" depends on CW1200 diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 65df33ac61f..a5b0f0ea759 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -526,8 +526,8 @@ int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, wsm_key->aesGroupKey.keyId = key->keyidx; } break; -#if 0 - case WLAN_CIPHER_SUITE_WAPI: +#ifdef CONFIG_CW1200_WAPI_SUPPORT + case WLAN_CIPHER_SUITE_SMS4: if (pairwise) { wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE; memcpy(wsm_key->wapiPairwiseKey.peerAddress, @@ -546,7 +546,7 @@ int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, wsm_key->wapiGroupKey.keyId = key->keyidx; } break; -#endif +#endif /* CONFIG_CW1200_WAPI_SUPPORT */ default: WARN_ON(1); cw1200_free_key(priv, idx); -- cgit v1.2.3 From 6823be647a6bb6811355a1058c499ec6d7e247b7 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Tue, 14 Jun 2011 10:11:11 +0200 Subject: cw1200: Remove unnecessary delays in cw1200_sdio_on. According to specification: "A valid reset shall be obtained by maintaining WRESETN active (low) for at least two cycles of LP_CLK after VDDIO is stable within it operating range." 2 cycles at of 32KHz is lesser than 1 ms. Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25976 Tested-by: Janusz DZIEDZIC Reviewed-by: Janusz DZIEDZIC Change-Id: I01871023276a4d7c3d0938dff6ca7957b75464ae Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/26978 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33475 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/cw1200_sdio.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index e106129f4a3..0438c5ec7cb 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -258,14 +258,7 @@ static int cw1200_sdio_on(const struct cw1200_platform_data *pdata) { const struct resource *reset = pdata->reset; gpio_request(reset->start, reset->name); - gpio_direction_output(reset->start, 1); - /* It is not stated in the datasheet, but at least some of devices - * have problems with reset if this stage is omited. */ - msleep(50); - gpio_set_value(reset->start, 0); - /* A valid reset shall be obtained by maintaining WRESETN - * active (low) for at least two cycles of LP_CLK after VDDIO - * is stable within it operating range. */ + gpio_direction_output(reset->start, 0); msleep(1); gpio_set_value(reset->start, 1); /* The host should wait 30 ms after the WRESETN release -- cgit v1.2.3 From 2d8a34d9dc08702c0db6139a5b45efe02e8676c8 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 15 Jun 2011 15:00:52 +0200 Subject: cw1200: Document and optimize device reset sequence. Some devices have problems with reset if WRESET is active (low) when device is being powered on. Additional WRESET cycle is required for these devices. Change-Id: I117c1776e3819260ec7f4aa4e0d14174eb3f7e82 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25624 Reviewed-by: Philippe LANGLAIS Reviewed-by: Robert MARKLUND Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27063 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33477 --- drivers/staging/cw1200/cw1200_sdio.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index 0438c5ec7cb..ae07267dc99 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -258,12 +258,19 @@ static int cw1200_sdio_on(const struct cw1200_platform_data *pdata) { const struct resource *reset = pdata->reset; gpio_request(reset->start, reset->name); + gpio_direction_output(reset->start, 1); + /* It is not stated in the datasheet, but at least some of devices + * have problems with reset if this stage is omited. */ + msleep(50); gpio_direction_output(reset->start, 0); + /* A valid reset shall be obtained by maintaining WRESETN + * active (low) for at least two cycles of LP_CLK after VDDIO + * is stable within it operating range. */ msleep(1); gpio_set_value(reset->start, 1); - /* The host should wait 30 ms after the WRESETN release + /* The host should wait 32 ms after the WRESETN release * for the on-chip LDO to stabilize */ - msleep(30); + msleep(32); cw1200_detect_card(pdata); return 0; } -- cgit v1.2.3 From bcd5f21573e805aec9743b218a47a833e4692ac9 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 7 Jul 2011 22:12:01 +0200 Subject: cw1200: Dynamic power save is offloaded to the firmware. Signed-off-by: Dmitry Tarnyagin Change-Id: Iff214651c82fe1f7203f3ce016e7646e41cccc35 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27071 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33478 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/debug.c | 15 +++++++++++++-- drivers/staging/cw1200/main.c | 1 + drivers/staging/cw1200/scan.c | 4 ++-- drivers/staging/cw1200/sta.c | 19 ++++++++++++++++--- drivers/staging/cw1200/wsm.c | 5 ++++- drivers/staging/cw1200/wsm.h | 10 +++++----- 6 files changed, 41 insertions(+), 13 deletions(-) diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 13c4570372f..612c74a7174 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -153,6 +153,18 @@ static int cw1200_status_show(struct seq_file *seq, void *v) priv->edca.params[i].maxReceiveLifetime); } if (priv->join_status == CW1200_JOIN_STATUS_STA) { + static const char *pmMode = "unknown"; + switch (priv->powersave_mode.pmMode) { + case WSM_PSM_ACTIVE: + pmMode = "off"; + break; + case WSM_PSM_PS: + pmMode = "on"; + break; + case WSM_PSM_FAST_PS: + pmMode = "dynamic"; + break; + } seq_printf(seq, "Preamble: %s\n", cw1200_debug_preamble[ priv->association_mode.preambleType]); @@ -166,8 +178,7 @@ static int cw1200_status_show(struct seq_file *seq, void *v) priv->bss_params.aid); seq_printf(seq, "Rates: 0x%.8X\n", priv->bss_params.operationalRateSet); - seq_printf(seq, "Powersave: %s\n", - priv->powersave_mode.pmMode ? "off" : "on"); + seq_printf(seq, "Powersave: %s\n", pmMode); } seq_printf(seq, "HT: %s\n", cw1200_is_ht(&priv->ht_info) ? "on" : "off"); diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 7aef0ea1c60..f809ebc7584 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -242,6 +242,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS | /* IEEE80211_HW_SUPPORTS_UAPSD | */ IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_CQM_RSSI | diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index 71268291181..6e5b89c5635 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -88,7 +88,7 @@ int cw1200_hw_scan(struct ieee80211_hw *hw, wsm_lock_tx(priv); if (priv->join_status == CW1200_JOIN_STATUS_STA && - priv->powersave_mode.pmMode != WSM_PSM_PS) { + !(priv->powersave_mode.pmMode & WSM_PSM_PS)) { struct wsm_set_pm pm = priv->powersave_mode; pm.pmMode = WSM_PSM_PS; WARN_ON(wsm_set_pm(priv, &pm)); @@ -139,7 +139,7 @@ void cw1200_scan_work(struct work_struct *work) WARN_ON(wsm_set_output_power(priv, priv->output_power * 10)); if (priv->join_status == CW1200_JOIN_STATUS_STA && - priv->powersave_mode.pmMode != WSM_PSM_PS) + !(priv->powersave_mode.pmMode & WSM_PSM_PS)) WARN_ON(wsm_set_pm(priv, &priv->powersave_mode)); if (priv->scan.req) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index a5b0f0ea759..fb5576f688a 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -285,9 +285,22 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) } if (changed & IEEE80211_CONF_CHANGE_PS) { - priv->powersave_mode.pmMode = - (conf->flags & IEEE80211_CONF_PS) ? - WSM_PSM_PS : WSM_PSM_ACTIVE; + if (!(conf->flags & IEEE80211_CONF_PS)) + priv->powersave_mode.pmMode = WSM_PSM_ACTIVE; + else if (conf->dynamic_ps_timeout <= 0) + priv->powersave_mode.pmMode = WSM_PSM_PS; + else + priv->powersave_mode.pmMode = WSM_PSM_FAST_PS; + + /* Firmware requires that value for this 1-byte field must + * be specified in units of 500us. Values above the 128ms + * threshold are not supported. */ + if (conf->dynamic_ps_timeout >= 0x80) + priv->powersave_mode.fastPsmIdlePeriod = 0xFF; + else + priv->powersave_mode.fastPsmIdlePeriod = + conf->dynamic_ps_timeout << 1; + if (priv->join_status == CW1200_JOIN_STATUS_STA) WARN_ON(wsm_set_pm(priv, &priv->powersave_mode)); } diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index c562f982477..e8662fa6054 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -654,7 +654,10 @@ int wsm_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) wsm_cmd_lock(priv); - WSM_PUT32(buf, arg->pmMode); + WSM_PUT8(buf, arg->pmMode); + WSM_PUT8(buf, arg->fastPsmIdlePeriod); + WSM_PUT8(buf, arg->apPsmChangePeriod); + WSM_PUT8(buf, arg->minAutoPsPollPeriod); ret = wsm_cmd_send(priv, buf, NULL, 0x0010, WSM_CMD_TIMEOUT); diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 4d446e9b924..d8db64da9b3 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -133,16 +133,16 @@ struct cw1200_common; #define WSM_PSM_ACTIVE (0) /* 802.11 PS mode */ -#define WSM_PSM_PS (1) +#define WSM_PSM_PS BIT(0) + +/* Dynamic aka Fast power save */ +#define WSM_PSM_FAST_PS (BIT(0) | BIT(7)) /* Undetermined */ /* Note : Undetermined status is reported when the */ /* NULL data frame used to advertise the PM mode to */ /* the AP at Pre or Post Background Scan is not Acknowledged */ -#define WSM_PSM_UNKNOWN (2) - -/* Use this flag to enable the fast power-saving mode */ -#define WSM_PM_F_FAST_PSM_ENABLE (0x80) +#define WSM_PSM_UNKNOWN BIT(1) /* Queue IDs */ /* best effort/legacy */ -- cgit v1.2.3 From 48d7216c7dec8855069a1e506f5eb7575ec33678 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Mon, 11 Jul 2011 16:03:44 +0200 Subject: cw1200: Syncing WSM API usage with firmware team. * WSM is not officially supporting link ID mapping in STA mode. * Foregroung scan is not officially supported when joined. Signed-off-by: Dmitry Tarnyagin Change-Id: I4a72884ae2d0202f1d57ff724c7323df2177441a Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27072 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33479 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 5 ----- drivers/staging/cw1200/scan.c | 7 +++++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 561dab3f94f..f17ce334ed1 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -38,13 +38,8 @@ int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, .link_id = 0, }; - - /* Link ID mapping works fine in STA mode as well. - * It's better to keep same handling for both STA ans AP modes */ -#if 0 if (priv->mode != NL80211_IFTYPE_AP) return 0; -#endif map_link.link_id = ffs(~(priv->link_id_map | 1)) - 1; if (map_link.link_id > CW1200_MAX_STA_IN_AP_MODE) { diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index 6e5b89c5635..7fb42d4ee05 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -192,6 +192,11 @@ void cw1200_scan_work(struct work_struct *work) scan.numOfChannels = it - priv->scan.begin; /* TODO: Is it optimal? */ scan.probeDelay = 100; + /* It is not stated in WSM specification, however + * FW team says that driver may not use FG scan + * when joined. */ + if (priv->join_status == CW1200_JOIN_STATUS_STA) + scan.scanType = WSM_SCAN_TYPE_BACKGROUND; scan.ch = kzalloc( sizeof(struct wsm_scan_ch[it - priv->scan.begin]), GFP_KERNEL); @@ -324,6 +329,8 @@ void cw1200_probe_work(struct work_struct *work) scan.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; + if (priv->join_status == CW1200_JOIN_STATUS_STA) + scan.scanType = WSM_SCAN_TYPE_BACKGROUND; ch[0].number = priv->channel->hw_value; skb_pull(frame.skb, sizeof(struct wsm_tx)); -- cgit v1.2.3 From 75f1bd193f1582595c875ff96719dab04f8b0a83 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Mon, 11 Jul 2011 17:18:10 +0200 Subject: cw1200: Aligning mac80211 API with compat-wireless-3.0-rc4-1 Signed-off-by: Dmitry Tarnyagin Change-Id: I8bf50cd2f97dcb5677b2db8efc89757d74eb3f55 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27073 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33480 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 3 ++- drivers/staging/cw1200/ap.h | 3 ++- drivers/staging/cw1200/txrx.c | 8 ++++---- drivers/staging/cw1200/txrx.h | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index f17ce334ed1..b9e91fb07b7 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -420,7 +420,8 @@ void cw1200_multicast_stop_work(struct work_struct *work) int cw1200_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn) + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) { /* Aggregation is implemented fully in firmware, * including block ack negotiation. Do not allow diff --git a/drivers/staging/cw1200/ap.h b/drivers/staging/cw1200/ap.h index 63e6165ed69..59462248bf4 100644 --- a/drivers/staging/cw1200/ap.h +++ b/drivers/staging/cw1200/ap.h @@ -28,7 +28,8 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, int cw1200_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn); + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size); void cw1200_suspend_resume(struct cw1200_common *priv, struct wsm_suspend_resume *arg); diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 65a29fef8ff..9b2864c8bde 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -412,7 +412,7 @@ int cw1200_skb_to_wsm(struct cw1200_common *priv, struct sk_buff *skb, /* ******************************************************************** */ -int cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) { struct cw1200_common *priv = dev->priv; unsigned queue = skb_get_queue_mapping(skb); @@ -516,16 +516,16 @@ int cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) else goto err; - return NETDEV_TX_OK; + return; err: /* TODO: Update TX failure counters */ dev_kfree_skb_any(skb); - return NETDEV_TX_OK; + return; drop: dev_kfree_skb_any(skb); - return NETDEV_TX_OK; + return; } /* ******************************************************************** */ diff --git a/drivers/staging/cw1200/txrx.h b/drivers/staging/cw1200/txrx.h index 12a9b49028b..12ec3d9808e 100644 --- a/drivers/staging/cw1200/txrx.h +++ b/drivers/staging/cw1200/txrx.h @@ -61,7 +61,7 @@ u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates); int cw1200_skb_to_wsm(struct cw1200_common *priv, struct sk_buff *skb, struct wsm_tx *wsm); -int cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb); +void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb); /* ******************************************************************** */ /* WSM callbacks */ -- cgit v1.2.3 From 9ace2c7c5d8c07f126abf8b84f6a397c8c985724 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 13 Jul 2011 18:06:26 +0200 Subject: cw1200: Driver should not report MCS rates to mac80211. Driver should not provide MCS rates to mac80211 stack in ieee80211_supported_band.bitrates table. Signed-off-by: Dmitry Tarnyagin Change-Id: I2f5287c4059f51ba7827607ac72fb2a93e4c05b8 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27148 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33481 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/main.c | 7 +++++-- drivers/staging/cw1200/txrx.c | 14 +++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index f809ebc7584..7a4c2536976 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -71,6 +71,9 @@ static struct ieee80211_rate cw1200_rates[] = { RATETAB_ENT(360, 11, 0), RATETAB_ENT(480, 12, 0), RATETAB_ENT(540, 13, 0), +}; + +static struct ieee80211_rate cw1200_mcs_rates[] = { RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS), RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS), RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS), @@ -85,8 +88,8 @@ static struct ieee80211_rate cw1200_rates[] = { #define cw1200_a_rates_size (ARRAY_SIZE(cw1200_rates) - 4) #define cw1200_g_rates (cw1200_rates + 0) #define cw1200_g_rates_size (ARRAY_SIZE(cw1200_rates)) -#define cw1200_n_rates (cw1200_rates + 12) -#define cw1200_n_rates_size (ARRAY_SIZE(cw1200_rates) - 12) +#define cw1200_n_rates (cw1200_mcs_rates) +#define cw1200_n_rates_size (ARRAY_SIZE(cw1200_mcs_rates)) #define CHAN2G(_channel, _freq, _flags) { \ diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 9b2864c8bde..0ca01c7ae0b 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -643,7 +643,6 @@ void cw1200_rx_cb(struct cw1200_common *priv, { struct sk_buff *skb = *skb_p; struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); - const struct ieee80211_rate *rate; __le16 frame_control; hdr->flag = 0; @@ -680,16 +679,13 @@ void cw1200_rx_cb(struct cw1200_common *priv, arg->channelNumber, hdr->band); - if (arg->rxedRate >= 4) - rate = &priv->rates[arg->rxedRate - 2]; - else - rate = &priv->rates[arg->rxedRate]; - - if (rate >= priv->mcs_rates) { - hdr->rate_idx = rate - priv->mcs_rates; + if (arg->rxedRate >= 14) { hdr->flag |= RX_FLAG_HT; + hdr->rate_idx = arg->rxedRate - 14; + } else if (arg->rxedRate >= 4) { + hdr->rate_idx = arg->rxedRate - 2; } else { - hdr->rate_idx = rate - priv->rates; + hdr->rate_idx = arg->rxedRate; } hdr->signal = (s8)arg->rcpiRssi; -- cgit v1.2.3 From 0803d6c860cc15c7b452bd7a6c7e1e7a87a17263 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Thu, 14 Jul 2011 11:55:04 +0200 Subject: cw1200: Multicast filtering support. Change-Id: Iccec781533e5ac017ee0ed04741fc224a899f9d9 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27175 Reviewed-by: Janusz DZIEDZIC Tested-by: Janusz DZIEDZIC Reviewed-by: Dmitry TARNYAGIN Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33482 Reviewed-by: Bartosz MARKOWSKI Tested-by: Dmitry TARNYAGIN Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/cw1200.h | 1 + drivers/staging/cw1200/main.c | 1 + drivers/staging/cw1200/sta.c | 31 +++++++++++++++++++++++++++++++ drivers/staging/cw1200/sta.h | 3 +++ drivers/staging/cw1200/wsm.h | 28 ++++++++++++++++++++++++++++ 5 files changed, 64 insertions(+) diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index cd5061d1097..f732af75fa0 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -109,6 +109,7 @@ struct cw1200_common { struct wsm_rx_filter rx_filter; struct wsm_beacon_filter_control bf_control; u8 ba_tid_mask; + struct wsm_multicast_filter multicast_filter; /* BH */ atomic_t bh_rx; diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 7a4c2536976..3b3c12488cb 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -214,6 +214,7 @@ static const struct ieee80211_ops cw1200_ops = { .set_rts_threshold = cw1200_set_rts_threshold, .config = cw1200_config, .bss_info_changed = cw1200_bss_info_changed, + .prepare_multicast = cw1200_prepare_multicast, .configure_filter = cw1200_configure_filter, .conf_tx = cw1200_conf_tx, .get_stats = cw1200_get_stats, diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index fb5576f688a..6906d932430 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -357,6 +357,8 @@ void cw1200_update_filtering(struct cw1200_common *priv) ret = wsm_beacon_filter_control(priv, &priv->bf_control); if (!ret) ret = wsm_set_bssid_filtering(priv, !priv->rx_filter.bssid); + if (!ret) + ret = wsm_set_multicast_filter(priv, &priv->multicast_filter); if (ret) wiphy_err(priv->hw->wiphy, "%s: Update filtering failed: %d.\n", @@ -364,6 +366,35 @@ void cw1200_update_filtering(struct cw1200_common *priv) return; } +u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + struct cw1200_common *priv = hw->priv; + struct netdev_hw_addr *ha; + int count = 0; + + /* Disable multicast filtering */ + memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter)); + + if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES) + return 0; + + /* Enable if requested */ + netdev_hw_addr_list_for_each(ha, mc_list) { + sta_printk(KERN_DEBUG "[STA] multicast: %pM\n", ha->addr); + memcpy(&priv->multicast_filter.macAddress[count], + ha->addr, ETH_ALEN); + count++; + } + + if (count) { + priv->multicast_filter.enable = __cpu_to_le32(1); + priv->multicast_filter.numOfAddresses = __cpu_to_le32(count); + } + + return netdev_hw_addr_list_count(mc_list); +} + void cw1200_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, diff --git a/drivers/staging/cw1200/sta.h b/drivers/staging/cw1200/sta.h index 2a3c8836c0b..8951a4a176b 100644 --- a/drivers/staging/cw1200/sta.h +++ b/drivers/staging/cw1200/sta.h @@ -42,6 +42,9 @@ int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value); void cw1200_flush(struct ieee80211_hw *hw, bool drop); +u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list); + /* ******************************************************************** */ /* WSM callbacks */ diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index d8db64da9b3..9b33a6e2b83 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -358,6 +358,7 @@ struct cw1200_common; /* 4.5 dot11GroupAddressesTable */ #define WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE 0x0004 +#define WSM_MAX_GRP_ADDRTABLE_ENTRIES 8 /* 4.6 dot11WepDefaultKeyId */ #define WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID 0x0005 @@ -1446,6 +1447,33 @@ static inline int wsm_set_bssid_filtering(struct cw1200_common *priv, &arg, sizeof(arg)); } +/* Multicat filtering - 4.5 */ +struct wsm_multicast_filter { + __le32 enable; + __le32 numOfAddresses; + u8 macAddress[WSM_MAX_GRP_ADDRTABLE_ENTRIES][ETH_ALEN]; +} __packed; + +static inline int wsm_set_multicast_filter(struct cw1200_common *priv, + struct wsm_multicast_filter *fp) +{ + return wsm_write_mib(priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE, + fp, sizeof(*fp)); +} + +/* IPv4 filtering - 4.10 */ +struct wsm_ipv4_filter { + __le32 enable; + u8 ipv4Address[4]; +} __packed; + +static inline int wsm_set_ipv4_arp_filter(struct cw1200_common *priv, + struct wsm_ipv4_filter *fp) +{ + return wsm_write_mib(priv, WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE, + fp, sizeof(*fp)); +} + /* UseMultiTxConfMessage */ static inline int wsm_use_multi_tx_conf(struct cw1200_common *priv, -- cgit v1.2.3 From c2cb8b17ab4e0219242ff985fdfad783a8f1d1d3 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 14 Jul 2011 22:41:49 +0200 Subject: cw1200: Removed obsolete code (software keepalive). Earlier WSM firmware has not supported keepalive. Software workaround was implemented to keep connection. This commit removes this code as obsolete. Signed-off-by: Dmitry Tarnyagin Chnage-Id: Ib00260c8cc05c4e7170d8cc6a071415fc87b8f03 Change-Id: I32c3b7f89e97a262592151a582235c4401de594c Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27285 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33483 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/Kconfig | 10 ---------- drivers/staging/cw1200/cw1200.h | 4 ---- drivers/staging/cw1200/main.c | 3 --- drivers/staging/cw1200/sta.c | 39 --------------------------------------- drivers/staging/cw1200/txrx.c | 3 --- 5 files changed, 59 deletions(-) diff --git a/drivers/staging/cw1200/Kconfig b/drivers/staging/cw1200/Kconfig index 7ad63fa9e2c..946d366cbfb 100644 --- a/drivers/staging/cw1200/Kconfig +++ b/drivers/staging/cw1200/Kconfig @@ -27,16 +27,6 @@ config CW1200_USE_GPIO_IRQ Say Y here if you want to include GPIO IRQ support instead of SDIO IRQ. If unsure, say N. -config CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE - bool "Software keep-alive (DEVELOPMENT)" - depends on CW1200 - help - Say Y if your firmware does not support keep-alive functionality - or you suspect problems with the implementation. - Please finalize software the software keep-alive functionality in - that case. - If unsure, say N. - config CW1200_5GHZ_SUPPORT bool "5GHz band support" depends on CW1200 diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index f732af75fa0..97afb8c1392 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -173,10 +173,6 @@ struct cw1200_common { struct work_struct event_handler; struct delayed_work bss_loss_work; struct delayed_work connection_loss_work; -#if defined(CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) - struct delayed_work keep_alive_work; - unsigned long last_activity_time; -#endif /* CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ struct work_struct tx_failure_work; int delayed_link_loss; diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 3b3c12488cb..15dd4fd5a1b 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -314,9 +314,6 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work); INIT_DELAYED_WORK(&priv->connection_loss_work, cw1200_connection_loss_work); -#if defined(CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) - INIT_DELAYED_WORK(&priv->keep_alive_work, cw1200_keep_alive_work); -#endif /* CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ INIT_WORK(&priv->tx_failure_work, cw1200_tx_failure_work); INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work); INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work); diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 6906d932430..50f447fb46b 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -109,9 +109,6 @@ void cw1200_stop(struct ieee80211_hw *dev) cancel_delayed_work_sync(&priv->join_timeout); cancel_delayed_work_sync(&priv->bss_loss_work); cancel_delayed_work_sync(&priv->connection_loss_work); -#if defined(CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) - cancel_delayed_work_sync(&priv->keep_alive_work); -#endif /* CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ mutex_lock(&priv->conf_mutex); switch (priv->join_status) { @@ -826,28 +823,6 @@ void cw1200_connection_loss_work(struct work_struct *work) ieee80211_connection_loss(priv->vif); } -#if defined(CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) -void cw1200_keep_alive_work(struct work_struct *work) -{ - struct cw1200_common *priv = - container_of(work, struct cw1200_common, keep_alive_work.work); - unsigned long now = jiffies; - unsigned long delta = now - priv->last_activity_time; - unsigned long tmo = 30 * HZ; - - if (delta >= tmo) { - sta_printk(KERN_DEBUG "[CQM] Keep-alive ping.\n"); - STUB(); - /* TODO: Do a keep-alive ping :) */ - priv->last_activity_time = now; - } else { - tmo -= delta; - } - queue_delayed_work(priv->workqueue, - &priv->keep_alive_work, tmo); -} -#endif /* CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ - void cw1200_tx_failure_work(struct work_struct *work) { struct cw1200_common *priv = @@ -1019,12 +994,6 @@ void cw1200_join_work(struct work_struct *work) WARN_ON(wsm_set_block_ack_policy(priv, priv->ba_tid_mask, priv->ba_tid_mask)); -#if defined(CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) - priv->last_activity_time = jiffies; - /* Queue keep-alive ping avery 30 sec. */ - queue_delayed_work(priv->workqueue, - &priv->keep_alive_work, 30 * HZ); -#endif /* CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ /* Queue unjoin if not associated in 3 sec. */ queue_delayed_work(priv->workqueue, &priv->join_timeout, 3 * HZ); @@ -1036,17 +1005,12 @@ void cw1200_join_work(struct work_struct *work) cw1200_queue_remove(&priv->tx_queue[queueId], priv, __le32_to_cpu(wsm->packetID)); cancel_delayed_work_sync(&priv->join_timeout); -#if defined(CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) - cancel_delayed_work_sync(&priv->keep_alive_work); -#endif /* CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ cw1200_update_listening(priv, priv->listening); WARN_ON(wsm_set_pm(priv, &priv->powersave_mode)); } else { /* Upload keys */ WARN_ON(cw1200_upload_keys(priv)); -#if !defined(CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) WARN_ON(wsm_keep_alive_period(priv, 30 /* sec */)); -#endif /* CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ cw1200_queue_requeue(&priv->tx_queue[queueId], __le32_to_cpu(wsm->packetID)); } @@ -1089,9 +1053,6 @@ void cw1200_unjoin_work(struct work_struct *work) cw1200_free_event_queue(priv); cancel_work_sync(&priv->event_handler); cancel_delayed_work_sync(&priv->connection_loss_work); -#if defined(CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) - cancel_delayed_work_sync(&priv->keep_alive_work); -#endif /* CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ cw1200_update_listening(priv, priv->listening); cw1200_update_filtering(priv); sta_printk(KERN_DEBUG "[STA] Unjoin.\n"); diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 0ca01c7ae0b..5db1282b188 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -592,9 +592,6 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, if (likely(!arg->status)) { tx->flags |= IEEE80211_TX_STAT_ACK; -#if defined(CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE) - priv->last_activity_time = jiffies; -#endif /* CONFIG_CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE */ priv->cqm_tx_failure_count = 0; ++tx_count; cw1200_debug_txed(priv); -- cgit v1.2.3 From c419f3df8e70242bd7dd45eeb6d4a62de95601ec Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 13 Jul 2011 23:03:17 +0200 Subject: cw1200: Wakeup on wireless (WoW) is implemented. + Core and SDIO power management is implemented as defined in wireless-next v3.0-rc4. + SDD is cached to avoid disk access at suspend/resume time. TODO: - WoW conditions and filtering are not yet implemented. - BUG: Late interrupts (coming after suspend() callback but before the actual suspend) are not detected as wakeup sources. Universal (Android/GLK) way of waking up halfly-suspended system to be found. Signed-off-by: Dmitry Tarnyagin Change-Id: Ib4931a261e592f2927455e988055cd673250ec81 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27297 Tested-by: Bartosz MARKOWSKI Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33484 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/Makefile | 3 +- drivers/staging/cw1200/bh.c | 70 +++++++++++++++-- drivers/staging/cw1200/bh.h | 2 + drivers/staging/cw1200/cw1200.h | 17 +++-- drivers/staging/cw1200/cw1200_plat.h | 5 ++ drivers/staging/cw1200/cw1200_sdio.c | 45 +++++++---- drivers/staging/cw1200/main.c | 25 ++++-- drivers/staging/cw1200/pm.c | 142 +++++++++++++++++++++++++++++++++++ drivers/staging/cw1200/pm.h | 23 ++++++ drivers/staging/cw1200/sbus.h | 1 + drivers/staging/cw1200/sta.c | 58 +++++++------- drivers/staging/cw1200/sta.h | 1 + drivers/staging/cw1200/wsm.c | 4 +- 13 files changed, 329 insertions(+), 67 deletions(-) create mode 100644 drivers/staging/cw1200/pm.c create mode 100644 drivers/staging/cw1200/pm.h diff --git a/drivers/staging/cw1200/Makefile b/drivers/staging/cw1200/Makefile index fae984d10ce..c0e88bd0f11 100644 --- a/drivers/staging/cw1200/Makefile +++ b/drivers/staging/cw1200/Makefile @@ -9,7 +9,8 @@ cw1200_core-y := \ sta.o \ ap.o \ scan.o -cw1200_core-$(CONFIG_CW1200_DEBUGFS) += debug.o +cw1200_core-$(CONFIG_CW1200_DEBUGFS) += debug.o +cw1200_core-$(CONFIG_PM) += pm.o cw1200_wlan-y := cw1200_sdio.o diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index d49f9fdf732..2d438a96e38 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -39,6 +39,14 @@ static int cw1200_bh(void *arg); #define PIGGYBACK_CTRL_REG (2) #define EFFECTIVE_BUF_SIZE (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG) +/* Suspend state privates */ +enum cw1200_bh_pm_state { + CW1200_BH_RESUMED = 0, + CW1200_BH_SUSPEND, + CW1200_BH_SUSPENDED, + CW1200_BH_RESUME, +}; + typedef int (*cw1200_wsm_handler)(struct cw1200_common *priv, u8 *data, size_t size); @@ -52,10 +60,11 @@ int cw1200_register_bh(struct cw1200_common *priv) atomic_set(&priv->bh_rx, 0); atomic_set(&priv->bh_tx, 0); atomic_set(&priv->bh_term, 0); + atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED); priv->buf_id_tx = 0; priv->buf_id_rx = 0; init_waitqueue_head(&priv->bh_wq); - init_waitqueue_head(&priv->hw_bufs_used_wq); + init_waitqueue_head(&priv->bh_evt_wq); priv->bh_thread = kthread_create(&cw1200_bh, priv, "cw1200_bh"); if (IS_ERR(priv->bh_thread)) { err = PTR_ERR(priv->bh_thread); @@ -107,6 +116,30 @@ void cw1200_bh_wakeup(struct cw1200_common *priv) wake_up_interruptible(&priv->bh_wq); } +void cw1200_bh_suspend(struct cw1200_common *priv) +{ + bh_printk(KERN_DEBUG "[BH] suspend.\n"); + if (WARN_ON(priv->bh_error)) + return; + + atomic_set(&priv->bh_suspend, CW1200_BH_SUSPEND); + wake_up_interruptible(&priv->bh_wq); + wait_event_interruptible(priv->bh_evt_wq, priv->bh_error || + (CW1200_BH_SUSPENDED == atomic_read(&priv->bh_suspend))); +} + +void cw1200_bh_resume(struct cw1200_common *priv) +{ + bh_printk(KERN_DEBUG "[BH] resume.\n"); + if (WARN_ON(priv->bh_error)) + return; + + atomic_set(&priv->bh_suspend, CW1200_BH_RESUME); + wake_up_interruptible(&priv->bh_wq); + wait_event_interruptible(priv->bh_evt_wq, priv->bh_error || + (CW1200_BH_RESUMED == atomic_read(&priv->bh_suspend))); +} + static inline void wsm_alloc_tx_buffer(struct cw1200_common *priv) { ++priv->hw_bufs_used; @@ -123,7 +156,7 @@ int wsm_release_tx_buffer(struct cw1200_common *priv, int count) else if (hw_bufs_used >= priv->wsm_caps.numInpChBufs) ret = 1; if (!priv->hw_bufs_used) - wake_up_interruptible(&priv->hw_bufs_used_wq); + wake_up_interruptible(&priv->bh_evt_wq); return ret; } @@ -221,7 +254,7 @@ static int cw1200_bh(void *arg) struct cw1200_common *priv = arg; struct sk_buff *skb_rx = NULL; size_t read_len = 0; - int rx, tx, term; + int rx, tx, term, suspend; struct wsm_hdr *wsm; size_t wsm_len; int wsm_id; @@ -244,7 +277,8 @@ static int cw1200_bh(void *arg) rx = atomic_xchg(&priv->bh_rx, 0); tx = atomic_xchg(&priv->bh_tx, 0); term = atomic_xchg(&priv->bh_term, 0); - (rx || tx || term); + suspend = atomic_read(&priv->bh_suspend); + (rx || tx || term || suspend); }), status); if (status < 0 || term) @@ -252,9 +286,35 @@ static int cw1200_bh(void *arg) if (!status) { bh_printk(KERN_DEBUG "[BH] Device wakedown.\n"); - WARN_ON(cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 0)); + WARN_ON(cw1200_reg_write_16(priv, + ST90TDS_CONTROL_REG_ID, 0)); priv->device_can_sleep = true; continue; + } else if (suspend) { + bh_printk(KERN_DEBUG "[BH] Device suspend.\n"); + if (priv->powersave_enabled && + !priv->device_can_sleep) { + WARN_ON(cw1200_reg_write_16(priv, + ST90TDS_CONTROL_REG_ID, 0)); + priv->device_can_sleep = true; + } + + atomic_set(&priv->bh_suspend, CW1200_BH_SUSPENDED); + wake_up_interruptible(&priv->bh_evt_wq); + status = wait_event_interruptible(priv->bh_wq, + CW1200_BH_RESUME == atomic_read( + &priv->bh_suspend)); + if (status < 0) { + wiphy_err(priv->hw->wiphy, + "%s: Failed to wait for resume: %ld.\n", + __func__, status); + break; + } + bh_printk(KERN_DEBUG "[BH] Device resume.\n"); + atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED); + wake_up_interruptible(&priv->bh_evt_wq); + atomic_add(1, &priv->bh_rx); + continue; } tx += pending_tx; diff --git a/drivers/staging/cw1200/bh.h b/drivers/staging/cw1200/bh.h index 14b641fbe1d..a19393c61dd 100644 --- a/drivers/staging/cw1200/bh.h +++ b/drivers/staging/cw1200/bh.h @@ -22,6 +22,8 @@ int cw1200_register_bh(struct cw1200_common *priv); void cw1200_unregister_bh(struct cw1200_common *priv); void cw1200_irq_handler(struct cw1200_common *priv); void cw1200_bh_wakeup(struct cw1200_common *priv); +void cw1200_bh_suspend(struct cw1200_common *priv); +void cw1200_bh_resume(struct cw1200_common *priv); /* Must be called from BH thread. */ void cw1200_enable_powersave(struct cw1200_common *priv, bool enable); diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 97afb8c1392..68d82874765 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -33,6 +33,8 @@ /* extern */ struct sbus_ops; /* extern */ struct task_struct; /* extern */ struct cw1200_debug_priv; +/* extern */ struct cw1200_suspend_state; +/* extern */ struct firmware; #if defined(CONFIG_CW1200_TXRX_DEBUG) #define txrx_printk(...) printk(__VA_ARGS__) @@ -81,6 +83,7 @@ struct cw1200_common { /* calibration, output power limit and rssi<->dBm conversation data */ /* BBP/MAC state */ + const struct firmware *sdd; struct ieee80211_rate *rates; struct ieee80211_rate *mcs_rates; u8 mac_addr[ETH_ALEN]; @@ -110,20 +113,22 @@ struct cw1200_common { struct wsm_beacon_filter_control bf_control; u8 ba_tid_mask; struct wsm_multicast_filter multicast_filter; + struct cw1200_suspend_state *suspend_state; /* BH */ atomic_t bh_rx; atomic_t bh_tx; atomic_t bh_term; + atomic_t bh_suspend; struct task_struct *bh_thread; int bh_error; wait_queue_head_t bh_wq; + wait_queue_head_t bh_evt_wq; int buf_id_tx; /* byte */ int buf_id_rx; /* byte */ int wsm_rx_seq; /* byte */ int wsm_tx_seq; /* byte */ int hw_bufs_used; - wait_queue_head_t hw_bufs_used_wq; struct sk_buff *skb_cache; bool powersave_enabled; bool device_can_sleep; @@ -194,11 +199,11 @@ struct cw1200_sta_priv { }; /* interfaces for the drivers */ -int cw1200_probe(const struct sbus_ops *sbus_ops, - struct sbus_priv *sbus, - struct device *pdev, - struct cw1200_common **pself); -void cw1200_release(struct cw1200_common *self); +int cw1200_core_probe(const struct sbus_ops *sbus_ops, + struct sbus_priv *sbus, + struct device *pdev, + struct cw1200_common **pself); +void cw1200_core_release(struct cw1200_common *self); #define CW1200_DBG_MSG 0x00000001 #define CW1200_DBG_NIY 0x00000002 diff --git a/drivers/staging/cw1200/cw1200_plat.h b/drivers/staging/cw1200/cw1200_plat.h index ed354de0edf..e79794f42a3 100644 --- a/drivers/staging/cw1200/cw1200_plat.h +++ b/drivers/staging/cw1200/cw1200_plat.h @@ -5,6 +5,9 @@ * License terms: GNU General Public License (GPL) version 2 */ +#ifndef CW1200_PLAT_H_INCLUDED +#define CW1200_PLAT_H_INCLUDED + #include struct cw1200_platform_data { @@ -17,3 +20,5 @@ struct cw1200_platform_data { /* Declaration only. Should be implemented in arch/xxx/mach-yyy */ const struct cw1200_platform_data *cw1200_get_platform_data(void); + +#endif /* CW1200_PLAT_H_INCLUDED */ diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index ae07267dc99..cf5c5c185be 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -39,7 +39,7 @@ struct sbus_priv { void *irq_priv; }; -static const struct sdio_device_id if_sdio_ids[] = { +static const struct sdio_device_id cw1200_sdio_ids[] = { { SDIO_DEVICE(SDIO_ANY_ID, SDIO_ANY_ID) }, { /* end: all zeroes */ }, }; @@ -52,7 +52,8 @@ static int cw1200_sdio_memcpy_fromio(struct sbus_priv *self, { int ret = sdio_memcpy_fromio(self->func, dst, addr, count); if (ret) { - printk(KERN_ERR "!!! Can't read %d bytes from 0x%.8X. Err %d.\n", + printk(KERN_ERR "!!! Can't read %d bytes from 0x%.8X." + " Err %d.\n", count, addr, ret); } return ret; @@ -111,10 +112,6 @@ static int cw1200_request_irq(struct sbus_priv *self, if (WARN_ON(ret < 0)) goto exit; - ret = enable_irq_wake(irq->start); - if (WARN_ON(ret)) - goto free_irq; - /* Hack to access Fuction-0 */ func_num = self->func->num; self->func->num = 0; @@ -139,8 +136,6 @@ static int cw1200_request_irq(struct sbus_priv *self, set_func: self->func->num = func_num; - disable_irq_wake(irq->start); -free_irq: free_irq(irq->start, self); exit: return ret; @@ -191,7 +186,6 @@ static int cw1200_sdio_irq_unsubscribe(struct sbus_priv *self) ret = sdio_release_irq(self->func); sdio_release_host(self->func); #else - disable_irq_wake(irq->start); free_irq(irq->start, self); #endif @@ -283,7 +277,7 @@ static int cw1200_sdio_reset(struct sbus_priv *self) return 0; } -static size_t cw1200_align_size(struct sbus_priv *self, size_t size) +static size_t cw1200_sdio_align_size(struct sbus_priv *self, size_t size) { size_t aligned = sdio_align_size(self->func, size); /* HACK!!! Problems with DMA size on u8500 platform */ @@ -295,6 +289,25 @@ static size_t cw1200_align_size(struct sbus_priv *self, size_t size) return aligned; } +static int cw1200_sdio_pm(struct sbus_priv *self, bool suspend) +{ + int ret; + const struct resource *irq = self->pdata->irq; + struct sdio_func *func = self->func; + + sdio_claim_host(func); + if (suspend) + ret = mmc_host_disable(func->card->host); + else + ret = mmc_host_enable(func->card->host); + sdio_release_host(func); + + if (!ret && irq) + ret = set_irq_wake(irq->start, suspend); + + return ret; +} + static struct sbus_ops cw1200_sdio_sbus_ops = { .sbus_memcpy_fromio = cw1200_sdio_memcpy_fromio, .sbus_memcpy_toio = cw1200_sdio_memcpy_toio, @@ -303,7 +316,8 @@ static struct sbus_ops cw1200_sdio_sbus_ops = { .irq_subscribe = cw1200_sdio_irq_subscribe, .irq_unsubscribe = cw1200_sdio_irq_unsubscribe, .reset = cw1200_sdio_reset, - .align_size = cw1200_align_size, + .align_size = cw1200_sdio_align_size, + .power_mgmt = cw1200_sdio_pm, }; /* Probe Function to be called by SDIO stack when device is discovered */ @@ -329,7 +343,7 @@ static int cw1200_sdio_probe(struct sdio_func *func, sdio_enable_func(func); sdio_release_host(func); - status = cw1200_probe(&cw1200_sdio_sbus_ops, + status = cw1200_core_probe(&cw1200_sdio_sbus_ops, self, &func->dev, &self->core); if (status) { sdio_claim_host(func); @@ -342,14 +356,15 @@ static int cw1200_sdio_probe(struct sdio_func *func, return status; } -/* Disconnect Function to be called by SDIO stack when device is disconnected */ +/* Disconnect Function to be called by SDIO stack when + * device is disconnected */ static void cw1200_sdio_disconnect(struct sdio_func *func) { struct sbus_priv *self = sdio_get_drvdata(func); if (self) { if (self->core) { - cw1200_release(self->core); + cw1200_core_release(self->core); self->core = NULL; } sdio_claim_host(func); @@ -362,7 +377,7 @@ static void cw1200_sdio_disconnect(struct sdio_func *func) static struct sdio_driver sdio_driver = { .name = "cw1200_wlan", - .id_table = if_sdio_ids, + .id_table = cw1200_sdio_ids, .probe = cw1200_sdio_probe, .remove = cw1200_sdio_disconnect, }; diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 15dd4fd5a1b..8a621ff2b06 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -26,7 +26,6 @@ #include #include #include - #include #include "cw1200.h" @@ -39,6 +38,7 @@ #include "ap.h" #include "scan.h" #include "debug.h" +#include "pm.h" MODULE_AUTHOR("Dmitry Tarnyagin "); MODULE_DESCRIPTION("Softmac ST-Ericsson CW1200 common code"); @@ -220,6 +220,10 @@ static const struct ieee80211_ops cw1200_ops = { .get_stats = cw1200_get_stats, .ampdu_action = cw1200_ampdu_action, .flush = cw1200_flush, +#ifdef CONFIG_PM + .suspend = cw1200_wow_suspend, + .resume = cw1200_wow_resume, +#endif /* CONFIG_PM */ /* Intentionally not offloaded: */ /*.channel_switch = cw1200_channel_switch, */ /*.remain_on_channel = cw1200_remain_on_channel, */ @@ -412,16 +416,21 @@ void cw1200_unregister_common(struct ieee80211_hw *dev) priv->skb_cache = NULL; } + if (priv->sdd) { + release_firmware(priv->sdd); + priv->sdd = NULL; + } + for (i = 0; i < 4; ++i) cw1200_queue_deinit(&priv->tx_queue[i]); cw1200_queue_stats_deinit(&priv->tx_queue_stats); } EXPORT_SYMBOL_GPL(cw1200_unregister_common); -int cw1200_probe(const struct sbus_ops *sbus_ops, - struct sbus_priv *sbus, - struct device *pdev, - struct cw1200_common **pself) +int cw1200_core_probe(const struct sbus_ops *sbus_ops, + struct sbus_priv *sbus, + struct device *pdev, + struct cw1200_common **pself) { int err = -ENOMEM; struct ieee80211_hw *dev; @@ -489,12 +498,12 @@ err1: err: return err; } -EXPORT_SYMBOL_GPL(cw1200_probe); +EXPORT_SYMBOL_GPL(cw1200_core_probe); -void cw1200_release(struct cw1200_common *self) +void cw1200_core_release(struct cw1200_common *self) { cw1200_unregister_common(self->hw); cw1200_free_common(self->hw); return; } -EXPORT_SYMBOL_GPL(cw1200_release); +EXPORT_SYMBOL_GPL(cw1200_core_release); diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c new file mode 100644 index 00000000000..72f11e09d50 --- /dev/null +++ b/drivers/staging/cw1200/pm.c @@ -0,0 +1,142 @@ +/* + * Mac80211 power management API for ST-Ericsson CW1200 drivers + * + * Copyright (c) 2011, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#include "cw1200.h" +#include "pm.h" +#include "sta.h" +#include "bh.h" +#include "sbus.h" + +/* private */ +struct cw1200_suspend_state { + unsigned long bss_loss_tmo; + unsigned long connection_loss_tmo; + unsigned long join_tmo; +}; + +static long cw1200_suspend_work(struct delayed_work *work) +{ + int ret = cancel_delayed_work(work); + long tmo; + if (ret > 0) { + /* Timer is pending */ + tmo = work->timer.expires - jiffies; + if (tmo < 0) + tmo = 0; + } else { + tmo = -1; + } + return tmo; +} + +static int cw1200_resume_work(struct cw1200_common *priv, + struct delayed_work *work, + unsigned long tmo) +{ + if ((long)tmo < 0) + return 1; + + return queue_delayed_work(priv->workqueue, work, tmo); +} + +int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_suspend_state *state; + int ret; + + /* Ensure pending operations are done. */ + ret = wait_event_interruptible_timeout( + priv->channel_switch_done, + !priv->channel_switch_in_progress, 3 * HZ); + if (WARN_ON(!ret)) + return -ETIMEDOUT; + else if (WARN_ON(ret < 0)) + return ret; + + /* Flush and lock TX. */ + ret = __cw1200_flush(priv, false); + if (WARN_ON(ret < 0)) + return ret; + + /* Allocate state */ + state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL); + if (!state) { + wsm_unlock_tx(priv); + return -ENOMEM; + } + + /* Store delayed work states. */ + state->bss_loss_tmo = + cw1200_suspend_work(&priv->bss_loss_work); + state->connection_loss_tmo = + cw1200_suspend_work(&priv->connection_loss_work); + state->join_tmo = + cw1200_suspend_work(&priv->join_timeout); + + /* Flush workqueue */ + flush_workqueue(priv->workqueue); + + /* Stop serving thread */ + cw1200_bh_suspend(priv); + + /* Store suspend state */ + priv->suspend_state = state; + + /* Enable IRQ wake */ + ret = priv->sbus_ops->power_mgmt(priv->sbus_priv, true); + if (ret) { + wiphy_err(priv->hw->wiphy, + "%s: PM request failed: %d. WoW is disabled.\n", + __func__, ret); + cw1200_wow_resume(hw); + return -EBUSY; + } + + /* Force resume if event is coming from the device. */ + if (atomic_read(&priv->bh_rx)) { + cw1200_wow_resume(hw); + return -EAGAIN; + } + + return 0; +} + +int cw1200_wow_resume(struct ieee80211_hw *hw) +{ + struct cw1200_common *priv = hw->priv; + struct cw1200_suspend_state *state; + + state = priv->suspend_state; + priv->suspend_state = NULL; + + /* Disable IRQ wake */ + priv->sbus_ops->power_mgmt(priv->sbus_priv, false); + + /* Resume BH thread */ + cw1200_bh_resume(priv); + + /* Resume delayed work */ + cw1200_resume_work(priv, &priv->bss_loss_work, + state->bss_loss_tmo); + cw1200_resume_work(priv, &priv->connection_loss_work, + state->connection_loss_tmo); + cw1200_resume_work(priv, &priv->join_timeout, + state->join_tmo); + + /* Unlock datapath */ + wsm_unlock_tx(priv); + + /* Free memory */ + kfree(state); + + return 0; +} diff --git a/drivers/staging/cw1200/pm.h b/drivers/staging/cw1200/pm.h new file mode 100644 index 00000000000..841b609457f --- /dev/null +++ b/drivers/staging/cw1200/pm.h @@ -0,0 +1,23 @@ +/* + * Mac80211 power management interface for ST-Ericsson CW1200 mac80211 drivers + * + * Copyright (c) 2011, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#ifndef PM_H_INCLUDED +#define PM_H_INCLUDED + +/* ******************************************************************** */ +/* mac80211 API */ + +#ifdef CONFIG_PM +int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); +int cw1200_wow_resume(struct ieee80211_hw *hw); +#endif /* CONFIG_PM */ + +#endif diff --git a/drivers/staging/cw1200/sbus.h b/drivers/staging/cw1200/sbus.h index c31a2f30a22..a911ef1a0e6 100644 --- a/drivers/staging/cw1200/sbus.h +++ b/drivers/staging/cw1200/sbus.h @@ -32,6 +32,7 @@ struct sbus_ops { int (*irq_unsubscribe)(struct sbus_priv *self); int (*reset)(struct sbus_priv *self); size_t (*align_size)(struct sbus_priv *self, size_t size); + int (*power_mgmt)(struct sbus_priv *self, bool suspend); }; #endif /* CW1200_SBUS_H */ diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 50f447fb46b..fe2cd5cc982 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -27,7 +27,6 @@ static int cw1200_cancel_scan(struct cw1200_common *priv); -static int __cw1200_flush(struct cw1200_common *priv, bool drop); static inline void __cw1200_free_event_queue(struct list_head *list) { @@ -652,7 +651,7 @@ int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value) return ret; } -static int __cw1200_flush(struct cw1200_common *priv, bool drop) +int __cw1200_flush(struct cw1200_common *priv, bool drop) { int i, ret; @@ -859,43 +858,42 @@ int cw1200_setup_mac(struct cw1200_common *priv) if (wsm_get_station_id(priv, &prev_mac[0]) || memcmp(prev_mac, priv->mac_addr, ETH_ALEN)) { const char *sdd_path = NULL; - const struct firmware *firmware = NULL; struct wsm_configuration cfg = { .dot11StationId = &priv->mac_addr[0], }; - switch (priv->hw_revision) { - case CW1200_HW_REV_CUT10: - sdd_path = SDD_FILE_10; - break; - case CW1200_HW_REV_CUT11: - sdd_path = SDD_FILE_11; - break; - case CW1200_HW_REV_CUT20: - sdd_path = SDD_FILE_20; - break; - case CW1200_HW_REV_CUT22: - sdd_path = SDD_FILE_22; - break; - default: - BUG_ON(1); - } + if (!priv->sdd) { + switch (priv->hw_revision) { + case CW1200_HW_REV_CUT10: + sdd_path = SDD_FILE_10; + break; + case CW1200_HW_REV_CUT11: + sdd_path = SDD_FILE_11; + break; + case CW1200_HW_REV_CUT20: + sdd_path = SDD_FILE_20; + break; + case CW1200_HW_REV_CUT22: + sdd_path = SDD_FILE_22; + break; + default: + BUG_ON(1); + } - ret = request_firmware(&firmware, - sdd_path, priv->pdev); + ret = request_firmware(&priv->sdd, + sdd_path, priv->pdev); - if (unlikely(ret)) { - cw1200_dbg(CW1200_DBG_ERROR, - "%s: can't load sdd file %s.\n", - __func__, sdd_path); - return ret; + if (unlikely(ret)) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't load sdd file %s.\n", + __func__, sdd_path); + return ret; + } } - cfg.dpdData = firmware->data; - cfg.dpdData_size = firmware->size; + cfg.dpdData = priv->sdd->data; + cfg.dpdData_size = priv->sdd->size; ret = WARN_ON(wsm_configuration(priv, &cfg)); - - release_firmware(firmware); } if (ret) return ret; diff --git a/drivers/staging/cw1200/sta.h b/drivers/staging/cw1200/sta.h index 8951a4a176b..8d0cc5d4c12 100644 --- a/drivers/staging/cw1200/sta.h +++ b/drivers/staging/cw1200/sta.h @@ -72,5 +72,6 @@ void cw1200_unjoin_work(struct work_struct *work); void cw1200_wep_key_work(struct work_struct *work); void cw1200_update_listening(struct cw1200_common *priv, bool enabled); void cw1200_update_filtering(struct cw1200_common *priv); +int __cw1200_flush(struct cw1200_common *priv, bool drop); #endif diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index e8662fa6054..46280046bae 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1060,7 +1060,7 @@ void wsm_lock_tx(struct cw1200_common *priv) { wsm_cmd_lock(priv); if (atomic_add_return(1, &priv->tx_lock) == 1) { - WARN_ON(wait_event_interruptible_timeout(priv->hw_bufs_used_wq, + WARN_ON(wait_event_interruptible_timeout(priv->bh_evt_wq, !priv->hw_bufs_used, WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); wsm_printk(KERN_DEBUG "[WSM] TX is locked.\n"); } @@ -1076,7 +1076,7 @@ void wsm_lock_tx_async(struct cw1200_common *priv) void wsm_flush_tx(struct cw1200_common *priv) { BUG_ON(!atomic_read(&priv->tx_lock)); - WARN_ON(wait_event_interruptible_timeout(priv->hw_bufs_used_wq, + WARN_ON(wait_event_interruptible_timeout(priv->bh_evt_wq, !priv->hw_bufs_used, WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); } -- cgit v1.2.3 From d8b7673c436cfaac1bfa729adce2d1a8d6a09e75 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Sun, 17 Jul 2011 02:11:27 +0200 Subject: cw1200: Fix for firmware exception on requeued TXed frames. TX sequence number was not cleared properly in requeued frames. Firmware checked it and triggered exception. Fix clears TX sequence part of id field before applying (or-ing) a new sequence number. Also handling of TX confirmation frames with "requeue" status was slightly changed with respect to PS flow control (suspend/resume link). Note that multicast TX-ing in SoftAP mode still be completly broken, to be fixed later. Signed-off-by: Dmitry Tarnyagin Change-Id: I4eacfa3e6cf8dc6e63e161489977228d8016c8d8 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27305 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33485 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 12 ++++++------ drivers/staging/cw1200/bh.c | 9 ++++++--- drivers/staging/cw1200/txrx.c | 11 ++++++++++- drivers/staging/cw1200/wsm.c | 30 ++++++++++++++++++------------ drivers/staging/cw1200/wsm.h | 9 +++++++++ 5 files changed, 49 insertions(+), 22 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index b9e91fb07b7..edcd4af885a 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -398,7 +398,6 @@ void cw1200_multicast_start_work(struct work_struct *work) container_of(work, struct cw1200_common, multicast_start_work); (void)cw1200_set_tim_impl(priv, true); - wsm_lock_tx(priv); priv->suspend_multicast = false; wsm_unlock_tx(priv); cw1200_bh_wakeup(priv); @@ -409,9 +408,8 @@ void cw1200_multicast_stop_work(struct work_struct *work) struct cw1200_common *priv = container_of(work, struct cw1200_common, multicast_stop_work); - /* Lock flushes send queue in device. Just to make sure DTIM beacom - * and frames are sent. */ - wsm_lock_tx(priv); + /* Flush to make sure DTIM beacon and frames are sent. */ + wsm_flush_tx(priv); priv->suspend_multicast = true; (void)cw1200_set_tim_impl(priv, false); wsm_unlock_tx(priv); @@ -452,15 +450,17 @@ void cw1200_suspend_resume(struct cw1200_common *priv, arg->multicast ? "broadcast" : "unicast"); if (arg->multicast) { - if (arg->stop) + if (arg->stop) { + wsm_lock_tx_async(priv); queue_work(priv->workqueue, &priv->multicast_stop_work); - else { + } else { /* Handle only if there is data to be sent */ for (i = 0; i < 4; ++i) { if (cw1200_queue_get_num_queued( &priv->tx_queue[i], after_dtim)) { + wsm_lock_tx_async(priv); queue_work(priv->workqueue, &priv->multicast_start_work); break; diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index 2d438a96e38..cddfd9dfaa4 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -476,8 +476,10 @@ tx: } #endif /* CONFIG_CW1200_NON_POWER_OF_TWO_BLOCKSIZES */ - wsm->id |= __cpu_to_le32( - priv->wsm_tx_seq << 13); + wsm->id &= __cpu_to_le32( + ~WSM_TX_SEQ(WSM_TX_SEQ_MAX)); + wsm->id |= cpu_to_le32( + WSM_TX_SEQ(priv->wsm_tx_seq)); if (WARN_ON(cw1200_data_write(priv, data, tx_len))) { @@ -491,7 +493,8 @@ tx: #endif /* CONFIG_CW1200_WSM_DUMPS */ wsm_txed(priv, data); - priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & 7; + priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & + WSM_TX_SEQ_MAX; } } diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 5db1282b188..7616c237a88 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -14,6 +14,7 @@ #include "cw1200.h" #include "wsm.h" #include "bh.h" +#include "ap.h" #include "debug.h" #if defined(CONFIG_CW1200_TX_POLICY_DEBUG) @@ -575,8 +576,16 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, if ((arg->status == WSM_REQUEUE) && (arg->flags & WSM_TX_STATUS_REQUEUE)) { + /* "Requeue" means "implicit suspend" */ + struct wsm_suspend_resume suspend = { + .link_id = arg->link_id, + .stop = 1, + .multicast = !arg->link_id, + }; + cw1200_suspend_resume(priv, &suspend); WARN_ON(cw1200_queue_requeue(queue, arg->packetID)); - } else if (!WARN_ON(cw1200_queue_get_skb(queue, arg->packetID, &skb))) { + } else if (!WARN_ON(cw1200_queue_get_skb( + queue, arg->packetID, &skb))) { struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb); struct wsm_tx *wsm_tx = (struct wsm_tx *)skb->data; int rate_id = (wsm_tx->flags >> 4) & 0x07; diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 46280046bae..604fa067ccc 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -179,7 +179,7 @@ int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg) { int ret; struct wsm_buf *buf = &priv->wsm_cmd_buf; - u16 cmd = 0x000A | ((arg->link_id & 0x0F) << 6); + u16 cmd = 0x000A | WSM_TX_LINK_ID(arg->link_id); wsm_cmd_lock(priv); @@ -361,7 +361,9 @@ int wsm_stop_scan(struct cw1200_common *priv) } -static int wsm_tx_confirm(struct cw1200_common *priv, struct wsm_buf *buf) +static int wsm_tx_confirm(struct cw1200_common *priv, + struct wsm_buf *buf, + int link_id) { struct wsm_tx_confirm tx_confirm; @@ -372,6 +374,7 @@ static int wsm_tx_confirm(struct cw1200_common *priv, struct wsm_buf *buf) tx_confirm.flags = WSM_GET16(buf); tx_confirm.mediaDelay = WSM_GET32(buf); tx_confirm.txQueueDelay = WSM_GET32(buf); + tx_confirm.link_id = link_id; if (priv->wsm_cbc.tx_confirm) priv->wsm_cbc.tx_confirm(priv, &tx_confirm); @@ -383,7 +386,7 @@ underflow: } static int wsm_multi_tx_confirm(struct cw1200_common *priv, - struct wsm_buf *buf) + struct wsm_buf *buf, int link_id) { int ret; int count; @@ -402,7 +405,7 @@ static int wsm_multi_tx_confirm(struct cw1200_common *priv, cw1200_debug_txed_multi(priv, count); for (i = 0; i < count; ++i) { - ret = wsm_tx_confirm(priv, buf); + ret = wsm_tx_confirm(priv, buf, link_id); if (ret) return ret; } @@ -754,7 +757,7 @@ int wsm_map_link(struct cw1200_common *priv, const struct wsm_map_link *arg) { int ret; struct wsm_buf *buf = &priv->wsm_cmd_buf; - u16 cmd = 0x001C | ((arg->link_id & 0x0F) << 6); + u16 cmd = 0x001C | WSM_TX_LINK_ID(arg->link_id); wsm_cmd_lock(priv); @@ -1109,7 +1112,7 @@ int wsm_handle_rx(struct cw1200_common *priv, int id, int link_id = (id >> 6) & 0x0F; /* Strip link id. */ - id &= ~(0x0F << 6); + id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); wsm_buf.begin = (u8 *)&wsm[0]; wsm_buf.data = (u8 *)&wsm[1]; @@ -1119,9 +1122,9 @@ int wsm_handle_rx(struct cw1200_common *priv, int id, wsm_buf.end - wsm_buf.begin); if (id == 0x404) { - ret = wsm_tx_confirm(priv, &wsm_buf); + ret = wsm_tx_confirm(priv, &wsm_buf, link_id); } else if (id == 0x41E) { - ret = wsm_multi_tx_confirm(priv, &wsm_buf); + ret = wsm_multi_tx_confirm(priv, &wsm_buf, link_id); } else if (id & 0x0400) { void *wsm_arg; u16 wsm_cmd; @@ -1130,7 +1133,8 @@ int wsm_handle_rx(struct cw1200_common *priv, int id, * response and race condition removal (see above). */ spin_lock(&priv->wsm_cmd.lock); wsm_arg = priv->wsm_cmd.arg; - wsm_cmd = priv->wsm_cmd.cmd & ~(0x0F << 6); + wsm_cmd = priv->wsm_cmd.cmd & + ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); priv->wsm_cmd.cmd = 0xFFFF; spin_unlock(&priv->wsm_cmd.lock); @@ -1501,9 +1505,10 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, /* Update link id */ sta_priv = (struct cw1200_sta_priv *) &tx_info->control.sta->drv_priv; - wsm->hdr.id &= __cpu_to_le16(~(0x0F << 6)); - wsm->hdr.id |= - cpu_to_le16(sta_priv->link_id << 6); + wsm->hdr.id &= __cpu_to_le16( + ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX)); + wsm->hdr.id |= cpu_to_le16( + WSM_TX_LINK_ID(sta_priv->link_id)); } *data = (u8 *)wsm; @@ -1519,6 +1524,7 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, } else if (priv->mode == NL80211_IFTYPE_AP && !priv->suspend_multicast) { priv->suspend_multicast = true; + wsm_lock_tx_async(priv); queue_work(priv->workqueue, &priv->multicast_stop_work); } diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 9b33a6e2b83..76b9253fa94 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -540,6 +540,13 @@ struct wsm_hdr { __le16 id; }; +#define WSM_TX_SEQ_MAX (7) +#define WSM_TX_SEQ(seq) \ + ((seq & WSM_TX_SEQ_MAX) << 13) +#define WSM_TX_LINK_ID_MAX (0x0F) +#define WSM_TX_LINK_ID(link_id) \ + ((link_id & WSM_TX_LINK_ID_MAX) << 6) + /* ******************************************************************** */ /* WSM capcbility */ @@ -701,6 +708,8 @@ struct wsm_tx_confirm { /* The total time in microseconds that the frame spent in */ /* the WLAN device before transmission was started. */ /* [out] */ u32 txQueueDelay; + + /* [out]*/ u32 link_id; }; /* 3.15 */ -- cgit v1.2.3 From e996d63309e8452eb2ed1c7eeafd23c62e434935 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Mon, 18 Jul 2011 10:07:02 +0200 Subject: cw1200: ARP ipv4 filtering Implementation of ARP ipv4 filtering. Currently firmware support only one IP address. In case of multiple IP addresses (aliases) ARP filtering will be disabled. Change-Id: I787c756fdc131523f68717b86a9d24daf185dd44 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27319 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33486 Tested-by: Dmitry TARNYAGIN Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 32 +++++++++++++++++++++++++++++++- drivers/staging/cw1200/wsm.h | 9 +++++---- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index edcd4af885a..908725ab5fe 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -163,7 +163,37 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, } /* TODO: BSS_CHANGED_IBSS */ - /* TODO: BSS_CHANGED_ARP_FILTER */ + + if (changed & BSS_CHANGED_ARP_FILTER) { + struct wsm_arp_ipv4_filter filter = {0}; + int i; + + ap_printk(KERN_DEBUG "[STA] BSS_CHANGED_ARP_FILTER " + "enabled: %d, cnt: %d\n", + info->arp_filter_enabled, + info->arp_addr_cnt); + + if (info->arp_filter_enabled) + filter.enable = __cpu_to_le32(1); + + /* Currently only one IP address is supported by firmware. + * In case of more IPs arp filtering will be disabled. */ + if (info->arp_addr_cnt > 0 && + info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) { + for(i=0; iarp_addr_cnt;i++) { + filter.ipv4Address[i] = info->arp_addr_list[i]; + ap_printk(KERN_DEBUG "[STA] addr[%d]: 0x%X\n", + i, filter.ipv4Address[i]); + } + } else + filter.enable = 0; + + ap_printk(KERN_DEBUG "[STA] arp ip filter enable: %d\n", + __le32_to_cpu(filter.enable)); + + if (wsm_set_arp_ipv4_filter(priv, &filter)) + WARN_ON(1); + } if (changed & BSS_CHANGED_BEACON_ENABLED) priv->enable_beacon = info->enable_beacon; diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 76b9253fa94..97989a01f1c 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -374,6 +374,7 @@ struct cw1200_common; /* 4.10 ArpIpAddressesTable */ #define WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE 0x1001 +#define WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES 1 /* 4.11 TemplateFrame */ #define WSM_MIB_ID_TEMPLATE_FRAME 0x1002 @@ -1471,13 +1472,13 @@ static inline int wsm_set_multicast_filter(struct cw1200_common *priv, } /* IPv4 filtering - 4.10 */ -struct wsm_ipv4_filter { +struct wsm_arp_ipv4_filter { __le32 enable; - u8 ipv4Address[4]; + __be32 ipv4Address[WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES]; } __packed; -static inline int wsm_set_ipv4_arp_filter(struct cw1200_common *priv, - struct wsm_ipv4_filter *fp) +static inline int wsm_set_arp_ipv4_filter(struct cw1200_common *priv, + struct wsm_arp_ipv4_filter *fp) { return wsm_write_mib(priv, WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE, fp, sizeof(*fp)); -- cgit v1.2.3 From 9bbc98e85c28227cc5780990084144deb1f246b6 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Fri, 22 Jul 2011 13:16:18 +0200 Subject: cw1200: Cancel connection and beacon loss works Canceling connection and beacon loss works after configuring new values for beacon and tx failure threshold. In case of re-association it will prevent extra connection loss event successfull roaming. Change-Id: I4707386ba9ea3791269655a38f9f0422e6568d2f Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27641 Reviewed-by: Dmitry TARNYAGIN Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33487 Tested-by: Dmitry TARNYAGIN Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 2 ++ 1 file changed, 2 insertions(+) mode change 100644 => 100755 drivers/staging/cw1200/ap.c diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c old mode 100644 new mode 100755 index 908725ab5fe..c6e5b78ab66 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -280,6 +280,8 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, priv->cqm_tx_failure_thold = info->cqm_tx_fail_thold; priv->cqm_tx_failure_count = 0; + cancel_delayed_work_sync(&priv->bss_loss_work); + cancel_delayed_work_sync(&priv->connection_loss_work); #endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ priv->bss_params.beaconLostCount = -- cgit v1.2.3 From cd0a297fda5d53c14498719da32df5e359356a05 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Sun, 15 May 2011 14:19:19 +0200 Subject: cw1200: Fix for buffered multicasts in AP-PS mode. Refactoring of multicast handling: * When multicast is coming, driver sets aid0 bit in TIM IE and puts multicas in the queue with "AFTER_DTIM" link id. * When WSM indicates DTIM beacon, driver verifies if aid0 indicating beacon was sent and sends buffered multicasts. * Driver clears aid0 bit in the TIM IE when no more multicasts are available. + A stilistic change: 1 << shift is replaced by BIT(shift) Change-Id: I15b134bf847913a907f00293ae99d7a71bbc7343 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27306 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33488 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 70 ++++++++++++++++++++---------------- drivers/staging/cw1200/cw1200.h | 4 +++ drivers/staging/cw1200/cw1200_sdio.c | 4 +-- drivers/staging/cw1200/main.c | 1 + drivers/staging/cw1200/sta.c | 4 ++- drivers/staging/cw1200/txrx.c | 30 ++++++++++++---- drivers/staging/cw1200/wsm.c | 41 +++++++++++++++------ drivers/staging/cw1200/wsm.h | 2 +- 8 files changed, 105 insertions(+), 51 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index c6e5b78ab66..fc23ff74100 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -51,7 +51,7 @@ int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, memcpy(map_link.mac_addr, sta->addr, ETH_ALEN); if (!WARN_ON(wsm_map_link(priv, &map_link))) { sta_priv->link_id = map_link.link_id; - priv->link_id_map |= 1 << map_link.link_id; + priv->link_id_map |= BIT(map_link.link_id); ap_printk(KERN_DEBUG "[AP] STA added, link_id: %d\n", map_link.link_id); } @@ -73,7 +73,7 @@ int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ap_printk(KERN_DEBUG "[AP] STA removed, link_id: %d\n", sta_priv->link_id); reset.link_id = sta_priv->link_id; - priv->link_id_map &= ~(1 << sta_priv->link_id); + priv->link_id_map &= ~BIT(sta_priv->link_id); sta_priv->link_id = 0; WARN_ON(wsm_reset(priv, &reset)); } @@ -87,7 +87,7 @@ void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, struct cw1200_common *priv = dev->priv; struct cw1200_sta_priv *sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; - u32 bit = 1 << sta_priv->link_id; + u32 bit = BIT(sta_priv->link_id); switch (notify_cmd) { case STA_NOTIFY_SLEEP: @@ -100,7 +100,7 @@ void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, } } -static int cw1200_set_tim_impl(struct cw1200_common *priv, bool multicast) +static int cw1200_set_tim_impl(struct cw1200_common *priv) { struct wsm_template_frame frame = { .frame_type = WSM_FRAME_TYPE_BEACON, @@ -120,7 +120,7 @@ static int cw1200_set_tim_impl(struct cw1200_common *priv, bool multicast) frame.skb->data[tim_offset + 2] = 0; /* Set/reset aid0 bit */ - if (multicast) + if (priv->aid0_bit_set) frame.skb->data[tim_offset + 4] |= 1; else frame.skb->data[tim_offset + 4] &= ~1; @@ -137,7 +137,7 @@ void cw1200_set_tim_work(struct work_struct *work) { struct cw1200_common *priv = container_of(work, struct cw1200_common, set_tim_work); - (void)cw1200_set_tim_impl(priv, !priv->suspend_multicast); + (void)cw1200_set_tim_impl(priv); } int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, @@ -429,10 +429,16 @@ void cw1200_multicast_start_work(struct work_struct *work) struct cw1200_common *priv = container_of(work, struct cw1200_common, multicast_start_work); - (void)cw1200_set_tim_impl(priv, true); - priv->suspend_multicast = false; - wsm_unlock_tx(priv); - cw1200_bh_wakeup(priv); + bool store_timestamp = !priv->aid0_bit_set; + + priv->aid0_bit_set = true; + cw1200_set_tim_impl(priv); + if (store_timestamp) { + unsigned long now = jiffies; + if (unlikely(!now)) + ++now; + priv->aid0_bit_timestamp = now; + } } void cw1200_multicast_stop_work(struct work_struct *work) @@ -440,11 +446,12 @@ void cw1200_multicast_stop_work(struct work_struct *work) struct cw1200_common *priv = container_of(work, struct cw1200_common, multicast_stop_work); - /* Flush to make sure DTIM beacon and frames are sent. */ - wsm_flush_tx(priv); - priv->suspend_multicast = true; - (void)cw1200_set_tim_impl(priv, false); + /* Flush to make sure frames are sent. */ + wsm_lock_tx(priv); wsm_unlock_tx(priv); + + priv->aid0_bit_set = false; + cw1200_set_tim_impl(priv); } int cw1200_ampdu_action(struct ieee80211_hw *hw, @@ -465,9 +472,8 @@ int cw1200_ampdu_action(struct ieee80211_hw *hw, void cw1200_suspend_resume(struct cw1200_common *priv, struct wsm_suspend_resume *arg) { - int queue = 1 << wsm_queue_id_to_linux(arg->queue); - u32 unicast = 1 << arg->link_id; - u32 after_dtim = 1 << CW1200_LINK_ID_AFTER_DTIM; + int queue = BIT(wsm_queue_id_to_linux(arg->queue)); + u32 unicast = BIT(arg->link_id); u32 wakeup_required = 0; u32 set = 0; u32 clear; @@ -475,7 +481,7 @@ void cw1200_suspend_resume(struct cw1200_common *priv, int i; if (!arg->link_id) /* For all links */ - unicast = (1 << (CW1200_MAX_STA_IN_AP_MODE + 1)) - 2; + unicast = BIT(CW1200_MAX_STA_IN_AP_MODE + 1) - 2; ap_printk(KERN_DEBUG "[AP] %s: %s\n", arg->stop ? "stop" : "start", @@ -483,20 +489,24 @@ void cw1200_suspend_resume(struct cw1200_common *priv, if (arg->multicast) { if (arg->stop) { - wsm_lock_tx_async(priv); queue_work(priv->workqueue, &priv->multicast_stop_work); } else { - /* Handle only if there is data to be sent */ - for (i = 0; i < 4; ++i) { - if (cw1200_queue_get_num_queued( - &priv->tx_queue[i], - after_dtim)) { - wsm_lock_tx_async(priv); - queue_work(priv->workqueue, - &priv->multicast_start_work); - break; - } + /* Handle only if there is data to be sent + * and aid0 is set in the beacon. */ + unsigned long timestamp = + priv->aid0_bit_timestamp; + /* 20ms grace interval is used to make sure + * the beacon was actually sent by hardware. */ + static const unsigned long grace_interval = + HZ * 20 / 1000; + + if (timestamp && + jiffies - timestamp > grace_interval) { + + priv->aid0_bit_timestamp = 0; + priv->suspend_multicast = false; + cw1200_bh_wakeup(priv); } } } else { @@ -511,7 +521,7 @@ void cw1200_suspend_resume(struct cw1200_common *priv, queue = 0x0F; for (i = 0; i < 4; ++i) { - if (!(queue & (1 << i))) + if (!(queue & BIT(i))) continue; tx_suspend_mask = priv->tx_suspend_mask[i]; diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 68d82874765..9ed86cd39a1 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -167,6 +167,10 @@ struct cw1200_common { u32 tx_suspend_mask[4]; u32 sta_asleep_mask; bool suspend_multicast; + bool aid0_bit_set; + unsigned long aid0_bit_timestamp; + spinlock_t buffered_multicasts_lock; + bool buffered_multicasts; struct work_struct set_tim_work; struct work_struct multicast_start_work; struct work_struct multicast_stop_work; diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index cf5c5c185be..279dff144ae 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -121,10 +121,10 @@ static int cw1200_request_irq(struct sbus_priv *self, goto set_func; /* Master interrupt enable ... */ - cccr |= 1; + cccr |= BIT(0); /* ... for our function */ - cccr |= 1 << func_num; + cccr |= BIT(func_num); sdio_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret); if (WARN_ON(ret)) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 8a621ff2b06..ded4fe9927c 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -319,6 +319,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) INIT_DELAYED_WORK(&priv->connection_loss_work, cw1200_connection_loss_work); INIT_WORK(&priv->tx_failure_work, cw1200_tx_failure_work); + spin_lock_init(&priv->buffered_multicasts_lock); INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work); INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work); INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work); diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index fe2cd5cc982..9cb801acefb 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1076,10 +1076,12 @@ static inline int cw1200_enable_listening(struct cw1200_common *priv) static inline int cw1200_disable_listening(struct cw1200_common *priv) { + int ret; struct wsm_reset reset = { .reset_statistics = true, }; - return wsm_reset(priv, &reset); + ret = wsm_reset(priv, &reset); + return ret; } void cw1200_update_listening(struct cw1200_common *priv, bool enabled) diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 7616c237a88..5439947d742 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -350,8 +350,8 @@ u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates) u32 ret = 0; int i; for (i = 0; i < 32; ++i) { - if (rates & (1 << i)) - ret |= 1 << priv->rates[i].hw_value; + if (rates & BIT(i)) + ret |= BIT(priv->rates[i].hw_value); } return ret; } @@ -422,10 +422,12 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) (struct ieee80211_hdr *)skb->data; struct cw1200_sta_priv *sta_priv = (struct cw1200_sta_priv *)&tx_info->control.sta->drv_priv; + bool obtain_lock; int link_id = 0; int ret; - if (tx_info->flags | IEEE80211_TX_CTL_SEND_AFTER_DTIM) + if ((tx_info->flags | IEEE80211_TX_CTL_SEND_AFTER_DTIM) && + (priv->mode == NL80211_IFTYPE_AP)) link_id = CW1200_LINK_ID_AFTER_DTIM; else if (tx_info->control.sta) link_id = sta_priv->link_id; @@ -510,8 +512,22 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) if (cw1200_handle_action_tx(priv, skb)) goto drop; + obtain_lock = (link_id == CW1200_LINK_ID_AFTER_DTIM); + + if (obtain_lock) + spin_lock_bh(&priv->buffered_multicasts_lock); + + if (link_id == CW1200_LINK_ID_AFTER_DTIM && + !priv->buffered_multicasts) { + priv->buffered_multicasts = true; + queue_work(priv->workqueue, + &priv->multicast_start_work); + } ret = cw1200_queue_put(&priv->tx_queue[queue], priv, skb, link_id); + if (obtain_lock) + spin_unlock_bh(&priv->buffered_multicasts_lock); + if (!WARN_ON(ret)) cw1200_bh_wakeup(priv); else @@ -773,16 +789,16 @@ int cw1200_alloc_key(struct cw1200_common *priv) if (idx < 0 || idx > WSM_KEY_MAX_INDEX) return -1; - priv->key_map |= 1 << idx; + priv->key_map |= BIT(idx); priv->keys[idx].entryIndex = idx; return idx; } void cw1200_free_key(struct cw1200_common *priv, int idx) { - BUG_ON(!(priv->key_map & (1 << idx))); + BUG_ON(!(priv->key_map & BIT(idx))); memset(&priv->keys[idx], 0, sizeof(priv->keys[idx])); - priv->key_map &= ~(1 << idx); + priv->key_map &= ~BIT(idx); } void cw1200_free_keys(struct cw1200_common *priv) @@ -795,7 +811,7 @@ int cw1200_upload_keys(struct cw1200_common *priv) { int idx, ret = 0; for (idx = 0; idx <= WSM_KEY_MAX_INDEX; ++idx) - if (priv->key_map & (1 << idx)) { + if (priv->key_map & BIT(idx)) { ret = wsm_add_key(priv, &priv->keys[idx]); if (ret < 0) break; diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 604fa067ccc..6f40934ed8f 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1423,7 +1423,7 @@ static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv, /* Search for a queue with multicast frames buffered */ if (priv->sta_asleep_mask && !priv->suspend_multicast) { - tx_allowed_mask = 1 << CW1200_LINK_ID_AFTER_DTIM; + tx_allowed_mask = BIT(CW1200_LINK_ID_AFTER_DTIM); for (i = 0; i < 4; ++i) { mcasts += cw1200_queue_get_num_queued( &priv->tx_queue[i], tx_allowed_mask); @@ -1443,7 +1443,7 @@ static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv, if (priv->sta_asleep_mask) { tx_allowed_mask |= ~priv->tx_suspend_mask[i]; } else { - tx_allowed_mask |= 1 << CW1200_LINK_ID_AFTER_DTIM; + tx_allowed_mask |= BIT(CW1200_LINK_ID_AFTER_DTIM); } if (cw1200_queue_get_num_queued( queue, tx_allowed_mask)) @@ -1486,13 +1486,39 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, spin_unlock(&priv->wsm_cmd.lock); } else { for (;;) { + bool obtain_lock = priv->sta_asleep_mask && + !priv->suspend_multicast; + int ret; + if (atomic_add_return(0, &priv->tx_lock)) break; - if (wsm_get_tx_queue_and_mask(priv, &queue, - &tx_allowed_mask, &more)) + if (obtain_lock) + spin_lock_bh( + &priv->buffered_multicasts_lock); + + ret = wsm_get_tx_queue_and_mask(priv, &queue, + &tx_allowed_mask, &more); + + if (priv->buffered_multicasts && + priv->sta_asleep_mask && + !priv->suspend_multicast && + (ret || tx_allowed_mask != + BIT(CW1200_LINK_ID_AFTER_DTIM))) { + priv->buffered_multicasts = false; + priv->suspend_multicast = true; + queue_work(priv->workqueue, + &priv->multicast_stop_work); + } + + if (obtain_lock) + spin_unlock_bh( + &priv->buffered_multicasts_lock); + + if (ret) break; + if (cw1200_queue_get(queue, tx_allowed_mask, &wsm, &tx_info)) @@ -1513,6 +1539,7 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, *data = (u8 *)wsm; *tx_len = __le16_to_cpu(wsm->hdr.len); + if (more) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) &wsm[1]; @@ -1521,12 +1548,6 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, * to inform PS STAs */ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); - } else if (priv->mode == NL80211_IFTYPE_AP && - !priv->suspend_multicast) { - priv->suspend_multicast = true; - wsm_lock_tx_async(priv); - queue_work(priv->workqueue, - &priv->multicast_stop_work); } wsm_printk(KERN_DEBUG "[WSM] >>> 0x%.4X (%d) %p %c\n", diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 97989a01f1c..8a861f918ad 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -1292,7 +1292,7 @@ static inline int wsm_set_template_frame(struct cw1200_common *priv, u8 *p = skb_push(arg->skb, 4); p[0] = arg->frame_type; p[1] = arg->rate; - ((u16 *) p)[1] = __cpu_to_le32(arg->skb->len - 4); + ((u16 *) p)[1] = __cpu_to_le16(arg->skb->len - 4); ret = wsm_write_mib(priv, WSM_MIB_ID_TEMPLATE_FRAME, p, arg->skb->len); skb_pull(arg->skb, 4); return ret; -- cgit v1.2.3 From 42a4af5798ae72b9b3e65598ea50599ff9a026b6 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Mon, 25 Jul 2011 15:19:51 +0200 Subject: cw1200: Off-channel sending of action frames is implemented. Action frames sending is required for P2P group negotiation. Due to firmware limitation TXing is possible only when device is "joined" (logically linked) to the AP. Join with "force" flag is used for sending action frames. Change-Id: Ic52b181f4b22d527dda27d0185b1ecc4ba488efe Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27775 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33489 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/queue.c | 8 +++--- drivers/staging/cw1200/sta.c | 56 ++++++++++++++++++++++++++++-------------- drivers/staging/cw1200/wsm.c | 11 ++++++--- drivers/staging/cw1200/wsm.h | 11 ++++++++- 4 files changed, 59 insertions(+), 27 deletions(-) diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c index f81e46cd4f4..4140981eed1 100644 --- a/drivers/staging/cw1200/queue.c +++ b/drivers/staging/cw1200/queue.c @@ -319,7 +319,7 @@ int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID) item->packetID = cw1200_queue_make_packet_id( queue_generation, queue_id, item_generation, item_id); wsm->packetID = __cpu_to_le32(item->packetID); - list_move_tail(&item->head, &queue->queue); + list_move(&item->head, &queue->queue); } spin_unlock_bh(&queue->lock); return ret; @@ -330,8 +330,8 @@ int cw1200_queue_requeue_all(struct cw1200_queue *queue) struct cw1200_queue_stats *stats = queue->stats; spin_lock_bh(&queue->lock); while (!list_empty(&queue->pending)) { - struct cw1200_queue_item *item = list_first_entry( - &queue->pending, struct cw1200_queue_item, head); + struct cw1200_queue_item *item = list_entry( + queue->pending.prev, struct cw1200_queue_item, head); struct wsm_tx *wsm = (struct wsm_tx *)item->skb->data; --queue->num_pending; @@ -347,7 +347,7 @@ int cw1200_queue_requeue_all(struct cw1200_queue *queue) queue->generation, queue->queue_id, item->generation, item - queue->pool); wsm->packetID = __cpu_to_le32(item->packetID); - list_move_tail(&item->head, &queue->queue); + list_move(&item->head, &queue->queue); } spin_unlock_bh(&queue->lock); diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 9cb801acefb..4923e9dd22f 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -922,31 +922,45 @@ void cw1200_join_work(struct work_struct *work) struct cw1200_common *priv = container_of(work, struct cw1200_common, join_work); const struct wsm_tx *wsm = priv->join_pending_frame; - const u8 *frame = (u8 *)&wsm[1]; - const u8 *bssid = &frame[4]; /* AP SSID in a 802.11 frame */ + const struct ieee80211_hdr *frame = (struct ieee80211_hdr *)&wsm[1]; + const u8 *bssid = &frame->addr1[0]; /* AP SSID in a 802.11 frame */ struct cfg80211_bss *bss; - const u8 *ssidie; - const u8 *dtimie; + const u8 *ssidie = NULL; + const u8 *dtimie = NULL; const struct ieee80211_tim_ie *tim = NULL; - u8 queueId = wsm_queue_id_to_linux(wsm->queueId); + u8 queueId; + bool action; + + BUG_ON(!wsm); + BUG_ON(!priv->channel); + + queueId = wsm_queue_id_to_linux(wsm->queueId); + action = ieee80211_is_action(frame->frame_control); + + if (unlikely(priv->join_status == CW1200_JOIN_STATUS_STA)) { + wsm_lock_tx(priv); + cw1200_unjoin_work(&priv->unjoin_work); + } cancel_delayed_work_sync(&priv->join_timeout); bss = cfg80211_get_bss(priv->hw->wiphy, NULL, bssid, NULL, 0, 0, 0); - if (!bss) { + if (!bss && !action) { priv->join_pending_frame = NULL; cw1200_queue_remove(&priv->tx_queue[queueId], priv, __le32_to_cpu(wsm->packetID)); + wsm_unlock_tx(priv); return; + } else if (bss) { + ssidie = cfg80211_find_ie(WLAN_EID_SSID, + bss->information_elements, + bss->len_information_elements); + dtimie = cfg80211_find_ie(WLAN_EID_TIM, + bss->information_elements, + bss->len_information_elements); + if (dtimie) + tim = (struct ieee80211_tim_ie *)&dtimie[2]; } - ssidie = cfg80211_find_ie(WLAN_EID_SSID, - bss->information_elements, - bss->len_information_elements); - dtimie = cfg80211_find_ie(WLAN_EID_TIM, - bss->information_elements, - bss->len_information_elements); - if (dtimie) - tim = (struct ieee80211_tim_ie *)&dtimie[2]; mutex_lock(&priv->conf_mutex); { @@ -969,9 +983,16 @@ void cw1200_join_work(struct work_struct *work) join.dtimPeriod); } + if (action) { + join.probeForJoin = 0; + join.flags |= WSM_JOIN_FLAGS_UNSYNCRONIZED | + WSM_JOIN_FLAGS_FORCE; + } else { + WARN_ON(wsm_set_block_ack_policy(priv, + priv->ba_tid_mask, priv->ba_tid_mask)); + } + priv->join_pending_frame = NULL; - BUG_ON(!wsm); - BUG_ON(!priv->channel); join.channelNumber = priv->channel->hw_value; join.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? @@ -989,9 +1010,6 @@ void cw1200_join_work(struct work_struct *work) wsm_flush_tx(priv); - WARN_ON(wsm_set_block_ack_policy(priv, - priv->ba_tid_mask, priv->ba_tid_mask)); - /* Queue unjoin if not associated in 3 sec. */ queue_delayed_work(priv->workqueue, &priv->join_timeout, 3 * HZ); diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 6f40934ed8f..4db5129394e 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1260,7 +1260,14 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, sizeof(priv->join_bssid)))) { if (ieee80211_is_auth(fctl)) action = doJoin; - else + else if (ieee80211_is_probe_req(fctl)) + action = doTx; + else if (ieee80211_is_action(fctl)) { + if (priv->join_status) + action = doTx; + else + action = doJoin; + } else action = doDrop; } break; @@ -1344,8 +1351,6 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, * in FW: it can't do RX/TX before "join". * "Join" here is not an association, * but just a syncronization between AP and STA. - * BTW that means device can't receive frames - * in monitor mode. * priv->join_status is used only in bh thread and does * not require protection */ wsm_printk(KERN_DEBUG "[WSM] Issue join command.\n"); diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 8a861f918ad..55792c4b3c8 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -284,7 +284,16 @@ struct cw1200_common; /* Join flags */ /* Unsynchronized */ -#define WSM_JOIN_FLAGS_UNSYNCRONIZED (1) +#define WSM_JOIN_FLAGS_UNSYNCRONIZED BIT(0) +/* The BSS owner is a P2P GO */ +#define WSM_JOIN_FLAGS_P2P_GO BIT(1) +/* Force to join BSS with the BSSID and the + * SSID specified without waiting for beacons. The + * ProbeForJoin parameter is ignored. */ +#define WSM_JOIN_FLAGS_FORCE BIT(2) +/* Give probe request/response higher + * priority over the BT traffic */ +#define WSM_JOIN_FLAGS_PRIO BIT(3) /* Key types */ #define WSM_KEY_TYPE_WEP_DEFAULT (0) -- cgit v1.2.3 From 3e439a0f6e6c982df4abf3ec60dc90cbcd819f39 Mon Sep 17 00:00:00 2001 From: "Ajitpal.Singh" Date: Tue, 26 Jul 2011 11:33:23 +0530 Subject: cw1200: Support for non-Greenfield STA protection +Support for non-Greenfield enabled STA protection when our STA is operating in Greenfield mode Change-Id: Id356467812713a7869ed81f3daf0993dcb768593 Signed-off-by: Ajitpal.Singh Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27806 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33490 Tested-by: Dmitry TARNYAGIN Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index fc23ff74100..adff7d51fc2 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -233,7 +233,6 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, /* Associated: kill join timeout */ cancel_delayed_work_sync(&priv->join_timeout); - /* TODO: This code is not verified {{{ */ rcu_read_lock(); if (info->bssid) sta = ieee80211_find_sta(vif, info->bssid); @@ -254,7 +253,20 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, priv->bss_params.operationalRateSet = -1; } rcu_read_unlock(); - /* }}} */ + + if (sta) { + __le32 val = 0; + if (priv->ht_info.operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) { + ap_printk(KERN_DEBUG"[STA]" + " Non-GF STA present\n"); + /* Non Green field capable STA */ + val = __cpu_to_le32(BIT(1)); + } + WARN_ON(wsm_write_mib(priv, + WSM_MID_ID_SET_HT_PROTECTION, + &val, sizeof(val))); + } priv->association_mode.greenfieldMode = cw1200_ht_greenfield(&priv->ht_info); -- cgit v1.2.3 From 936f509d95f51bf0d14fe68e011ee373e851bf05 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 3 Aug 2011 08:40:20 +0200 Subject: cw1200: Support for limited wowlan functionalities * Enables wowlan ANY support for wiphy structure * Set no off patterns support to 0 Change-Id: I56f58078850d13a3e178a021506cd1b64e9f3d35 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28169 Reviewed-by: Dmitry TARNYAGIN Tested-by: Bartosz MARKOWSKI Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33491 Tested-by: Dmitry TARNYAGIN Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index ded4fe9927c..8faa59fd633 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -268,6 +268,11 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MESH_POINT); + /* Support only for limited wowlan functionalities */ + hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY | + WIPHY_WOWLAN_DISCONNECT; + hw->wiphy->wowlan.n_patterns = 0; + hw->channel_change_time = 1000; /* TODO: find actual value */ /* priv->beacon_req_id = cpu_to_le32(0); */ hw->queues = 4; -- cgit v1.2.3 From bac96480350974193683150c4c7df16b9667de04 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Tue, 2 Aug 2011 15:18:24 +0200 Subject: cw1200: Fix for buffered multicasts in AP-PS mode: bugfix. Bugfix of Change-Id I15b134bf847913a907f00293ae99d7a71bbc7343. + Locking semantic is simplified. + Overcomplicated check of aid0_bit_timestamp removed: FW is doing it. + wsm_get_tx_queue_and_mask() mistakely ignored suspend_multicast. Change-Id: I0152fefd167dbb8a6fdc8dd8f5582b10fb645006 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28152 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33492 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 65 +++++++++++++++++++---------------------- drivers/staging/cw1200/cw1200.h | 3 +- drivers/staging/cw1200/sta.c | 1 - drivers/staging/cw1200/txrx.c | 12 +++----- drivers/staging/cw1200/wsm.c | 26 +++++++---------- 5 files changed, 45 insertions(+), 62 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index adff7d51fc2..55f254a6da6 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -89,18 +89,28 @@ void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, (struct cw1200_sta_priv *)&sta->drv_priv; u32 bit = BIT(sta_priv->link_id); + spin_lock_bh(&priv->buffered_multicasts_lock); switch (notify_cmd) { case STA_NOTIFY_SLEEP: + if (priv->buffered_multicasts && + !priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_start_work); priv->sta_asleep_mask |= bit; break; case STA_NOTIFY_AWAKE: priv->sta_asleep_mask &= ~bit; + if (priv->tx_multicast && + !priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_stop_work); cw1200_bh_wakeup(priv); break; } + spin_unlock_bh(&priv->buffered_multicasts_lock); } -static int cw1200_set_tim_impl(struct cw1200_common *priv) +static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set) { struct wsm_template_frame frame = { .frame_type = WSM_FRAME_TYPE_BEACON, @@ -120,7 +130,7 @@ static int cw1200_set_tim_impl(struct cw1200_common *priv) frame.skb->data[tim_offset + 2] = 0; /* Set/reset aid0 bit */ - if (priv->aid0_bit_set) + if (aid0_bit_set) frame.skb->data[tim_offset + 4] |= 1; else frame.skb->data[tim_offset + 4] &= ~1; @@ -137,7 +147,7 @@ void cw1200_set_tim_work(struct work_struct *work) { struct cw1200_common *priv = container_of(work, struct cw1200_common, set_tim_work); - (void)cw1200_set_tim_impl(priv); + (void)cw1200_set_tim_impl(priv, priv->aid0_bit_set); } int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, @@ -441,15 +451,11 @@ void cw1200_multicast_start_work(struct work_struct *work) struct cw1200_common *priv = container_of(work, struct cw1200_common, multicast_start_work); - bool store_timestamp = !priv->aid0_bit_set; - - priv->aid0_bit_set = true; - cw1200_set_tim_impl(priv); - if (store_timestamp) { - unsigned long now = jiffies; - if (unlikely(!now)) - ++now; - priv->aid0_bit_timestamp = now; + if (!priv->aid0_bit_set) { + wsm_lock_tx(priv); + cw1200_set_tim_impl(priv, true); + priv->aid0_bit_set = true; + wsm_unlock_tx(priv); } } @@ -458,12 +464,12 @@ void cw1200_multicast_stop_work(struct work_struct *work) struct cw1200_common *priv = container_of(work, struct cw1200_common, multicast_stop_work); - /* Flush to make sure frames are sent. */ - wsm_lock_tx(priv); - wsm_unlock_tx(priv); - - priv->aid0_bit_set = false; - cw1200_set_tim_impl(priv); + if (priv->aid0_bit_set) { + wsm_lock_tx(priv); + priv->aid0_bit_set = false; + cw1200_set_tim_impl(priv, false); + wsm_unlock_tx(priv); + } } int cw1200_ampdu_action(struct ieee80211_hw *hw, @@ -500,27 +506,16 @@ void cw1200_suspend_resume(struct cw1200_common *priv, arg->multicast ? "broadcast" : "unicast"); if (arg->multicast) { + spin_lock_bh(&priv->buffered_multicasts_lock); if (arg->stop) { - queue_work(priv->workqueue, - &priv->multicast_stop_work); + priv->tx_multicast = false; } else { - /* Handle only if there is data to be sent - * and aid0 is set in the beacon. */ - unsigned long timestamp = - priv->aid0_bit_timestamp; - /* 20ms grace interval is used to make sure - * the beacon was actually sent by hardware. */ - static const unsigned long grace_interval = - HZ * 20 / 1000; - - if (timestamp && - jiffies - timestamp > grace_interval) { - - priv->aid0_bit_timestamp = 0; - priv->suspend_multicast = false; + priv->tx_multicast = priv->aid0_bit_set && + priv->buffered_multicasts; + if (priv->tx_multicast) cw1200_bh_wakeup(priv); - } } + spin_unlock_bh(&priv->buffered_multicasts_lock); } else { if (arg->stop) set = unicast; diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 9ed86cd39a1..64bae8b03c0 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -166,11 +166,10 @@ struct cw1200_common { u32 link_id_map; u32 tx_suspend_mask[4]; u32 sta_asleep_mask; - bool suspend_multicast; bool aid0_bit_set; - unsigned long aid0_bit_timestamp; spinlock_t buffered_multicasts_lock; bool buffered_multicasts; + bool tx_multicast; struct work_struct set_tim_work; struct work_struct multicast_start_work; struct work_struct multicast_stop_work; diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 4923e9dd22f..c24e5c3c8c8 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -121,7 +121,6 @@ void cw1200_stop(struct ieee80211_hw *dev) * through the map and reset each link separately. */ WARN_ON(priv->link_id_map); priv->sta_asleep_mask = 0; - priv->suspend_multicast = false; wsm_reset(priv, &reset); break; case CW1200_JOIN_STATUS_MONITOR: diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 5439947d742..a87743b453a 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -422,7 +422,6 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) (struct ieee80211_hdr *)skb->data; struct cw1200_sta_priv *sta_priv = (struct cw1200_sta_priv *)&tx_info->control.sta->drv_priv; - bool obtain_lock; int link_id = 0; int ret; @@ -512,21 +511,18 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) if (cw1200_handle_action_tx(priv, skb)) goto drop; - obtain_lock = (link_id == CW1200_LINK_ID_AFTER_DTIM); - - if (obtain_lock) - spin_lock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->buffered_multicasts_lock); if (link_id == CW1200_LINK_ID_AFTER_DTIM && !priv->buffered_multicasts) { priv->buffered_multicasts = true; - queue_work(priv->workqueue, + if (priv->sta_asleep_mask) + queue_work(priv->workqueue, &priv->multicast_start_work); } ret = cw1200_queue_put(&priv->tx_queue[queue], priv, skb, link_id); - if (obtain_lock) - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->buffered_multicasts_lock); if (!WARN_ON(ret)) cw1200_bh_wakeup(priv); diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 4db5129394e..035f8e3d8ce 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1427,7 +1427,7 @@ static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv, int mcasts = 0; /* Search for a queue with multicast frames buffered */ - if (priv->sta_asleep_mask && !priv->suspend_multicast) { + if (priv->tx_multicast) { tx_allowed_mask = BIT(CW1200_LINK_ID_AFTER_DTIM); for (i = 0; i < 4; ++i) { mcasts += cw1200_queue_get_num_queued( @@ -1447,6 +1447,7 @@ static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv, tx_allowed_mask = ~priv->sta_asleep_mask; if (priv->sta_asleep_mask) { tx_allowed_mask |= ~priv->tx_suspend_mask[i]; + tx_allowed_mask &= ~BIT(CW1200_LINK_ID_AFTER_DTIM); } else { tx_allowed_mask |= BIT(CW1200_LINK_ID_AFTER_DTIM); } @@ -1491,34 +1492,27 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, spin_unlock(&priv->wsm_cmd.lock); } else { for (;;) { - bool obtain_lock = priv->sta_asleep_mask && - !priv->suspend_multicast; int ret; if (atomic_add_return(0, &priv->tx_lock)) break; - if (obtain_lock) - spin_lock_bh( - &priv->buffered_multicasts_lock); + spin_lock_bh(&priv->buffered_multicasts_lock); ret = wsm_get_tx_queue_and_mask(priv, &queue, &tx_allowed_mask, &more); if (priv->buffered_multicasts && - priv->sta_asleep_mask && - !priv->suspend_multicast && - (ret || tx_allowed_mask != - BIT(CW1200_LINK_ID_AFTER_DTIM))) { + (ret || !more) && + (priv->tx_multicast || + !priv->sta_asleep_mask)) { priv->buffered_multicasts = false; - priv->suspend_multicast = true; - queue_work(priv->workqueue, - &priv->multicast_stop_work); + if (priv->tx_multicast) + queue_work(priv->workqueue, + &priv->multicast_stop_work); } - if (obtain_lock) - spin_unlock_bh( - &priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->buffered_multicasts_lock); if (ret) break; -- cgit v1.2.3 From 05545826bc8a21bfdf4b36d1b62fb27ff396846e Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 3 Aug 2011 11:39:01 +0200 Subject: cw1200: Workaround against cyclic requeue of multicast frames. WSM324 firmware has tendency to requeue multicast frames in a loop, causing performance drop and high power consumption of the driver. The workaround blocks attempts to requeue multicasts frames. Workaround has to be modified for further WSM firmware releases where this issue is suppose to be fixed with respect to backward compatibility. Change-Id: I27b66d9acc48b8058efd524a72e6f69c816393cc Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28209 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33493 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/txrx.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index a87743b453a..8531994d3da 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -595,7 +595,20 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, .multicast = !arg->link_id, }; cw1200_suspend_resume(priv, &suspend); - WARN_ON(cw1200_queue_requeue(queue, arg->packetID)); + if (suspend.multicast) { + /* HACK!!! WSM324 firmware has tendency to requeue + * multicast frames in a loop, causing performance + * drop and high power consumption of the driver. + * In this situation it is better just to drop + * the problematic frame. */ + wiphy_warn(priv->hw->wiphy, "Attempt to requeue a " + "multicat frame. Frame is dropped\n"); + WARN_ON(cw1200_queue_remove(queue, priv, + arg->packetID)); + } else { + WARN_ON(cw1200_queue_requeue(queue, + arg->packetID)); + } } else if (!WARN_ON(cw1200_queue_get_skb( queue, arg->packetID, &skb))) { struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb); -- cgit v1.2.3 From 89dc32a9c559b986984f70eb7b35dbdef8fe5a68 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 3 Aug 2011 11:49:55 +0200 Subject: cw1200: Removing obsolete code which blocked power management by default. Legacy code from p54 driver was left by mistake in the main configuration, causing power save to be disabled by default. Change-Id: I6428a8a041e34c02cedd54652d54162aa9233f1c Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28210 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33494 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/main.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 8faa59fd633..8782f92aca7 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -284,12 +284,6 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) hw->sta_data_size = sizeof(struct cw1200_sta_priv); - /* - * For now, disable PS by default because it affects - * link stability significantly. - */ - hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &cw1200_band_2ghz; #ifdef CONFIG_CW1200_5GHZ_SUPPORT hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &cw1200_band_5ghz; -- cgit v1.2.3 From aa53ef3e524bc58223040dfb308b20d9853043ee Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 4 Aug 2011 11:21:33 +0200 Subject: cw1200: Remove some spam from logging. FW sends "start multicast" request on every DTIM. It's interesting to see in log only if buffered multicasts are waiting for delivery. Change-Id: I5faefbdd18710e38fab4087e2676c1e245210cee Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28258 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33495 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 55f254a6da6..4f9afde2df5 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -501,9 +501,12 @@ void cw1200_suspend_resume(struct cw1200_common *priv, if (!arg->link_id) /* For all links */ unicast = BIT(CW1200_MAX_STA_IN_AP_MODE + 1) - 2; - ap_printk(KERN_DEBUG "[AP] %s: %s\n", - arg->stop ? "stop" : "start", - arg->multicast ? "broadcast" : "unicast"); + /* if () is intendend to protect against spam. FW sends + * "start multicast" request on every DTIM. */ + if (arg->stop || !arg->multicast || priv->buffered_multicasts) + ap_printk(KERN_DEBUG "[AP] %s: %s\n", + arg->stop ? "stop" : "start", + arg->multicast ? "broadcast" : "unicast"); if (arg->multicast) { spin_lock_bh(&priv->buffered_multicasts_lock); -- cgit v1.2.3 From b87a5d05747a79b6c84a51c8714419c0310af955 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 3 Aug 2011 22:24:40 +0200 Subject: cw1200: Minor fixes. * Driver unloading without prior "ifconfig wlan0 down" caused warnings in cw1200_remove_interface() and cw1200_setup_mac(). * Entering SoftAP mode caused a warning in cw1200_set_tim_impl(). * Scan request failed with timeout if current BSS was out of range. * wsm_set_pm is failing if scanned BSS is out of range, and it is not a fault. * Some state wariables were not reset to initial state in cw1200_stop(). ST-Ericsson ID: ER354873 ST-Ericsson ID: ER354919 Change-Id: I7f12748c0570de5c6f25aa8bf83b6012b44d87de Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28232 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33496 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/main.c | 3 ++- drivers/staging/cw1200/scan.c | 10 +++++----- drivers/staging/cw1200/sta.c | 4 ++++ drivers/staging/cw1200/txrx.c | 3 ++- drivers/staging/cw1200/wsm.c | 6 +++--- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 8782f92aca7..a704d12d469 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -390,6 +390,8 @@ void cw1200_unregister_common(struct ieee80211_hw *dev) struct cw1200_common *priv = dev->priv; int i; + ieee80211_unregister_hw(dev); + cw1200_debug_release(priv); priv->sbus_ops->irq_unsubscribe(priv->sbus_priv); @@ -399,7 +401,6 @@ void cw1200_unregister_common(struct ieee80211_hw *dev) cw1200_unregister_leds(priv); #endif /* CONFIG_CW1200_LEDS */ - ieee80211_unregister_hw(dev); mutex_destroy(&priv->conf_mutex); mutex_destroy(&priv->eeprom_mutex); diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index 7fb42d4ee05..17706e58549 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -16,7 +16,7 @@ static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan) { int ret, i; - int tmo = 1000; + int tmo = 2000; for (i = 0; i < scan->numOfChannels; ++i) tmo += scan->ch[i].maxChannelTime + 10; @@ -91,7 +91,7 @@ int cw1200_hw_scan(struct ieee80211_hw *hw, !(priv->powersave_mode.pmMode & WSM_PSM_PS)) { struct wsm_set_pm pm = priv->powersave_mode; pm.pmMode = WSM_PSM_PS; - WARN_ON(wsm_set_pm(priv, &pm)); + wsm_set_pm(priv, &pm); } BUG_ON(priv->scan.req); @@ -140,7 +140,7 @@ void cw1200_scan_work(struct work_struct *work) priv->output_power * 10)); if (priv->join_status == CW1200_JOIN_STATUS_STA && !(priv->powersave_mode.pmMode & WSM_PSM_PS)) - WARN_ON(wsm_set_pm(priv, &priv->powersave_mode)); + wsm_set_pm(priv, &priv->powersave_mode); if (priv->scan.req) printk(KERN_DEBUG "[SCAN] Scan completed.\n"); @@ -273,8 +273,8 @@ void cw1200_scan_timeout(struct work_struct *work) struct cw1200_common *priv = container_of(work, struct cw1200_common, scan.timeout.work); if (likely(atomic_xchg(&priv->scan.in_progress, 0))) { - cw1200_dbg(CW1200_DBG_ERROR, - "CW1200 FW: Timeout waiting for scan " \ + wiphy_warn(priv->hw->wiphy, + "Timeout waiting for scan " \ "complete notification.\n"); cw1200_scan_complete(priv); } diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index c24e5c3c8c8..e69bd729f07 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -121,6 +121,10 @@ void cw1200_stop(struct ieee80211_hw *dev) * through the map and reset each link separately. */ WARN_ON(priv->link_id_map); priv->sta_asleep_mask = 0; + priv->enable_beacon = false; + priv->tx_multicast = false; + priv->aid0_bit_set = false; + priv->buffered_multicasts = false; wsm_reset(priv, &reset); break; case CW1200_JOIN_STATUS_MONITOR: diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 8531994d3da..6cae1a046d6 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -426,7 +426,8 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) int ret; if ((tx_info->flags | IEEE80211_TX_CTL_SEND_AFTER_DTIM) && - (priv->mode == NL80211_IFTYPE_AP)) + (priv->mode == NL80211_IFTYPE_AP) && + priv->enable_beacon) link_id = CW1200_LINK_ID_AFTER_DTIM; else if (tx_info->control.sta) link_id = sta_priv->link_id; diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 035f8e3d8ce..097bd6d20f1 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1184,10 +1184,10 @@ int wsm_handle_rx(struct cw1200_common *priv, int id, WARN_ON(wsm_arg != NULL); ret = wsm_generic_confirm(priv, wsm_arg, &wsm_buf); if (ret) - printk(KERN_ERR - "[WSM] wsm_generic_confirm " + wiphy_warn(priv->hw->wiphy, + "wsm_generic_confirm " "failed for request 0x%.4X.\n", - id); + id & ~0x0400); break; default: BUG_ON(1); -- cgit v1.2.3 From 591e2060969f6e0724a0ba08a00a54a56890c139 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 4 Aug 2011 14:53:06 +0200 Subject: cw1200: Fix for a crash in cw1200_join_work. [ 129.340148] Unable to handle kernel NULL pointer dereference at virtual address 00000018 [ 129.348266] pgd = c0004000 [ 129.350982] [00000018] *pgd=00000000 [ 129.354553] Internal error: Oops: 17 [#1] PREEMPT SMP [ 129.359619] last sysfs file: /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state [ 129.367431] Modules linked in: cw1200_wlan cw1200_core [ 129.372589] CPU: 1 Not tainted (2.6.35.7+ #111) [ 129.377502] PC is at cw1200_join_work+0x148/0x3c8 [cw1200_core] [ 129.383422] LR is at mutex_lock+0x18/0x4c Change-Id: I62b013780fa66592d49938c6d7d86229450105a1 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28283 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33497 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/sta.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index e69bd729f07..86a05dcaca2 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -968,17 +968,22 @@ void cw1200_join_work(struct work_struct *work) mutex_lock(&priv->conf_mutex); { struct wsm_join join = { - .mode = (bss->capability & WLAN_CAPABILITY_IBSS) ? - WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS, + .mode = WSM_JOIN_MODE_BSS, .preambleType = WSM_JOIN_PREAMBLE_SHORT, .probeForJoin = 1, /* dtimPeriod will be updated after association */ .dtimPeriod = 1, - .beaconInterval = bss->beacon_interval, + .beaconInterval = 100, /* basicRateSet will be updated after association */ .basicRateSet = 7, }; + if (bss) { + join.mode = (bss->capability & WLAN_CAPABILITY_IBSS) ? + WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS; + join.beaconInterval = bss->beacon_interval; + } + if (tim && tim->dtim_period > 1) { join.dtimPeriod = tim->dtim_period; priv->join_dtim_period = tim->dtim_period; @@ -1036,7 +1041,8 @@ void cw1200_join_work(struct work_struct *work) cw1200_update_filtering(priv); } mutex_unlock(&priv->conf_mutex); - cfg80211_put_bss(bss); + if (bss) + cfg80211_put_bss(bss); } void cw1200_join_timeout(struct work_struct *work) -- cgit v1.2.3 From a90761feb77ebcdd03bed963bb75809d8080b75c Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 4 Aug 2011 17:58:27 +0200 Subject: cw1200: Fix for driver hang at join timeout (scan in progress) Join timout might interfere with scan activity. Firmware hangs if unjoin (reset) request is triggered when scan is in progress. The patchs delays unjoin request up to completion of the scan. Change-Id: I17fa573ee52aab843e69e4344ae5b66e94b5b4db Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28298 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33498 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/cw1200.h | 1 + drivers/staging/cw1200/scan.c | 68 +++++++++++++++++++++++------------------ drivers/staging/cw1200/sta.c | 7 +++++ 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 64bae8b03c0..13c3b2d3751 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -154,6 +154,7 @@ struct cw1200_common { struct delayed_work join_timeout; struct work_struct unjoin_work; int join_dtim_period; + bool delayed_unjoin; /* TX/RX and security */ s8 wep_default_key_id; diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index 17706e58549..3c80c571ea7 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -13,6 +13,8 @@ #include "cw1200.h" #include "scan.h" +static void cw1200_scan_restart_delayed(struct cw1200_common *priv); + static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan) { int ret, i; @@ -28,6 +30,7 @@ static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan) if (unlikely(ret)) { atomic_set(&priv->scan.in_progress, 0); cancel_delayed_work_sync(&priv->scan.timeout); + cw1200_scan_restart_delayed(priv); } return ret; } @@ -48,7 +51,7 @@ int cw1200_hw_scan(struct ieee80211_hw *hw, if (req->n_ssids == 1 && !req->ssids[0].ssid_len) req->n_ssids = 0; - printk(KERN_DEBUG "[SCAN] Scan request for %d SSIDs.\n", + wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n", req->n_ssids); if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS) @@ -143,25 +146,14 @@ void cw1200_scan_work(struct work_struct *work) wsm_set_pm(priv, &priv->powersave_mode); if (priv->scan.req) - printk(KERN_DEBUG "[SCAN] Scan completed.\n"); + wiphy_dbg(priv->hw->wiphy, + "[SCAN] Scan completed.\n"); else - printk(KERN_DEBUG "[SCAN] Scan canceled.\n"); + wiphy_dbg(priv->hw->wiphy, + "[SCAN] Scan canceled.\n"); priv->scan.req = NULL; - - if (priv->delayed_link_loss) { - priv->delayed_link_loss = 0; - /* Restart beacon loss timer and requeue - BSS loss work. */ - printk(KERN_DEBUG "[CQM] Requeue BSS loss in %d " \ - "beacons.\n", - priv->cqm_beacon_loss_count); - cancel_delayed_work_sync(&priv->bss_loss_work); - queue_delayed_work(priv->workqueue, - &priv->bss_loss_work, - priv->cqm_beacon_loss_count * HZ / 10); - } - + cw1200_scan_restart_delayed(priv); wsm_unlock_tx(priv); mutex_unlock(&priv->conf_mutex); ieee80211_scan_completed(priv->hw, priv->scan.status ? 1 : 0); @@ -231,22 +223,38 @@ fail: return; } +static void cw1200_scan_restart_delayed(struct cw1200_common *priv) +{ + if (priv->delayed_link_loss) { + int tmo = priv->cqm_beacon_loss_count; + + if (priv->scan.direct_probe) + tmo = 0; + + priv->delayed_link_loss = 0; + /* Restart beacon loss timer and requeue + BSS loss work. */ + wiphy_dbg(priv->hw->wiphy, + "[CQM] Requeue BSS loss in %d " + "beacons.\n", tmo); + cancel_delayed_work_sync(&priv->bss_loss_work); + queue_delayed_work(priv->workqueue, + &priv->bss_loss_work, + tmo * HZ / 10); + } + if (priv->delayed_unjoin) { + priv->delayed_unjoin = false; + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + } +} + static void cw1200_scan_complete(struct cw1200_common *priv) { if (priv->scan.direct_probe) { - printk(KERN_DEBUG "[SCAN] Direct probe complete.\n"); + wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe complete.\n"); + cw1200_scan_restart_delayed(priv); priv->scan.direct_probe = 0; - - if (priv->delayed_link_loss) { - priv->delayed_link_loss = 0; - /* Requeue BSS loss work now. Direct probe does not - * affect BSS loss subscription. */ - printk(KERN_DEBUG "[CQM] Requeue BSS loss now.\n"); - cancel_delayed_work_sync(&priv->bss_loss_work); - queue_delayed_work(priv->workqueue, - &priv->bss_loss_work, 0); - } - up(&priv->scan.lock); wsm_unlock_tx(priv); } else { @@ -310,7 +318,7 @@ void cw1200_probe_work(struct work_struct *work) size_t ies_len; int ret; - printk(KERN_DEBUG "[SCAN] Direct probe work.\n"); + wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n"); if (!priv->channel) { dev_kfree_skb(priv->scan.probe_skb); diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 86a05dcaca2..8c813282021 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1064,6 +1064,13 @@ void cw1200_unjoin_work(struct work_struct *work) }; mutex_lock(&priv->conf_mutex); + if (unlikely(atomic_read(&priv->scan.in_progress))) { + BUG_ON(priv->delayed_unjoin); + priv->delayed_unjoin = true; + mutex_unlock(&priv->conf_mutex); + return; + } + BUG_ON(priv->join_status && priv->join_status != CW1200_JOIN_STATUS_STA); if (priv->join_status == CW1200_JOIN_STATUS_STA) { -- cgit v1.2.3 From cb427a04d73fa66aa5405413ba23212cc468abea Mon Sep 17 00:00:00 2001 From: "Ajitpal.Singh" Date: Thu, 4 Aug 2011 16:33:08 +0530 Subject: cw1200: Allow proberesponse frame to be txed Added change for allowing probe response frame to be txe'ed Probe Resp frames are generated by the supplicant during p2p find process. Change-Id: If36fb07d3aabf38870e32f4c7ce21a317551badb Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28272 Tested-by: Janusz DZIEDZIC Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33499 Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/wsm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 097bd6d20f1..59f40666d69 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1262,7 +1262,8 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, action = doJoin; else if (ieee80211_is_probe_req(fctl)) action = doTx; - else if (ieee80211_is_action(fctl)) { + else if (ieee80211_is_action(fctl) || + ieee80211_is_probe_resp(fctl)) { if (priv->join_status) action = doTx; else -- cgit v1.2.3 From c99aef0bd5d3a6f42555483a91c82bec20b77f89 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Thu, 4 Aug 2011 11:45:14 +0200 Subject: cw1200: P2P power save support P2P power save support: - Opportunistic Power Save - Notice Of Absence Change-Id: I5523c852632598d9c80acc390c920ccfb42eaefd Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28267 Tested-by: Janusz DZIEDZIC Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33500 Tested-by: Dmitry TARNYAGIN Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 68 +++++++++++++++++++++++++++++------------ drivers/staging/cw1200/cw1200.h | 3 ++ drivers/staging/cw1200/main.c | 5 ++- drivers/staging/cw1200/sta.c | 57 ++++++++++++++++++++++++++++++++++ drivers/staging/cw1200/wsm.h | 46 +++++++++++++++++++++++++++- 5 files changed, 157 insertions(+), 22 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 4f9afde2df5..9a8e41f1e2a 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -23,7 +23,8 @@ static int cw1200_upload_beacon(struct cw1200_common *priv); static int cw1200_start_ap(struct cw1200_common *priv); static int cw1200_update_beaconing(struct cw1200_common *priv); - +static int cw1200_enable_beaconing(struct cw1200_common *priv, + bool enable); /* ******************************************************************** */ /* AP API */ @@ -205,15 +206,30 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, WARN_ON(1); } - if (changed & BSS_CHANGED_BEACON_ENABLED) - priv->enable_beacon = info->enable_beacon; - if (changed & BSS_CHANGED_BEACON) + if (changed & BSS_CHANGED_BEACON) { + ap_printk(KERN_DEBUG "BSS_CHANGED_BEACON\n"); + WARN_ON(cw1200_update_beaconing(priv)); WARN_ON(cw1200_upload_beacon(priv)); + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) { + ap_printk(KERN_DEBUG "BSS_CHANGED_BEACON_ENABLED\n"); + + if (priv->enable_beacon != info->enable_beacon) { + WARN_ON(cw1200_enable_beaconing(priv, + info->enable_beacon)); + priv->enable_beacon = info->enable_beacon; + } + } + + if (changed & BSS_CHANGED_BEACON_INT) { + ap_printk(KERN_DEBUG "CHANGED_BEACON_INT\n"); + /* Restart AP only when connected */ + if(priv->join_status == CW1200_JOIN_STATUS_AP) + WARN_ON(cw1200_update_beaconing(priv)); + } - if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON | - BSS_CHANGED_BEACON_INT)) - WARN_ON(cw1200_update_beaconing(priv)); if (changed & BSS_CHANGED_ASSOC) { wsm_lock_tx(priv); @@ -562,7 +578,6 @@ static int cw1200_upload_beacon(struct cw1200_common *priv) .frame_type = WSM_FRAME_TYPE_BEACON, }; - ap_printk(KERN_DEBUG "[AP] %s.\n", __func__); frame.skb = ieee80211_beacon_get(priv->hw, priv->vif); if (WARN_ON(!frame.skb)) @@ -597,6 +612,16 @@ static int cw1200_upload_beacon(struct cw1200_common *priv) return ret; } +static int cw1200_enable_beaconing(struct cw1200_common *priv, + bool enable) +{ + struct wsm_beacon_transmit transmit = { + .enableBeaconing = enable, + }; + + return wsm_beacon_transmit(priv, &transmit); +} + static int cw1200_start_ap(struct cw1200_common *priv) { int ret; @@ -616,23 +641,18 @@ static int cw1200_start_ap(struct cw1200_common *priv) conf->basic_rates), .ssidLength = priv->ssid_length, }; - struct wsm_beacon_transmit transmit = { - .enableBeaconing = priv->enable_beacon, - }; + priv->beacon_int = conf->beacon_int; memcpy(&start.ssid[0], priv->ssid, start.ssidLength); - ap_printk(KERN_DEBUG "[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s %s.\n", + ap_printk(KERN_DEBUG "[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s.\n", start.channelNumber, start.band, start.beaconInterval, start.DTIMPeriod, start.basicRateSet, - start.ssidLength, start.ssid, - transmit.enableBeaconing ? "ena" : "dis"); + start.ssidLength, start.ssid); ret = WARN_ON(wsm_start(priv, &start)); if (!ret) ret = WARN_ON(cw1200_upload_keys(priv)); - if (!ret) - ret = WARN_ON(wsm_beacon_transmit(priv, &transmit)); if (!ret) { WARN_ON(wsm_set_block_ack_policy(priv, priv->ba_tid_mask, priv->ba_tid_mask)); @@ -644,16 +664,24 @@ static int cw1200_start_ap(struct cw1200_common *priv) static int cw1200_update_beaconing(struct cw1200_common *priv) { + struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; struct wsm_reset reset = { .link_id = 0, .reset_statistics = true, }; if (priv->mode == NL80211_IFTYPE_AP) { - ap_printk(KERN_DEBUG "[AP] %s.\n", __func__); - WARN_ON(wsm_reset(priv, &reset)); - priv->join_status = CW1200_JOIN_STATUS_PASSIVE; - WARN_ON(cw1200_start_ap(priv)); + /* TODO: check if changed channel, band */ + if (priv->join_status != CW1200_JOIN_STATUS_AP || + priv->beacon_int != conf->beacon_int) { + ap_printk(KERN_DEBUG "ap restarting\n"); + if (priv->join_status != CW1200_JOIN_STATUS_PASSIVE) + WARN_ON(wsm_reset(priv, &reset)); + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + WARN_ON(cw1200_start_ap(priv)); + } else + ap_printk(KERN_DEBUG "ap started join_status: %d\n", + priv->join_status); } return 0; } diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 13c3b2d3751..d0c2d7588d3 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -106,14 +106,17 @@ struct cw1200_common { u8 short_frame_max_tx_count; int mode; bool enable_beacon; + int beacon_int; size_t ssid_length; u8 ssid[IEEE80211_MAX_SSID_LEN]; bool listening; struct wsm_rx_filter rx_filter; + struct wsm_beacon_filter_table bf_table; struct wsm_beacon_filter_control bf_control; u8 ba_tid_mask; struct wsm_multicast_filter multicast_filter; struct cw1200_suspend_state *suspend_state; + struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo; /* BH */ atomic_t bh_rx; diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index a704d12d469..74095a59d8a 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -257,6 +257,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) /* Aggregation is fully controlled by firmware. * Do not need any support from the mac80211 stack */ /* IEEE80211_HW_AMPDU_AGGREGATION | */ + IEEE80211_HW_SUPPORTS_P2P_PS | #if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) IEEE80211_HW_SUPPORTS_CQM_BEACON_MISS | IEEE80211_HW_SUPPORTS_CQM_TX_FAIL | @@ -266,7 +267,9 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_MESH_POINT); + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); /* Support only for limited wowlan functionalities */ hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY | diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 8c813282021..a0f65c37c18 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -67,6 +67,17 @@ int cw1200_start(struct ieee80211_hw *dev) priv->cqm_link_loss_count = 60; priv->cqm_beacon_loss_count = 20; + /* Temporary configuration - beacon filter table */ + priv->bf_table.numOfIEs = __cpu_to_le32(1); + priv->bf_table.entry[0].ieId = WLAN_EID_VENDOR_SPECIFIC; + priv->bf_table.entry[0].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED; + priv->bf_table.entry[0].oui[0] = 0x50; + priv->bf_table.entry[0].oui[1] = 0x6F; + priv->bf_table.entry[0].oui[2] = 0x9A; + + priv->bf_control.enabled = 1; ret = cw1200_setup_mac(priv); if (WARN_ON(ret)) goto out; @@ -304,6 +315,50 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) WARN_ON(wsm_set_pm(priv, &priv->powersave_mode)); } + if (changed & IEEE80211_CONF_CHANGE_P2P_PS) { + struct wsm_p2p_ps_modeinfo *modeinfo; + modeinfo = &priv->p2p_ps_modeinfo; + sta_printk(KERN_DEBUG "[STA] IEEE80211_CONF_CHANGE_P2P_PS\n"); + + if (conf->p2p_ps.ctwindow >= 128) + modeinfo->oppPsCTWindow = 127; + else if (conf->p2p_ps.ctwindow >= 0) + modeinfo->oppPsCTWindow = conf->p2p_ps.ctwindow; + + switch (conf->p2p_ps.opp_ps) { + case 0: + modeinfo->oppPsCTWindow &= ~(BIT(7)); + break; + case 1: + modeinfo->oppPsCTWindow |= BIT(7); + break; + default: + break; + } + + /* Notice of Absence */ + modeinfo->count = conf->p2p_ps.count; + modeinfo->startTime = __cpu_to_le32(conf->p2p_ps.start); + modeinfo->duration = __cpu_to_le32(conf->p2p_ps.duration); + modeinfo->interval = __cpu_to_le32(conf->p2p_ps.interval); + + if(conf->p2p_ps.count) + modeinfo->dtimCount = 1; + else + modeinfo->dtimCount = 0; + + if (priv->join_status == CW1200_JOIN_STATUS_STA || + priv->join_status == CW1200_JOIN_STATUS_AP) { +#if defined(CONFIG_CW1200_STA_DEBUG) + print_hex_dump_bytes("p2p_ps_modeinfo: ", + DUMP_PREFIX_NONE, + (u8*) modeinfo, + sizeof(*modeinfo)); +#endif + WARN_ON(wsm_set_p2p_ps_modeinfo(priv, modeinfo)); + } + } + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { /* TBD: It looks like it's transparent * there's a monitor interface present -- use this @@ -352,6 +407,8 @@ void cw1200_update_filtering(struct cw1200_common *priv) return; ret = wsm_set_rx_filter(priv, &priv->rx_filter); + if (!ret) + ret = wsm_set_beacon_filter_table(priv, &priv->bf_table); if (!ret) ret = wsm_beacon_filter_control(priv, &priv->bf_control); if (!ret) diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 55792c4b3c8..66534b940aa 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -1246,6 +1246,32 @@ static inline int wsm_set_rx_filter(struct cw1200_common *priv, return wsm_write_mib(priv, WSM_MIB_ID_RX_FILTER, &val, sizeof(val)); } +#define WSM_BEACON_FILTER_IE_HAS_CHANGED BIT(0) +#define WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT BIT(1) +#define WSM_BEACON_FILTER_IE_HAS_APPEARED BIT(2) + +struct wsm_beacon_filter_table_entry { + u8 ieId; + u8 actionFlags; + u8 oui[3]; + u8 matchData[3]; +} __packed; + +struct wsm_beacon_filter_table { + __le32 numOfIEs; + struct wsm_beacon_filter_table_entry entry[10]; +} __packed; + +static inline int wsm_set_beacon_filter_table(struct cw1200_common *priv, + struct wsm_beacon_filter_table *ft) +{ + size_t size = __le32_to_cpu(ft->numOfIEs) * + sizeof(struct wsm_beacon_filter_table_entry) + + sizeof(__le32); + + return wsm_write_mib(priv, WSM_MIB_ID_BEACON_FILTER_TABLE, ft, size); +} + struct wsm_beacon_filter_control { int enabled; int bcn_count; @@ -1480,7 +1506,7 @@ static inline int wsm_set_multicast_filter(struct cw1200_common *priv, fp, sizeof(*fp)); } -/* IPv4 filtering - 4.10 */ +/* ARP IPv4 filtering - 4.10 */ struct wsm_arp_ipv4_filter { __le32 enable; __be32 ipv4Address[WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES]; @@ -1493,6 +1519,24 @@ static inline int wsm_set_arp_ipv4_filter(struct cw1200_common *priv, fp, sizeof(*fp)); } +/* P2P Power Save Mode Info - 4.31 */ +struct wsm_p2p_ps_modeinfo { + u8 oppPsCTWindow; + u8 count; + u8 reserved; + u8 dtimCount; + __le32 duration; + __le32 interval; + __le32 startTime; +} __packed; + +static inline int wsm_set_p2p_ps_modeinfo(struct cw1200_common *priv, + struct wsm_p2p_ps_modeinfo *mi) +{ + return wsm_write_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO, + mi, sizeof(*mi)); +} + /* UseMultiTxConfMessage */ static inline int wsm_use_multi_tx_conf(struct cw1200_common *priv, -- cgit v1.2.3 From a8e47a9f68a7efcb4d54a2e62dfdd04d53b31845 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Fri, 5 Aug 2011 11:58:52 +0200 Subject: cw1200: Do join if upper layer is trying to TX in monitor mode. Change-Id: Idf79762b48f37fc6ac315ef1cecdf3a28c2e38f4 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28326 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33501 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/cw1200.h | 1 + drivers/staging/cw1200/sta.c | 24 ++++++++++++++++++------ drivers/staging/cw1200/wsm.c | 5 +++-- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index d0c2d7588d3..067ad1ed5cd 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -47,6 +47,7 @@ #define CW1200_MAX_STA_IN_AP_MODE (5) #define CW1200_LINK_ID_AFTER_DTIM (CW1200_MAX_STA_IN_AP_MODE + 1) +/* Please keep order */ enum cw1200_join_status { CW1200_JOIN_STATUS_PASSIVE = 0, CW1200_JOIN_STATUS_MONITOR, diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index a0f65c37c18..cd0284f3f05 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -995,9 +995,10 @@ void cw1200_join_work(struct work_struct *work) BUG_ON(!priv->channel); queueId = wsm_queue_id_to_linux(wsm->queueId); - action = ieee80211_is_action(frame->frame_control); + action = ieee80211_is_action(frame->frame_control) || + ieee80211_is_probe_resp(frame->frame_control); - if (unlikely(priv->join_status == CW1200_JOIN_STATUS_STA)) { + if (unlikely(priv->join_status)) { wsm_lock_tx(priv); cw1200_unjoin_work(&priv->unjoin_work); } @@ -1122,15 +1123,26 @@ void cw1200_unjoin_work(struct work_struct *work) mutex_lock(&priv->conf_mutex); if (unlikely(atomic_read(&priv->scan.in_progress))) { - BUG_ON(priv->delayed_unjoin); + if (priv->delayed_unjoin) { + wiphy_err(priv->hw->wiphy, + "%s: Unexpected: delayed unjoin " + "is already scheduled.\n", + __func__); + BUG_ON(1); + } priv->delayed_unjoin = true; mutex_unlock(&priv->conf_mutex); return; } - BUG_ON(priv->join_status && - priv->join_status != CW1200_JOIN_STATUS_STA); - if (priv->join_status == CW1200_JOIN_STATUS_STA) { + if (priv->join_status && + priv->join_status > CW1200_JOIN_STATUS_STA) { + wiphy_err(priv->hw->wiphy, + "%s: Unexpected: join status: %d\n", + __func__, priv->join_status); + BUG_ON(1); + } + if (priv->join_status) { memset(&priv->join_bssid[0], 0, sizeof(priv->join_bssid)); priv->join_status = CW1200_JOIN_STATUS_PASSIVE; diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 59f40666d69..1fe5e32a27a 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1255,7 +1255,8 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, switch (priv->mode) { case NL80211_IFTYPE_STATION: - if (unlikely(!priv->join_status || + if (unlikely( + (priv->join_status <= CW1200_JOIN_STATUS_MONITOR) || memcmp(frame->addr1, priv->join_bssid, sizeof(priv->join_bssid)))) { if (ieee80211_is_auth(fctl)) @@ -1264,7 +1265,7 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, action = doTx; else if (ieee80211_is_action(fctl) || ieee80211_is_probe_resp(fctl)) { - if (priv->join_status) + if (priv->join_status > CW1200_JOIN_STATUS_MONITOR) action = doTx; else action = doJoin; -- cgit v1.2.3 From 8d15072effaeea5d7cd3d0c0025267a84ba710e6 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Fri, 5 Aug 2011 13:56:56 +0200 Subject: cw1200: Missing interrupt detection. Missing interrupt (or slow response?) was observed on wsm_reset() call. Indication: [ 106.596252] ADDRCONF(NETDEV_UP): wlan0: link is not ready [ 108.681884] WARNING: at kernel/net/compat-wireless/drivers/staging/cw1200/ap.c:654 cw1200_bss_info_changed+0x2dc/0x9f8 [cw1200_core]() ... [ 108.931396] WARNING: at kernel/net/compat-wireless/drivers/staging/cw1200/wsm.c:1141 wsm_handle_rx+0x9bc/0x9f4 [cw1200_core]() ... [ 109.021667] WARNING: at kernel/net/compat-wireless/drivers/staging/cw1200/bh.c:411 cw1200_bh+0x718/0x95c [cw1200_core]() [ 109.095275] [BH] Fatal error, exitting. The patch implements missing interrupt detection and also increases timeout for the wsm_reset() call. Change-Id: I3fb9ad92ab62bc1be831c835fd16a7c600793bb6 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28336 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33502 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/bh.c | 8 +++++++- drivers/staging/cw1200/wsm.c | 8 ++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index cddfd9dfaa4..1ecae7466f6 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -270,6 +270,9 @@ static int cw1200_bh(void *arg) && priv->powersave_enabled && !priv->device_can_sleep) status = 1 * HZ; + else if (priv->hw_bufs_used) + /* Interrupt loss detection */ + status = 1 * HZ; else status = MAX_SCHEDULE_TIMEOUT; @@ -284,7 +287,10 @@ static int cw1200_bh(void *arg) if (status < 0 || term) break; - if (!status) { + if (!status && priv->hw_bufs_used) { + wiphy_warn(priv->hw->wiphy, "Missed interrupt?\n"); + rx = 1; + } else if (!status) { bh_printk(KERN_DEBUG "[BH] Device wakedown.\n"); WARN_ON(cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 0)); diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 1fe5e32a27a..f1ab481984b 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -27,10 +27,10 @@ #define wsm_printk(...) #endif -#define WSM_CMD_TIMEOUT (1 * HZ) -#define WSM_CMD_JOIN_TIMEOUT (7 * HZ) /* Join timeout is 5 sec. in FW */ +#define WSM_CMD_TIMEOUT (2 * HZ) /* With respect to interrupt loss */ +#define WSM_CMD_JOIN_TIMEOUT (7 * HZ) /* Join timeout is 5 sec. in FW */ #define WSM_CMD_START_TIMEOUT (7 * HZ) -#define WSM_TX_TIMEOUT (1 * HZ) +#define WSM_CMD_RESET_TIMEOUT (3 * HZ) /* 2 sec. timeout was observed. */ #define WSM_CMD_LAST_CHANCE_TIMEOUT (10 * HZ) #define WSM_SKIP(buf, size) \ @@ -184,7 +184,7 @@ int wsm_reset(struct cw1200_common *priv, const struct wsm_reset *arg) wsm_cmd_lock(priv); WSM_PUT32(buf, arg->reset_statistics ? 0 : 1); - ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_TIMEOUT); + ret = wsm_cmd_send(priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT); wsm_cmd_unlock(priv); return ret; -- cgit v1.2.3 From fb5c251a8122c59326278ea5baf3b5adc06b4f49 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Fri, 5 Aug 2011 15:17:47 +0200 Subject: cw1200: Allow TXing in monitor mode Forced-join firmware mode, previously used for offchannel TXing, has number of limitations and does not work well. The patch re-implements offchannel TXing in monitor aka p2p-dev mode. Change-Id: Id5327792ab78f8515ece575639b02ddb70d4a73f Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28492 Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28695 Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33503 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 1 + drivers/staging/cw1200/cw1200.h | 1 + drivers/staging/cw1200/main.c | 1 + drivers/staging/cw1200/scan.c | 37 +++++++++++++----- drivers/staging/cw1200/sta.c | 87 ++++++++++++++++++++++------------------- drivers/staging/cw1200/sta.h | 3 ++ drivers/staging/cw1200/wsm.c | 29 +++++++------- drivers/staging/cw1200/wsm.h | 2 +- 8 files changed, 97 insertions(+), 64 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 9a8e41f1e2a..362a5560116 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -351,6 +351,7 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, WARN_ON(wsm_set_bss_params(priv, &priv->bss_params)); WARN_ON(wsm_set_beacon_wakeup_period(priv, dtim_interval, listen_interval)); + WARN_ON(wsm_set_pm(priv, &priv->powersave_mode)); #if 0 /* It's better to override internal TX rete; otherwise * device sends RTS at too high rate. However device diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 067ad1ed5cd..1c5fb561492 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -157,6 +157,7 @@ struct cw1200_common { struct work_struct join_work; struct delayed_work join_timeout; struct work_struct unjoin_work; + struct work_struct offchannel_work; int join_dtim_period; bool delayed_unjoin; diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 74095a59d8a..cddb9943ee6 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -313,6 +313,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) INIT_WORK(&priv->join_work, cw1200_join_work); INIT_DELAYED_WORK(&priv->join_timeout, cw1200_join_timeout); INIT_WORK(&priv->unjoin_work, cw1200_unjoin_work); + INIT_WORK(&priv->offchannel_work, cw1200_offchannel_work); INIT_WORK(&priv->wep_key_work, cw1200_wep_key_work); INIT_WORK(&priv->tx_policy_upload_work, tx_policy_upload_work); INIT_LIST_HEAD(&priv->event_queue); diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index 3c80c571ea7..c663f6dc173 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -12,6 +12,7 @@ #include #include "cw1200.h" #include "scan.h" +#include "sta.h" static void cw1200_scan_restart_delayed(struct cw1200_common *priv); @@ -95,6 +96,9 @@ int cw1200_hw_scan(struct ieee80211_hw *hw, struct wsm_set_pm pm = priv->powersave_mode; pm.pmMode = WSM_PSM_PS; wsm_set_pm(priv, &pm); + } else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { + /* FW bug: driver has to restart p2p-dev mode after scan */ + cw1200_disable_listening(priv); } BUG_ON(priv->scan.req); @@ -242,6 +246,13 @@ static void cw1200_scan_restart_delayed(struct cw1200_common *priv) &priv->bss_loss_work, tmo * HZ / 10); } + + /* FW bug: driver has to restart p2p-dev mode after scan. */ + if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { + cw1200_enable_listening(priv); + cw1200_update_filtering(priv); + } + if (priv->delayed_unjoin) { priv->delayed_unjoin = false; if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) @@ -258,21 +269,21 @@ static void cw1200_scan_complete(struct cw1200_common *priv) up(&priv->scan.lock); wsm_unlock_tx(priv); } else { - queue_work(priv->workqueue, &priv->scan.work); + cw1200_scan_work(&priv->scan.work); } } void cw1200_scan_complete_cb(struct cw1200_common *priv, struct wsm_scan_complete *arg) { - if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) /* STA is stopped. */ return; - } - if (likely(atomic_xchg(&priv->scan.in_progress, 0))) { - cancel_delayed_work_sync(&priv->scan.timeout); - cw1200_scan_complete(priv); + if (cancel_delayed_work_sync(&priv->scan.timeout) > 0) { + priv->scan.status = 1; + queue_delayed_work(priv->workqueue, + &priv->scan.timeout, 0); } } @@ -281,9 +292,14 @@ void cw1200_scan_timeout(struct work_struct *work) struct cw1200_common *priv = container_of(work, struct cw1200_common, scan.timeout.work); if (likely(atomic_xchg(&priv->scan.in_progress, 0))) { - wiphy_warn(priv->hw->wiphy, - "Timeout waiting for scan " \ - "complete notification.\n"); + if (priv->scan.status > 0) + priv->scan.status = 0; + else if (!priv->scan.status) { + wiphy_warn(priv->hw->wiphy, + "Timeout waiting for scan " + "complete notification.\n"); + priv->scan.status = -ETIMEDOUT; + } cw1200_scan_complete(priv); } } @@ -367,6 +383,9 @@ void cw1200_probe_work(struct work_struct *work) } mutex_lock(&priv->conf_mutex); + /* FW bug: driver has to restart p2p-dev mode after scan */ + if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) + cw1200_disable_listening(priv); ret = WARN_ON(wsm_set_template_frame(priv, &frame)); priv->scan.direct_probe = 1; if (!ret) { diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index cd0284f3f05..17a4184a360 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -375,6 +375,13 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) .disableMoreFlagUsage = true, }; wsm_lock_tx(priv); + /* Disable p2p-dev mode forced by TX request */ + if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) && + (conf->flags & IEEE80211_CONF_IDLE) && + !priv->listening) { + cw1200_disable_listening(priv); + priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + } WARN_ON(wsm_set_operational_mode(priv, &mode)); wsm_unlock_tx(priv); } @@ -977,6 +984,23 @@ int cw1200_setup_mac(struct cw1200_common *priv) return 0; } +void cw1200_offchannel_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, offchannel_work); + + BUG_ON(!priv->channel); + + mutex_lock(&priv->conf_mutex); + if (!priv->join_status) { + wsm_flush_tx(priv); + cw1200_update_listening(priv, true); + cw1200_update_filtering(priv); + } + mutex_unlock(&priv->conf_mutex); + wsm_unlock_tx(priv); +} + void cw1200_join_work(struct work_struct *work) { struct cw1200_common *priv = @@ -985,18 +1009,15 @@ void cw1200_join_work(struct work_struct *work) const struct ieee80211_hdr *frame = (struct ieee80211_hdr *)&wsm[1]; const u8 *bssid = &frame->addr1[0]; /* AP SSID in a 802.11 frame */ struct cfg80211_bss *bss; - const u8 *ssidie = NULL; - const u8 *dtimie = NULL; + const u8 *ssidie; + const u8 *dtimie; const struct ieee80211_tim_ie *tim = NULL; u8 queueId; - bool action; BUG_ON(!wsm); BUG_ON(!priv->channel); queueId = wsm_queue_id_to_linux(wsm->queueId); - action = ieee80211_is_action(frame->frame_control) || - ieee80211_is_probe_resp(frame->frame_control); if (unlikely(priv->join_status)) { wsm_lock_tx(priv); @@ -1005,43 +1026,37 @@ void cw1200_join_work(struct work_struct *work) cancel_delayed_work_sync(&priv->join_timeout); + priv->join_pending_frame = NULL; bss = cfg80211_get_bss(priv->hw->wiphy, NULL, bssid, NULL, 0, 0, 0); - if (!bss && !action) { - priv->join_pending_frame = NULL; + if (!bss) { cw1200_queue_remove(&priv->tx_queue[queueId], priv, __le32_to_cpu(wsm->packetID)); wsm_unlock_tx(priv); return; - } else if (bss) { - ssidie = cfg80211_find_ie(WLAN_EID_SSID, - bss->information_elements, - bss->len_information_elements); - dtimie = cfg80211_find_ie(WLAN_EID_TIM, - bss->information_elements, - bss->len_information_elements); - if (dtimie) - tim = (struct ieee80211_tim_ie *)&dtimie[2]; } + ssidie = cfg80211_find_ie(WLAN_EID_SSID, + bss->information_elements, + bss->len_information_elements); + dtimie = cfg80211_find_ie(WLAN_EID_TIM, + bss->information_elements, + bss->len_information_elements); + if (dtimie) + tim = (struct ieee80211_tim_ie *)&dtimie[2]; mutex_lock(&priv->conf_mutex); { struct wsm_join join = { - .mode = WSM_JOIN_MODE_BSS, + .mode = (bss->capability & WLAN_CAPABILITY_IBSS) ? + WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS, .preambleType = WSM_JOIN_PREAMBLE_SHORT, .probeForJoin = 1, /* dtimPeriod will be updated after association */ .dtimPeriod = 1, - .beaconInterval = 100, + .beaconInterval = bss->beacon_interval, /* basicRateSet will be updated after association */ .basicRateSet = 7, }; - if (bss) { - join.mode = (bss->capability & WLAN_CAPABILITY_IBSS) ? - WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS; - join.beaconInterval = bss->beacon_interval; - } - if (tim && tim->dtim_period > 1) { join.dtimPeriod = tim->dtim_period; priv->join_dtim_period = tim->dtim_period; @@ -1049,17 +1064,6 @@ void cw1200_join_work(struct work_struct *work) join.dtimPeriod); } - if (action) { - join.probeForJoin = 0; - join.flags |= WSM_JOIN_FLAGS_UNSYNCRONIZED | - WSM_JOIN_FLAGS_FORCE; - } else { - WARN_ON(wsm_set_block_ack_policy(priv, - priv->ba_tid_mask, priv->ba_tid_mask)); - } - - priv->join_pending_frame = NULL; - join.channelNumber = priv->channel->hw_value; join.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; @@ -1076,6 +1080,9 @@ void cw1200_join_work(struct work_struct *work) wsm_flush_tx(priv); + WARN_ON(wsm_set_block_ack_policy(priv, + priv->ba_tid_mask, priv->ba_tid_mask)); + /* Queue unjoin if not associated in 3 sec. */ queue_delayed_work(priv->workqueue, &priv->join_timeout, 3 * HZ); @@ -1088,19 +1095,19 @@ void cw1200_join_work(struct work_struct *work) priv, __le32_to_cpu(wsm->packetID)); cancel_delayed_work_sync(&priv->join_timeout); cw1200_update_listening(priv, priv->listening); - WARN_ON(wsm_set_pm(priv, &priv->powersave_mode)); } else { /* Upload keys */ WARN_ON(cw1200_upload_keys(priv)); WARN_ON(wsm_keep_alive_period(priv, 30 /* sec */)); cw1200_queue_requeue(&priv->tx_queue[queueId], __le32_to_cpu(wsm->packetID)); + priv->join_status = CW1200_JOIN_STATUS_STA; } cw1200_update_filtering(priv); } mutex_unlock(&priv->conf_mutex); - if (bss) - cfg80211_put_bss(bss); + cfg80211_put_bss(bss); + wsm_unlock_tx(priv); } void cw1200_join_timeout(struct work_struct *work) @@ -1162,7 +1169,7 @@ void cw1200_unjoin_work(struct work_struct *work) wsm_unlock_tx(priv); } -static inline int cw1200_enable_listening(struct cw1200_common *priv) +int cw1200_enable_listening(struct cw1200_common *priv) { struct wsm_start start = { .mode = WSM_START_MODE_P2P_DEV, @@ -1177,7 +1184,7 @@ static inline int cw1200_enable_listening(struct cw1200_common *priv) return wsm_start(priv, &start); } -static inline int cw1200_disable_listening(struct cw1200_common *priv) +int cw1200_disable_listening(struct cw1200_common *priv) { int ret; struct wsm_reset reset = { diff --git a/drivers/staging/cw1200/sta.h b/drivers/staging/cw1200/sta.h index 8d0cc5d4c12..88c1cf6a34b 100644 --- a/drivers/staging/cw1200/sta.h +++ b/drivers/staging/cw1200/sta.h @@ -69,9 +69,12 @@ int cw1200_setup_mac(struct cw1200_common *priv); void cw1200_join_work(struct work_struct *work); void cw1200_join_timeout(struct work_struct *work); void cw1200_unjoin_work(struct work_struct *work); +void cw1200_offchannel_work(struct work_struct *work); void cw1200_wep_key_work(struct work_struct *work); void cw1200_update_listening(struct cw1200_common *priv, bool enabled); void cw1200_update_filtering(struct cw1200_common *priv); int __cw1200_flush(struct cw1200_common *priv, bool drop); +int cw1200_enable_listening(struct cw1200_common *priv); +int cw1200_disable_listening(struct cw1200_common *priv); #endif diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index f1ab481984b..970b3cd9405 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -423,22 +423,16 @@ static int wsm_join_confirm(struct cw1200_common *priv, struct wsm_buf *buf) { if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS)) { - priv->join_status = CW1200_JOIN_STATUS_PASSIVE; - wsm_unlock_tx(priv); return -EINVAL; } arg->minPowerLevel = WSM_GET32(buf); arg->maxPowerLevel = WSM_GET32(buf); - priv->join_status = CW1200_JOIN_STATUS_STA; - wsm_unlock_tx(priv); return 0; underflow: WARN_ON(1); - priv->join_status = CW1200_JOIN_STATUS_PASSIVE; - wsm_unlock_tx(priv); return -EINVAL; } @@ -1249,6 +1243,7 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, doProbe, doDrop, doJoin, + doOffchannel, doWep, doTx, } action = doTx; @@ -1263,14 +1258,11 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, action = doJoin; else if (ieee80211_is_probe_req(fctl)) action = doTx; - else if (ieee80211_is_action(fctl) || - ieee80211_is_probe_resp(fctl)) { - if (priv->join_status > CW1200_JOIN_STATUS_MONITOR) - action = doTx; - else - action = doJoin; - } else - action = doDrop; + else if (priv->join_status >= + CW1200_JOIN_STATUS_MONITOR) + action = doTx; + else + action = doOffchannel; } break; case NL80211_IFTYPE_AP: @@ -1364,6 +1356,15 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, handled = true; } break; + case doOffchannel: + { + wsm_printk(KERN_DEBUG "[WSM] Offchannel TX request.\n"); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, &priv->offchannel_work) <= 0) + wsm_unlock_tx(priv); + handled = true; + } + break; case doWep: { wsm_printk(KERN_DEBUG "[WSM] Issue set_default_wep_key.\n"); diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 66534b940aa..542eededffc 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -1470,7 +1470,7 @@ static inline int wsm_keep_alive_period(struct cw1200_common *priv, int period) { struct wsm_keep_alive_period arg = { - .keepAlivePeriod = period, + .keepAlivePeriod = __cpu_to_le16(period), }; return wsm_write_mib(priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD, &arg, sizeof(arg)); -- cgit v1.2.3 From 44d89245e7506a8039fbd4be5aa52ef4fc320847 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 11 Aug 2011 10:45:34 +0200 Subject: cw1200: Syncing join status by wsm_tx_lock(). All operations changing join_status should be executed with wsm_tx_lock() held. Change-Id: Iadea249b080f1d7c090868c527ff78eecb53a26a Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28595 Tested-by: Janusz DZIEDZIC Reviewed-by: Janusz DZIEDZIC Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28697 Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33505 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 4 ++++ drivers/staging/cw1200/sta.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 362a5560116..6fe254802c0 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -76,7 +76,9 @@ int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, reset.link_id = sta_priv->link_id; priv->link_id_map &= ~BIT(sta_priv->link_id); sta_priv->link_id = 0; + wsm_lock_tx(priv); WARN_ON(wsm_reset(priv, &reset)); + wsm_unlock_tx(priv); } return 0; } @@ -676,10 +678,12 @@ static int cw1200_update_beaconing(struct cw1200_common *priv) if (priv->join_status != CW1200_JOIN_STATUS_AP || priv->beacon_int != conf->beacon_int) { ap_printk(KERN_DEBUG "ap restarting\n"); + wsm_lock_tx(priv); if (priv->join_status != CW1200_JOIN_STATUS_PASSIVE) WARN_ON(wsm_reset(priv, &reset)); priv->join_status = CW1200_JOIN_STATUS_PASSIVE; WARN_ON(cw1200_start_ap(priv)); + wsm_unlock_tx(priv); } else ap_printk(KERN_DEBUG "ap started join_status: %d\n", priv->join_status); diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 17a4184a360..69456066e4c 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -230,11 +230,13 @@ void cw1200_remove_interface(struct ieee80211_hw *dev, priv->mode = NL80211_IFTYPE_MONITOR; memset(priv->mac_addr, 0, ETH_ALEN); memset(priv->bssid, 0, ETH_ALEN); + wsm_lock_tx(priv); WARN_ON(wsm_reset(priv, &reset)); cw1200_free_keys(priv); cw1200_setup_mac(priv); priv->listening = false; priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + wsm_unlock_tx(priv); mutex_unlock(&priv->conf_mutex); } @@ -489,7 +491,9 @@ void cw1200_configure_filter(struct ieee80211_hw *dev, FIF_PROBE_REQ)) ? 1 : 0; if (priv->listening ^ listening) { priv->listening = listening; + wsm_lock_tx(priv); cw1200_update_listening(priv, listening); + wsm_unlock_tx(priv); } cw1200_update_filtering(priv); mutex_unlock(&priv->conf_mutex); -- cgit v1.2.3 From 47733d45293f98d081b47e3fcb628dd993427e3c Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Fri, 12 Aug 2011 00:23:29 +0200 Subject: cw1200: PSPOLL is supported. Handling of PSPOLL frames in SoftAP mode was missing until now. This commit implements PSPOLL support. Change-Id: I9b18554ee8fca296793af4db168c458276d45f82 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28849 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29060 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33506 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 3 ++- drivers/staging/cw1200/cw1200.h | 1 + drivers/staging/cw1200/debug.c | 2 ++ drivers/staging/cw1200/sta.c | 1 + drivers/staging/cw1200/txrx.c | 57 ++++++++++++++++++++++++++++++++++++++--- drivers/staging/cw1200/wsm.c | 2 ++ 6 files changed, 61 insertions(+), 5 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 6fe254802c0..28cb11faa35 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -120,7 +120,8 @@ static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set) }; u16 tim_offset, tim_length; - ap_printk(KERN_DEBUG "[AP] %s.\n", __func__); + ap_printk(KERN_DEBUG "[AP] %s mcast: %s.\n", + __func__, aid0_bit_set ? "ena" : "dis"); frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, &tim_offset, &tim_length); diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 1c5fb561492..9bb68541035 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -172,6 +172,7 @@ struct cw1200_common { u32 link_id_map; u32 tx_suspend_mask[4]; u32 sta_asleep_mask; + u32 pspoll_mask; bool aid0_bit_set; spinlock_t buffered_multicasts_lock; bool buffered_multicasts; diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 612c74a7174..c918d3f8291 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -217,6 +217,8 @@ static int cw1200_status_show(struct seq_file *seq, void *v) priv->link_id_map); cw1200_debug_print_map(seq, priv, "Asleep map: ", priv->sta_asleep_mask); + cw1200_debug_print_map(seq, priv, "PSPOLL map: ", + priv->pspoll_mask); seq_puts(seq, "\n"); diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 69456066e4c..b3896ac3fab 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -136,6 +136,7 @@ void cw1200_stop(struct ieee80211_hw *dev) priv->tx_multicast = false; priv->aid0_bit_set = false; priv->buffered_multicasts = false; + priv->pspoll_mask = 0; wsm_reset(priv, &reset); break; case CW1200_JOIN_STATUS_MONITOR: diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 6cae1a046d6..5201ffe65ab 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -425,12 +425,12 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) int link_id = 0; int ret; - if ((tx_info->flags | IEEE80211_TX_CTL_SEND_AFTER_DTIM) && + if (tx_info->control.sta) + link_id = sta_priv->link_id; + else if ((tx_info->flags | IEEE80211_TX_CTL_SEND_AFTER_DTIM) && (priv->mode == NL80211_IFTYPE_AP) && priv->enable_beacon) link_id = CW1200_LINK_ID_AFTER_DTIM; - else if (tx_info->control.sta) - link_id = sta_priv->link_id; txrx_printk(KERN_DEBUG "[TX] TX %d bytes (queue: %d, link_id: %d).\n", skb->len, queue, link_id); @@ -568,6 +568,50 @@ static int cw1200_handle_action_tx(struct cw1200_common *priv, return 0; } +static int cw1200_handle_pspoll(struct cw1200_common *priv, + struct sk_buff *skb) +{ + struct ieee80211_sta *sta; + struct cw1200_sta_priv *sta_priv; + struct ieee80211_pspoll *pspoll = + (struct ieee80211_pspoll *) skb->data; + u32 pspoll_mask; + int drop = 1; + int i; + + if (priv->join_status != CW1200_JOIN_STATUS_AP) + goto done; + if (memcmp(priv->vif->addr, pspoll->bssid, ETH_ALEN)) + goto done; + + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, pspoll->ta); + if (!sta) { + rcu_read_unlock(); + goto done; + } + sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; + pspoll_mask = BIT(sta_priv->link_id); + rcu_read_unlock(); + + priv->pspoll_mask |= pspoll_mask; + drop = 0; + + /* Do not report pspols if data for given link id is + * queued already. */ + for (i = 0; i < 4; ++i) { + if (cw1200_queue_get_num_queued( + &priv->tx_queue[i], + pspoll_mask)) { + cw1200_bh_wakeup(priv); + drop = 1; + break; + } + } +done: + return drop; +} + /* ******************************************************************** */ void cw1200_tx_confirm_cb(struct cw1200_common *priv, @@ -697,13 +741,18 @@ void cw1200_rx_cb(struct cw1200_common *priv, } } - if (skb->len < sizeof(struct ieee80211_hdr_3addr)) { + if (skb->len < sizeof(struct ieee80211_pspoll)) { wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. " "Size is lesser than IEEE header.\n"); goto drop; } frame_control = *(__le16*)skb->data; + + if (unlikely(ieee80211_is_pspoll(frame_control))) + if (cw1200_handle_pspoll(priv, skb)) + goto drop; + hdr->mactime = 0; /* Not supported by WSM */ hdr->band = (arg->channelNumber > 14) ? IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 970b3cd9405..57bfa47efcd 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1450,6 +1450,7 @@ static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv, tx_allowed_mask = ~priv->sta_asleep_mask; if (priv->sta_asleep_mask) { tx_allowed_mask |= ~priv->tx_suspend_mask[i]; + tx_allowed_mask |= priv->pspoll_mask; tx_allowed_mask &= ~BIT(CW1200_LINK_ID_AFTER_DTIM); } else { tx_allowed_mask |= BIT(CW1200_LINK_ID_AFTER_DTIM); @@ -1537,6 +1538,7 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX)); wsm->hdr.id |= cpu_to_le16( WSM_TX_LINK_ID(sta_priv->link_id)); + priv->pspoll_mask &= ~BIT(sta_priv->link_id); } *data = (u8 *)wsm; -- cgit v1.2.3 From 13392772297ce2af0e5b6fc1a6b29fdba403498a Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Tue, 16 Aug 2011 10:01:18 +0200 Subject: cw1200: Cancel suspend on late interrupts. * Code added to detect late (after suspend) interrupts and cancel suspend in that case. Applicable only for GPIO IRQs. Similar modification is required in the MMC/SDIO driver to handle late SDIO IRQs properly. * Code added to hold wakelock for 1 sec. when frame is sent from the driver up to userspace. Change-Id: Ia739c243164a6d35602dc9f634d3990214560eb9 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28850 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29061 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33507 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/cw1200.h | 4 +- drivers/staging/cw1200/main.c | 2 + drivers/staging/cw1200/pm.c | 119 +++++++++++++++++++++++++++++++++++++++- drivers/staging/cw1200/pm.h | 27 ++++++++- drivers/staging/cw1200/txrx.c | 10 ++++ 5 files changed, 156 insertions(+), 6 deletions(-) diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 9bb68541035..80eb3c49f81 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -29,11 +29,11 @@ #include "scan.h" #include "txrx.h" #include "ht.h" +#include "pm.h" /* extern */ struct sbus_ops; /* extern */ struct task_struct; /* extern */ struct cw1200_debug_priv; -/* extern */ struct cw1200_suspend_state; /* extern */ struct firmware; #if defined(CONFIG_CW1200_TXRX_DEBUG) @@ -116,7 +116,7 @@ struct cw1200_common { struct wsm_beacon_filter_control bf_control; u8 ba_tid_mask; struct wsm_multicast_filter multicast_filter; - struct cw1200_suspend_state *suspend_state; + struct cw1200_pm_state pm_state; struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo; /* BH */ diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index cddb9943ee6..8ba8629cb03 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -349,6 +349,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) init_waitqueue_head(&priv->wsm_startup_done); wsm_buf_init(&priv->wsm_cmd_buf); tx_policy_init(priv); + cw1200_pm_init(&priv->pm_state, priv); return hw; } @@ -429,6 +430,7 @@ void cw1200_unregister_common(struct ieee80211_hw *dev) for (i = 0; i < 4; ++i) cw1200_queue_deinit(&priv->tx_queue[i]); cw1200_queue_stats_deinit(&priv->tx_queue_stats); + cw1200_pm_deinit(&priv->pm_state); } EXPORT_SYMBOL_GPL(cw1200_unregister_common); diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c index 72f11e09d50..e27c4144f51 100644 --- a/drivers/staging/cw1200/pm.c +++ b/drivers/staging/cw1200/pm.c @@ -9,12 +9,15 @@ * published by the Free Software Foundation. */ +#include #include "cw1200.h" #include "pm.h" #include "sta.h" #include "bh.h" #include "sbus.h" +static int cw1200_suspend_late(struct device *dev); + /* private */ struct cw1200_suspend_state { unsigned long bss_loss_tmo; @@ -22,6 +25,94 @@ struct cw1200_suspend_state { unsigned long join_tmo; }; +static struct dev_pm_ops cw1200_pm_ops = { + .suspend_noirq = cw1200_suspend_late, +}; +static struct platform_driver cw1200_power_driver = { + .driver.name = "cw1200_power", + .driver.pm = &cw1200_pm_ops, +}; +static struct platform_device cw1200_power_device = { + .name = "cw1200_power", +}; + +static void cw1200_pm_init_common(struct cw1200_pm_state *pm, + struct cw1200_common *priv) +{ + spin_lock_init(&pm->lock); + cw1200_power_device.dev.platform_data = priv; + platform_device_register(&cw1200_power_device); + platform_driver_register(&cw1200_power_driver); +} + +static void cw1200_pm_deinit_common(struct cw1200_pm_state *pm) +{ + platform_driver_unregister(&cw1200_power_driver); + platform_device_unregister(&cw1200_power_device); +} + +#ifdef CONFIG_WAKELOCK + +void cw1200_pm_init(struct cw1200_pm_state *pm, + struct cw1200_common *priv) +{ + cw1200_pm_init_common(pm, priv); + wake_lock_init(&pm->wakelock, + WAKE_LOCK_SUSPEND, "cw1200_wlan"); +} + +void cw1200_pm_deinit(struct cw1200_pm_state *pm) +{ + if (wake_lock_active(&pm->wakelock)) + wake_unlock(&pm->wakelock); + wake_lock_destroy(&pm->wakelock); + cw1200_pm_deinit_common(pm); +} +void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, + unsigned long tmo) +{ + long cur_tmo; + spin_lock_bh(&pm->lock); + cur_tmo = pm->wakelock.expires - jiffies; + if (!wake_lock_active(&pm->wakelock) || + cur_tmo < (long)tmo) + wake_lock_timeout(&pm->wakelock, tmo); + spin_unlock_bh(&pm->lock); +} + +#else /* CONFIG_WAKELOCK */ + +static void cw1200_pm_stay_awake_tmo(unsigned long) +{ +} + +void cw1200_pm_init(struct cw1200_pm_state *pm) +{ + cw1200_init_common(pm); + init_timer(&pm->stay_awake); + pm->stay_awake.data = (unsigned long)pm; + pm->stay_awake.function = cw1200_pm_stay_awake_tmo; +} + +void cw1200_pm_deinit(struct cw1200_pm_state *pm) +{ + del_timer_sync(&pm->stay_awake); +} + +void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, + unsigned long tmo) +{ + long cur_tmo; + spin_lock_bh(&pm->lock); + cur_tmo = pm->stay_awake.expires - jiffies; + if (!timer_pending(&pm->stay_awake) || + cur_tmo < (long)tmo) + mod_timer(&pm->stay_awake, jiffies + tmo); + spin_unlock_bh(&pm->lock); +} + +#endif /* CONFIG_WAKELOCK */ + static long cw1200_suspend_work(struct delayed_work *work) { int ret = cancel_delayed_work(work); @@ -47,12 +138,33 @@ static int cw1200_resume_work(struct cw1200_common *priv, return queue_delayed_work(priv->workqueue, work, tmo); } +static int cw1200_suspend_late(struct device *dev) +{ + struct cw1200_common *priv = dev->platform_data; + if (atomic_read(&priv->bh_rx)) { + wiphy_dbg(priv->hw->wiphy, + "%s: Suspend interrupted.\n", + __func__); + return -EAGAIN; + } + return 0; +} + int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { struct cw1200_common *priv = hw->priv; + struct cw1200_pm_state *pm_state = &priv->pm_state; struct cw1200_suspend_state *state; int ret; +#ifndef CONFIG_WAKELOCK + spin_lock_bh(&pm->lock); + ret = timer_pending(&pm->stay_awake); + spin_unlock_bh(&pm->lock); + if (ret) + return -EAGAIN; +#endif + /* Ensure pending operations are done. */ ret = wait_event_interruptible_timeout( priv->channel_switch_done, @@ -89,7 +201,7 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) cw1200_bh_suspend(priv); /* Store suspend state */ - priv->suspend_state = state; + pm_state->suspend_state = state; /* Enable IRQ wake */ ret = priv->sbus_ops->power_mgmt(priv->sbus_priv, true); @@ -113,10 +225,11 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) int cw1200_wow_resume(struct ieee80211_hw *hw) { struct cw1200_common *priv = hw->priv; + struct cw1200_pm_state *pm_state = &priv->pm_state; struct cw1200_suspend_state *state; - state = priv->suspend_state; - priv->suspend_state = NULL; + state = pm_state->suspend_state; + pm_state->suspend_state = NULL; /* Disable IRQ wake */ priv->sbus_ops->power_mgmt(priv->sbus_priv, false); diff --git a/drivers/staging/cw1200/pm.h b/drivers/staging/cw1200/pm.h index 841b609457f..433e5ec1139 100644 --- a/drivers/staging/cw1200/pm.h +++ b/drivers/staging/cw1200/pm.h @@ -12,12 +12,37 @@ #ifndef PM_H_INCLUDED #define PM_H_INCLUDED +#ifdef CONFIG_WAKELOCK +#include +#endif + /* ******************************************************************** */ /* mac80211 API */ #ifdef CONFIG_PM -int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); + +/* extern */ struct cw1200_common; +/* private */ struct cw1200_suspend_state; + +struct cw1200_pm_state { + struct cw1200_suspend_state *suspend_state; +#ifdef CONFIG_WAKELOCK + struct wake_lock wakelock; +#else + struct timer_list stay_awake; +#endif + spinlock_t lock; +}; + +void cw1200_pm_init(struct cw1200_pm_state *pm, + struct cw1200_common *priv); +void cw1200_pm_deinit(struct cw1200_pm_state *pm); +void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, + unsigned long tmo); +int cw1200_wow_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan); int cw1200_wow_resume(struct ieee80211_hw *hw); + #endif /* CONFIG_PM */ #endif diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 5201ffe65ab..18544011b16 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -719,6 +719,7 @@ void cw1200_rx_cb(struct cw1200_common *priv, { struct sk_buff *skb = *skb_p; struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); + unsigned long grace_period; __le16 frame_control; hdr->flag = 0; @@ -825,6 +826,15 @@ void cw1200_rx_cb(struct cw1200_common *priv, if (cw1200_handle_action_rx(priv, skb)) return; + /* Stay awake for 1sec. after frame is received to give + * userspace chance to react and acquire appropriate + * wakelock. */ + if (ieee80211_is_auth(frame_control)) + grace_period = 5 * HZ; + else + grace_period = 1 * HZ; + cw1200_pm_stay_awake(&priv->pm_state, grace_period); + /* Not that we really need _irqsafe variant here, * but it offloads realtime bh thread and improve * system performance. */ -- cgit v1.2.3 From bc08cc1d5cebd42f47c95fd064c3c26fdfa2e9c6 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 17 Aug 2011 11:48:00 +0200 Subject: cw1200: Print firmware exception information. WSM firmware reports exception information in exception indication message. Code added for printing this informarion in human-readable format. Change-Id: I1bbd208521166021fd7d38229b8dbeff8490d244 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28957 Reviewed-by: Ajit Pal SINGH Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29062 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33508 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/wsm.c | 91 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 71 insertions(+), 20 deletions(-) diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 57bfa47efcd..16ebfe658fe 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -35,26 +35,26 @@ #define WSM_SKIP(buf, size) \ do { \ - if (unlikely(buf->data + size > buf->end)) \ + if (unlikely((buf)->data + size > (buf)->end)) \ goto underflow; \ - buf->data += size; \ + (buf)->data += size; \ } while (0) #define WSM_GET(buf, ptr, size) \ do { \ - if (unlikely(buf->data + size > buf->end)) \ + if (unlikely((buf)->data + size > (buf)->end)) \ goto underflow; \ - memcpy(ptr, buf->data, size); \ - buf->data += size; \ + memcpy(ptr, (buf)->data, size); \ + (buf)->data += size; \ } while (0) #define __WSM_GET(buf, type, cvt) \ ({ \ type val; \ - if (unlikely(buf->data + sizeof(type) > buf->end)) \ + if (unlikely((buf)->data + sizeof(type) > (buf)->end)) \ goto underflow; \ - val = cvt(*(type *)buf->data); \ - buf->data += sizeof(type); \ + val = cvt(*(type *)(buf)->data); \ + (buf)->data += sizeof(type); \ val; \ }) @@ -64,20 +64,20 @@ #define WSM_PUT(buf, ptr, size) \ do { \ - if (unlikely(buf->data + size > buf->end)) \ - if (unlikely(wsm_buf_reserve(buf, size))) \ + if (unlikely((buf)->data + size > (buf)->end)) \ + if (unlikely(wsm_buf_reserve((buf), size))) \ goto nomem; \ - memcpy(buf->data, ptr, size); \ - buf->data += size; \ + memcpy((buf)->data, ptr, size); \ + (buf)->data += size; \ } while (0) -#define __WSM_PUT(buf, val, type, cvt) \ - do { \ - if (unlikely(buf->data + sizeof(type) > buf->end)) \ - if (unlikely(wsm_buf_reserve(buf, sizeof(type)))) \ - goto nomem; \ - *(type *)buf->data = cvt(val); \ - buf->data += sizeof(type); \ +#define __WSM_PUT(buf, val, type, cvt) \ + do { \ + if (unlikely((buf)->data + sizeof(type) > (buf)->end)) \ + if (unlikely(wsm_buf_reserve((buf), sizeof(type)))) \ + goto nomem; \ + *(type *)(buf)->data = cvt(val); \ + (buf)->data += sizeof(type); \ } while (0) #define WSM_PUT8(buf, val) __WSM_PUT(buf, val, u8, (u8)) @@ -1094,8 +1094,59 @@ void wsm_unlock_tx(struct cw1200_common *priv) int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len) { - STUB(); + struct wsm_buf buf; + u32 reason; + u32 reg[18]; + char fname[48]; + size_t i; + + static const char *reason_str[] = { + "undefined instruction", + "prefetch abort", + "data abort", + "unknown error", + }; + + buf.begin = buf.data = data; + buf.end = &buf.begin[len]; + + reason = WSM_GET32(&buf); + for (i = 0; i < ARRAY_SIZE(reg); ++i) + reg[i] = WSM_GET32(&buf); + WSM_GET(&buf, fname, sizeof(fname)); + + if (reason < 4) + wiphy_err(priv->hw->wiphy, + "Firmware exception: %s.\n", + reason_str[reason]); + else + wiphy_err(priv->hw->wiphy, + "Firmware assert at %.*s, line %d\n", + sizeof(fname), fname, reg[0]); + + for (i = 0; i < 12; i += 4) + wiphy_err(priv->hw->wiphy, + "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n", + i + 0, reg[i + 0], i + 1, reg[i + 1], + i + 2, reg[i + 2], i + 3, reg[i + 3]); + wiphy_err(priv->hw->wiphy, + "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n", + reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]); + i += 4; + wiphy_err(priv->hw->wiphy, + "CPSR: 0x%.8X, SPSR: 0x%.8X\n", + reg[i + 0], reg[i + 1]); + + print_hex_dump_bytes("R1: ", DUMP_PREFIX_NONE, + fname, sizeof(fname)); return 0; + +underflow: + wiphy_err(priv->hw->wiphy, + "Firmware exception.\n"); + print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE, + data, len); + return -EINVAL; } int wsm_handle_rx(struct cw1200_common *priv, int id, -- cgit v1.2.3 From 9746b8925b3f49340529d80e0f71235be3e40790 Mon Sep 17 00:00:00 2001 From: Amit Date: Thu, 11 Aug 2011 19:33:19 +0530 Subject: cw1200: Add changes for UAPSD * Added OID configuration for UAPSD configuration and related data structure updates * Added code to disable FastPS at FW in driver when UAPSD is enabled. * Restructured the code Change-Id: Ia64533960698646494268599c7f8f9aea57e4d88 Signed-off-by: Amit Shakya Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29063 Reviewed-by: Dmitry TARNYAGIN Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33509 Tested-by: Dmitry TARNYAGIN Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 4 ++- drivers/staging/cw1200/cw1200.h | 2 ++ drivers/staging/cw1200/main.c | 2 +- drivers/staging/cw1200/sta.c | 76 +++++++++++++++++++++++++++++++++++++---- drivers/staging/cw1200/sta.h | 4 +++ drivers/staging/cw1200/wsm.h | 28 ++++++++++++++- 6 files changed, 107 insertions(+), 9 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 28cb11faa35..2a6d27f212f 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -352,9 +352,10 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, WARN_ON(wsm_set_association_mode(priv, &priv->association_mode)); WARN_ON(wsm_set_bss_params(priv, &priv->bss_params)); + priv->setbssparams_done = true; WARN_ON(wsm_set_beacon_wakeup_period(priv, dtim_interval, listen_interval)); - WARN_ON(wsm_set_pm(priv, &priv->powersave_mode)); + cw1200_set_pm(priv, &priv->powersave_mode); #if 0 /* It's better to override internal TX rete; otherwise * device sends RTS at too high rate. However device @@ -460,6 +461,7 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, priv->cqm_beacon_loss_count : priv->cqm_link_loss_count; WARN_ON(wsm_set_bss_params(priv, &priv->bss_params)); + priv->setbssparams_done = true; } #endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ } diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 80eb3c49f81..e4fda896b78 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -118,6 +118,8 @@ struct cw1200_common { struct wsm_multicast_filter multicast_filter; struct cw1200_pm_state pm_state; struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo; + struct wsm_uapsd_info uapsd_info; + bool setbssparams_done; /* BH */ atomic_t bh_rx; diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 8ba8629cb03..e9e6806e811 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -251,7 +251,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | - /* IEEE80211_HW_SUPPORTS_UAPSD | */ + IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_CQM_RSSI | /* Aggregation is fully controlled by firmware. diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index b3896ac3fab..2bbedd2971a 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -50,14 +50,20 @@ int cw1200_start(struct ieee80211_hw *dev) mutex_lock(&priv->conf_mutex); /* default ECDA */ - WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47); - WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94); - WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0); - WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0); + WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, false); + WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, false); + WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, false); + WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, false); ret = wsm_set_edca_params(priv, &priv->edca); if (WARN_ON(ret)) goto out; + ret = cw1200_set_uapsd_param(priv, &priv->edca); + if (WARN_ON(ret)) + goto out; + + priv->setbssparams_done = false; + memset(priv->bssid, ~0, ETH_ALEN); memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN); priv->mode = NL80211_IFTYPE_MONITOR; @@ -315,7 +321,7 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) conf->dynamic_ps_timeout << 1; if (priv->join_status == CW1200_JOIN_STATUS_STA) - WARN_ON(wsm_set_pm(priv, &priv->powersave_mode)); + cw1200_set_pm(priv, &priv->powersave_mode); } if (changed & IEEE80211_CONF_CHANGE_P2P_PS) { @@ -505,13 +511,26 @@ int cw1200_conf_tx(struct ieee80211_hw *dev, u16 queue, { struct cw1200_common *priv = dev->priv; int ret = 0; + /* To prevent re-applying PM request OID again and again*/ + bool old_uapsdFlags; mutex_lock(&priv->conf_mutex); if (queue < dev->queues) { + old_uapsdFlags = priv->uapsd_info.uapsdFlags; + WSM_EDCA_SET(&priv->edca, queue, params->aifs, - params->cw_min, params->cw_max, params->txop); + params->cw_min, params->cw_max, params->txop, + params->uapsd); ret = wsm_set_edca_params(priv, &priv->edca); + + if (!ret && (priv->mode == NL80211_IFTYPE_STATION)) { + ret = cw1200_set_uapsd_param(priv, &priv->edca); + if (!ret && priv->setbssparams_done && + (priv->join_status == CW1200_JOIN_STATUS_STA) && + (old_uapsdFlags != priv->uapsd_info.uapsdFlags)) + cw1200_set_pm(priv, &priv->powersave_mode); + } } else ret = -EINVAL; @@ -542,6 +561,16 @@ int cw1200_get_tx_stats(struct ieee80211_hw *dev, } */ +int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) +{ + struct wsm_set_pm pm = *arg; + + if (priv->uapsd_info.uapsdFlags != 0) + pm.pmMode &= ~WSM_PSM_FAST_PS_FLAG; + + return wsm_set_pm(priv, &pm); +} + int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) @@ -1168,6 +1197,8 @@ void cw1200_unjoin_work(struct work_struct *work) cancel_delayed_work_sync(&priv->connection_loss_work); cw1200_update_listening(priv, priv->listening); cw1200_update_filtering(priv); + priv->setbssparams_done = false; + sta_printk(KERN_DEBUG "[STA] Unjoin.\n"); } mutex_unlock(&priv->conf_mutex); @@ -1221,6 +1252,39 @@ void cw1200_update_listening(struct cw1200_common *priv, bool enabled) } } +int cw1200_set_uapsd_param(struct cw1200_common *priv, + const struct wsm_edca_params *arg) +{ + int ret; + u16 uapsdFlags = 0; + + /* Here's the mapping AC [queue, bit] + VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0]*/ + + if (arg->params[0].uapsdEnable) + uapsdFlags |= 1 << 3; + + if (arg->params[1].uapsdEnable) + uapsdFlags |= 1 << 2; + + if (arg->params[2].uapsdEnable) + uapsdFlags |= 1 << 1; + + if (arg->params[3].uapsdEnable) + uapsdFlags |= 1; + + /* Currently pseudo U-APSD operation is not supported, so setting + * MinAutoTriggerInterval, MaxAutoTriggerInterval and + * AutoTriggerStep to 0 */ + + priv->uapsd_info.uapsdFlags = cpu_to_le16(uapsdFlags); + priv->uapsd_info.minAutoTriggerInterval = 0; + priv->uapsd_info.maxAutoTriggerInterval = 0; + priv->uapsd_info.autoTriggerStep = 0; + + ret = wsm_set_uapsd_info(priv, &priv->uapsd_info); + return ret; +} /* ******************************************************************** */ /* STA privates */ diff --git a/drivers/staging/cw1200/sta.h b/drivers/staging/cw1200/sta.h index 88c1cf6a34b..88a08aaceaf 100644 --- a/drivers/staging/cw1200/sta.h +++ b/drivers/staging/cw1200/sta.h @@ -45,6 +45,8 @@ void cw1200_flush(struct ieee80211_hw *hw, bool drop); u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list); +int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg); + /* ******************************************************************** */ /* WSM callbacks */ @@ -76,5 +78,7 @@ void cw1200_update_filtering(struct cw1200_common *priv); int __cw1200_flush(struct cw1200_common *priv, bool drop); int cw1200_enable_listening(struct cw1200_common *priv); int cw1200_disable_listening(struct cw1200_common *priv); +int cw1200_set_uapsd_param(struct cw1200_common *priv, + const struct wsm_edca_params *arg); #endif diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 542eededffc..30db888e535 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -135,6 +135,9 @@ struct cw1200_common; /* 802.11 PS mode */ #define WSM_PSM_PS BIT(0) +/* Fast Power Save bit */ +#define WSM_PSM_FAST_PS_FLAG BIT(7) + /* Dynamic aka Fast power save */ #define WSM_PSM_FAST_PS (BIT(0) | BIT(7)) @@ -1044,6 +1047,9 @@ struct wsm_edca_queue_params { /* the access class. Overrides the global */ /* dot11MaxReceiveLifetime value */ /* [in] */ u32 maxReceiveLifetime; + + /* UAPSD trigger support for the access class. */ + /* [in] */ bool uapsdEnable; }; struct wsm_edca_params { @@ -1051,18 +1057,22 @@ struct wsm_edca_params { struct wsm_edca_queue_params params[4]; }; -#define WSM_EDCA_SET(edca, queue, aifs, cw_min, cw_max, txop) \ +#define WSM_EDCA_SET(edca, queue, aifs, cw_min, cw_max, txop, uapsd) \ do { \ struct wsm_edca_queue_params *p = &(edca)->params[queue]; \ p->cwMin = (cw_min); \ p->cwMax = (cw_max); \ p->aifns = (aifs); \ p->txOpLimit = (txop); \ + p->uapsdEnable = (uapsd); \ } while (0) int wsm_set_edca_params(struct cw1200_common *priv, const struct wsm_edca_params *arg); +int wsm_set_uapsd_param(struct cw1200_common *priv, + const struct wsm_edca_params *arg); + /* 3.38 */ /* Set-System info. Skipped for now. Irrelevent. */ @@ -1548,6 +1558,22 @@ static inline int wsm_use_multi_tx_conf(struct cw1200_common *priv, &arg, sizeof(arg)); } + +/* 4.26 SetUpasdInformation */ +struct wsm_uapsd_info { + __le16 uapsdFlags; + __le16 minAutoTriggerInterval; + __le16 maxAutoTriggerInterval; + __le16 autoTriggerStep; +}; + +static inline int wsm_set_uapsd_info(struct cw1200_common *priv, + struct wsm_uapsd_info *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_SET_UAPSD_INFORMATION, + arg, sizeof(*arg)); +} + /* ******************************************************************** */ /* WSM TX port control */ -- cgit v1.2.3 From e36b9c702b9f496dfa32f01df61d645ed23dd390 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 18 Aug 2011 00:35:00 +0200 Subject: cw1200: Fix for firmware crash/hang in SoftAP with powersave. * Firmware does not support block ACK in SoftAP mode and crashes when BA-enabled settings are applied. Fix completly disables BA when firmware is in SoftAP mode. * Proper register is used for firmware assert line number diagnostic message. * wsm_update_ie is implemented (cosmetical change). Change-Id: I93bff4e59a1a6ab35f537097a46199cb8a1c34fa Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29064 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-by: QATEST Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33510 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 24 ++++++++++++++---------- drivers/staging/cw1200/wsm.c | 27 ++++++++++++++++++++++++++- drivers/staging/cw1200/wsm.h | 17 ++++++++++++++++- 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 2a6d27f212f..39c11172544 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -115,34 +115,38 @@ void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set) { - struct wsm_template_frame frame = { - .frame_type = WSM_FRAME_TYPE_BEACON, + struct sk_buff *skb; + struct wsm_update_ie update_ie = { + .what = WSM_UPDATE_IE_BEACON, + .count = 1, }; u16 tim_offset, tim_length; ap_printk(KERN_DEBUG "[AP] %s mcast: %s.\n", __func__, aid0_bit_set ? "ena" : "dis"); - frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, + skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, &tim_offset, &tim_length); - if (WARN_ON(!frame.skb)) + if (WARN_ON(!skb)) return -ENOMEM; if (tim_offset && tim_length >= 6) { /* Ignore DTIM count from mac80211: * firmware handles DTIM internally. */ - frame.skb->data[tim_offset + 2] = 0; + skb->data[tim_offset + 2] = 0; /* Set/reset aid0 bit */ if (aid0_bit_set) - frame.skb->data[tim_offset + 4] |= 1; + skb->data[tim_offset + 4] |= 1; else - frame.skb->data[tim_offset + 4] &= ~1; + skb->data[tim_offset + 4] &= ~1; } - WARN_ON(wsm_set_template_frame(priv, &frame)); + update_ie.ies = &skb->data[tim_offset]; + update_ie.length = tim_length; + WARN_ON(wsm_update_ie(priv, &update_ie)); - dev_kfree_skb(frame.skb); + dev_kfree_skb(skb); return 0; } @@ -661,7 +665,7 @@ static int cw1200_start_ap(struct cw1200_common *priv) ret = WARN_ON(cw1200_upload_keys(priv)); if (!ret) { WARN_ON(wsm_set_block_ack_policy(priv, - priv->ba_tid_mask, priv->ba_tid_mask)); + 0, 0)); priv->join_status = CW1200_JOIN_STATUS_AP; cw1200_update_filtering(priv); } diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 16ebfe658fe..37194870c56 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -768,6 +768,30 @@ nomem: return -ENOMEM; } +/* ******************************************************************** */ + +int wsm_update_ie(struct cw1200_common *priv, + const struct wsm_update_ie *arg) +{ + int ret; + struct wsm_buf *buf = &priv->wsm_cmd_buf; + + wsm_cmd_lock(priv); + + WSM_PUT16(buf, arg->what); + WSM_PUT16(buf, arg->count); + WSM_PUT(buf, arg->ies, arg->length); + + ret = wsm_cmd_send(priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT); + + wsm_cmd_unlock(priv); + return ret; + +nomem: + wsm_cmd_unlock(priv); + return -ENOMEM; + +} /* ******************************************************************** */ /* WSM indication events implementation */ @@ -1122,7 +1146,7 @@ int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len) else wiphy_err(priv->hw->wiphy, "Firmware assert at %.*s, line %d\n", - sizeof(fname), fname, reg[0]); + sizeof(fname), fname, reg[1]); for (i = 0; i < 12; i += 4) wiphy_err(priv->hw->wiphy, @@ -1225,6 +1249,7 @@ int wsm_handle_rx(struct cw1200_common *priv, int id, case 0x0418: /* beacon_transmit */ case 0x0419: /* start_find */ case 0x041A: /* stop_find */ + case 0x041B: /* update_ie */ case 0x041C: /* map_link */ WARN_ON(wsm_arg != NULL); ret = wsm_generic_confirm(priv, wsm_arg, &wsm_buf); diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 30db888e535..6b6d4a8049e 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -333,6 +333,11 @@ struct cw1200_common; #define WSM_RCPI_RSSI_DONT_USE_UPPER (BIT(2)) #define WSM_RCPI_RSSI_DONT_USE_LOWER (BIT(3)) +/* Update-ie constants */ +#define WSM_UPDATE_IE_BEACON (BIT(0)) +#define WSM_UPDATE_IE_PROBE_RESP (BIT(1)) +#define WSM_UPDATE_IE_PROBE_REQ (BIT(2)) + /* WSM events */ /* Error */ #define WSM_EVENT_ERROR (0) @@ -1168,7 +1173,17 @@ struct wsm_suspend_resume { typedef void (*wsm_suspend_resume_cb) (struct cw1200_common *priv, struct wsm_suspend_resume *arg); -/* 3.53 Update-IE request: Not implemented: not relevant. */ +/* 3.54 Update-IE request. */ +struct wsm_update_ie { + /* WSM_UPDATE_IE_... */ + /* [in] */ u16 what; + /* [in] */ u16 count; + /* [in] */ u8 *ies; + /* [in] */ size_t length; +}; + +int wsm_update_ie(struct cw1200_common *priv, + const struct wsm_update_ie *arg); /* 3.56 */ struct wsm_map_link { -- cgit v1.2.3 From 6b4e4d80e871fab7bc0208fe6b882a2edcc10def Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 18 Aug 2011 23:32:14 +0200 Subject: cw1200: do not allow suspend when it is useless. Do not allow entering suspend state if firmware is polling for multicasts and when scanning. + bugfix: remove blocking calls from wow_suspend() callback to reduce suspend time and make sure watchdog is not triggered. Change-Id: I69196fda25b56d01b90311582705ba586759ae35 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29079 Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33511 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 8 ++++++ drivers/staging/cw1200/pm.c | 59 ++++++++++++++++++++++++++++++++----------- drivers/staging/cw1200/scan.c | 2 ++ 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 39c11172544..e4bf98d46ed 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -539,6 +539,13 @@ void cw1200_suspend_resume(struct cw1200_common *priv, if (arg->stop) { priv->tx_multicast = false; } else { + /* Firmware sends this indication every DTIM if there + * is a STA in powersave connected. There is no reason + * to suspend, following wakeup will consume much more + * power than could be saved. */ + cw1200_pm_stay_awake(&priv->pm_state, + priv->join_dtim_period * + (priv->beacon_int + 20) * HZ / 1024); priv->tx_multicast = priv->aid0_bit_set && priv->buffered_multicasts; if (priv->tx_multicast) @@ -652,6 +659,7 @@ static int cw1200_start_ap(struct cw1200_common *priv) .ssidLength = priv->ssid_length, }; priv->beacon_int = conf->beacon_int; + priv->join_dtim_period = conf->dtim_period; memcpy(&start.ssid[0], priv->ssid, start.ssidLength); diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c index e27c4144f51..1d9207c8761 100644 --- a/drivers/staging/cw1200/pm.c +++ b/drivers/staging/cw1200/pm.c @@ -23,6 +23,7 @@ struct cw1200_suspend_state { unsigned long bss_loss_tmo; unsigned long connection_loss_tmo; unsigned long join_tmo; + unsigned long direct_probe; }; static struct dev_pm_ops cw1200_pm_ops = { @@ -165,24 +166,45 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) return -EAGAIN; #endif - /* Ensure pending operations are done. */ - ret = wait_event_interruptible_timeout( - priv->channel_switch_done, - !priv->channel_switch_in_progress, 3 * HZ); - if (WARN_ON(!ret)) - return -ETIMEDOUT; - else if (WARN_ON(ret < 0)) - return ret; + /* Make sure there is no configuration requests in progress. */ + if (!mutex_trylock(&priv->conf_mutex)) + return -EBUSY; + + /* Ensure pending operations are done. + * Note also that wow_suspend must return in ~2.5sec, before + * watchdog is triggered. */ + if (priv->channel_switch_in_progress) { + mutex_unlock(&priv->conf_mutex); + return -EBUSY; + } + + /* Do not suspend when join work is scheduled */ + if (work_pending(&priv->join_work)) { + mutex_unlock(&priv->conf_mutex); + return -EBUSY; + } + + /* Do not suspend when scanning */ + if (down_trylock(&priv->scan.lock)) { + mutex_unlock(&priv->conf_mutex); + return -EBUSY; + } - /* Flush and lock TX. */ - ret = __cw1200_flush(priv, false); - if (WARN_ON(ret < 0)) - return ret; + /* Lock TX. */ + wsm_lock_tx_async(priv); + if (priv->hw_bufs_used) { + wsm_unlock_tx(priv); + up(&priv->scan.lock); + mutex_unlock(&priv->conf_mutex); + return -EBUSY; + } /* Allocate state */ state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL); if (!state) { wsm_unlock_tx(priv); + up(&priv->scan.lock); + mutex_unlock(&priv->conf_mutex); return -ENOMEM; } @@ -193,9 +215,8 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) cw1200_suspend_work(&priv->connection_loss_work); state->join_tmo = cw1200_suspend_work(&priv->join_timeout); - - /* Flush workqueue */ - flush_workqueue(priv->workqueue); + state->direct_probe = + cw1200_suspend_work(&priv->scan.probe_work); /* Stop serving thread */ cw1200_bh_suspend(priv); @@ -244,10 +265,18 @@ int cw1200_wow_resume(struct ieee80211_hw *hw) state->connection_loss_tmo); cw1200_resume_work(priv, &priv->join_timeout, state->join_tmo); + cw1200_resume_work(priv, &priv->scan.probe_work, + state->direct_probe); /* Unlock datapath */ wsm_unlock_tx(priv); + /* Unlock scan */ + up(&priv->scan.lock); + + /* Unlock configuration mutex */ + mutex_unlock(&priv->conf_mutex); + /* Free memory */ kfree(state); diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index c663f6dc173..4daf4e2daa2 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -13,6 +13,7 @@ #include "cw1200.h" #include "scan.h" #include "sta.h" +#include "pm.h" static void cw1200_scan_restart_delayed(struct cw1200_common *priv); @@ -25,6 +26,7 @@ static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan) tmo += scan->ch[i].maxChannelTime + 10; atomic_set(&priv->scan.in_progress, 1); + cw1200_pm_stay_awake(&priv->pm_state, tmo * HZ / 1000); queue_delayed_work(priv->workqueue, &priv->scan.timeout, tmo * HZ / 1000); ret = wsm_scan(priv, scan); -- cgit v1.2.3 From 63de404e3af8e4d416d7b410a90f31ffc9d915ff Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Fri, 19 Aug 2011 10:11:24 +0200 Subject: cw1200: Fix for a BUG in cw1200_unjoin_work. Dump was seen twice: deauthentication between scan-req and scan-res. D/wpa_supplicant( 2443): nl80211: Event message available D/wpa_supplicant( 2443): nl80211: New scan results available [ 549.251159] wlan0: deauthenticated from 00:0c:e3:6b:8e:cd (Reason: 7) D/wpa_supplicant( 2443): nl80211: Associated on 2422 MHz D/wpa_supplicant( 2443): Received scan results (38 BSSes) D/wpa_supplicant( 2443): nl80211: Scan results indicate BSS status with 00:0c:e3:6b:8e:cd as associated [ 549.277404] ieee80211 phy0: cw1200_unjoin_work: Unexpected: delayed unjoin is already scheduled. V/WifiMonitor( 1779): Even... [ 549.291015] kernel BUG at drivers/staging/cw1200/sta.c:1139! Fix removes BUG_ON which was added for verification-only purposes and handles multiple DEAUTH during scan correctly. ST-Ericsson ID: 355770 Change-Id: I671fe949555f3e31a634ee27ea237da2aeb1c83e Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29087 Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33512 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/sta.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 2bbedd2971a..78258b9db38 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1165,13 +1165,14 @@ void cw1200_unjoin_work(struct work_struct *work) mutex_lock(&priv->conf_mutex); if (unlikely(atomic_read(&priv->scan.in_progress))) { if (priv->delayed_unjoin) { - wiphy_err(priv->hw->wiphy, - "%s: Unexpected: delayed unjoin " + wiphy_dbg(priv->hw->wiphy, + "%s: Delayed unjoin " "is already scheduled.\n", __func__); - BUG_ON(1); + wsm_unlock_tx(priv); + } else { + priv->delayed_unjoin = true; } - priv->delayed_unjoin = true; mutex_unlock(&priv->conf_mutex); return; } -- cgit v1.2.3 From 876cf5f4592595e2e74500d8384ec5bae380a514 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Fri, 19 Aug 2011 14:35:55 +0200 Subject: cw1200: Fix incorrect usage of wait_event_interruptible_timeout. In many places in the driver wait_event_interruptible_timeout was wrongly used instead of wait_event_timeout. It caused several problems when upper layer received signals. A typical one was: [ 4553.767944] [BH] wakeup. [ 4553.768035] kernel BUG at drivers/staging/cw1200/wsm.c:1069! [ 4553.768066] Unable to handle kernel NULL pointer dereference at virtual address 00000000 ... [ 4553.770507] [] (__bug+0x1c/0x28) from [] (wsm_cmd_send+0x270/0x2cc [cw1200_core]) [ 4553.770538] [] (wsm_cmd_send+0x270/0x2cc [cw1200_core]) from [] (wsm_remove_key+0xf0/0x114 [cw1200_core]) ... [ 4553.771972] [] (sock_sendmsg+0xa0/0xbc) from [] (sys_sendmsg+0x1b0/0x20c) [ 4553.772003] [] (sys_sendmsg+0x1b0/0x20c) from [] (ret_fast_syscall+0x0/0x30) Change-Id: Ie0c2ded7348379a324f1f23fae4416014c272530 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29164 Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33513 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/bh.c | 34 +++++++++++++------------ drivers/staging/cw1200/bh.h | 4 +-- drivers/staging/cw1200/pm.c | 56 ++++++++++++++++++++++-------------------- drivers/staging/cw1200/queue.c | 4 +-- drivers/staging/cw1200/sta.c | 4 +-- drivers/staging/cw1200/wsm.c | 14 +++++------ 6 files changed, 61 insertions(+), 55 deletions(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index 1ecae7466f6..4c4a3bf86ad 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -89,7 +89,7 @@ void cw1200_unregister_bh(struct cw1200_common *priv) priv->bh_thread = NULL; bh_printk(KERN_DEBUG "[BH] unregister.\n"); atomic_add(1, &priv->bh_term); - wake_up_interruptible(&priv->bh_wq); + wake_up(&priv->bh_wq); kthread_stop(thread); #ifdef HAS_PUT_TASK_STRUCT put_task_struct(thread); @@ -103,7 +103,7 @@ void cw1200_irq_handler(struct cw1200_common *priv) return; if (atomic_add_return(1, &priv->bh_rx) == 1) - wake_up_interruptible(&priv->bh_wq); + wake_up(&priv->bh_wq); } void cw1200_bh_wakeup(struct cw1200_common *priv) @@ -113,31 +113,33 @@ void cw1200_bh_wakeup(struct cw1200_common *priv) return; if (atomic_add_return(1, &priv->bh_tx) == 1) - wake_up_interruptible(&priv->bh_wq); + wake_up(&priv->bh_wq); } -void cw1200_bh_suspend(struct cw1200_common *priv) +int cw1200_bh_suspend(struct cw1200_common *priv) { bh_printk(KERN_DEBUG "[BH] suspend.\n"); if (WARN_ON(priv->bh_error)) - return; + return 0; atomic_set(&priv->bh_suspend, CW1200_BH_SUSPEND); - wake_up_interruptible(&priv->bh_wq); - wait_event_interruptible(priv->bh_evt_wq, priv->bh_error || - (CW1200_BH_SUSPENDED == atomic_read(&priv->bh_suspend))); + wake_up(&priv->bh_wq); + return wait_event_timeout(priv->bh_evt_wq, priv->bh_error || + (CW1200_BH_SUSPENDED == atomic_read(&priv->bh_suspend)), + 1 * HZ) ? 0 : -ETIMEDOUT; } -void cw1200_bh_resume(struct cw1200_common *priv) +int cw1200_bh_resume(struct cw1200_common *priv) { bh_printk(KERN_DEBUG "[BH] resume.\n"); if (WARN_ON(priv->bh_error)) - return; + return 0; atomic_set(&priv->bh_suspend, CW1200_BH_RESUME); - wake_up_interruptible(&priv->bh_wq); - wait_event_interruptible(priv->bh_evt_wq, priv->bh_error || - (CW1200_BH_RESUMED == atomic_read(&priv->bh_suspend))); + wake_up(&priv->bh_wq); + return wait_event_timeout(priv->bh_evt_wq, priv->bh_error || + (CW1200_BH_RESUMED == atomic_read(&priv->bh_suspend)), + 1 * HZ) ? 0 : -ETIMEDOUT; } static inline void wsm_alloc_tx_buffer(struct cw1200_common *priv) @@ -156,7 +158,7 @@ int wsm_release_tx_buffer(struct cw1200_common *priv, int count) else if (hw_bufs_used >= priv->wsm_caps.numInpChBufs) ret = 1; if (!priv->hw_bufs_used) - wake_up_interruptible(&priv->bh_evt_wq); + wake_up(&priv->bh_evt_wq); return ret; } @@ -306,7 +308,7 @@ static int cw1200_bh(void *arg) } atomic_set(&priv->bh_suspend, CW1200_BH_SUSPENDED); - wake_up_interruptible(&priv->bh_evt_wq); + wake_up(&priv->bh_evt_wq); status = wait_event_interruptible(priv->bh_wq, CW1200_BH_RESUME == atomic_read( &priv->bh_suspend)); @@ -318,7 +320,7 @@ static int cw1200_bh(void *arg) } bh_printk(KERN_DEBUG "[BH] Device resume.\n"); atomic_set(&priv->bh_suspend, CW1200_BH_RESUMED); - wake_up_interruptible(&priv->bh_evt_wq); + wake_up(&priv->bh_evt_wq); atomic_add(1, &priv->bh_rx); continue; } diff --git a/drivers/staging/cw1200/bh.h b/drivers/staging/cw1200/bh.h index a19393c61dd..6d4d27b18c5 100644 --- a/drivers/staging/cw1200/bh.h +++ b/drivers/staging/cw1200/bh.h @@ -22,8 +22,8 @@ int cw1200_register_bh(struct cw1200_common *priv); void cw1200_unregister_bh(struct cw1200_common *priv); void cw1200_irq_handler(struct cw1200_common *priv); void cw1200_bh_wakeup(struct cw1200_common *priv); -void cw1200_bh_suspend(struct cw1200_common *priv); -void cw1200_bh_resume(struct cw1200_common *priv); +int cw1200_bh_suspend(struct cw1200_common *priv); +int cw1200_bh_resume(struct cw1200_common *priv); /* Must be called from BH thread. */ void cw1200_enable_powersave(struct cw1200_common *priv, bool enable); diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c index 1d9207c8761..694f97851ee 100644 --- a/drivers/staging/cw1200/pm.c +++ b/drivers/staging/cw1200/pm.c @@ -173,40 +173,26 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) /* Ensure pending operations are done. * Note also that wow_suspend must return in ~2.5sec, before * watchdog is triggered. */ - if (priv->channel_switch_in_progress) { - mutex_unlock(&priv->conf_mutex); - return -EBUSY; - } + if (priv->channel_switch_in_progress) + goto revert1; /* Do not suspend when join work is scheduled */ - if (work_pending(&priv->join_work)) { - mutex_unlock(&priv->conf_mutex); - return -EBUSY; - } + if (work_pending(&priv->join_work)) + goto revert1; /* Do not suspend when scanning */ - if (down_trylock(&priv->scan.lock)) { - mutex_unlock(&priv->conf_mutex); - return -EBUSY; - } + if (down_trylock(&priv->scan.lock)) + goto revert1; /* Lock TX. */ wsm_lock_tx_async(priv); - if (priv->hw_bufs_used) { - wsm_unlock_tx(priv); - up(&priv->scan.lock); - mutex_unlock(&priv->conf_mutex); - return -EBUSY; - } + if (priv->hw_bufs_used) + goto revert3; /* Allocate state */ state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL); - if (!state) { - wsm_unlock_tx(priv); - up(&priv->scan.lock); - mutex_unlock(&priv->conf_mutex); - return -ENOMEM; - } + if (!state) + goto revert3; /* Store delayed work states. */ state->bss_loss_tmo = @@ -219,7 +205,8 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) cw1200_suspend_work(&priv->scan.probe_work); /* Stop serving thread */ - cw1200_bh_suspend(priv); + if (cw1200_bh_suspend(priv)) + goto revert4; /* Store suspend state */ pm_state->suspend_state = state; @@ -241,6 +228,23 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) } return 0; + +revert4: + cw1200_resume_work(priv, &priv->bss_loss_work, + state->bss_loss_tmo); + cw1200_resume_work(priv, &priv->connection_loss_work, + state->connection_loss_tmo); + cw1200_resume_work(priv, &priv->join_timeout, + state->join_tmo); + cw1200_resume_work(priv, &priv->scan.probe_work, + state->direct_probe); + kfree(state); +revert3: + wsm_unlock_tx(priv); + up(&priv->scan.lock); +revert1: + mutex_unlock(&priv->conf_mutex); + return -EBUSY; } int cw1200_wow_resume(struct ieee80211_hw *hw) @@ -256,7 +260,7 @@ int cw1200_wow_resume(struct ieee80211_hw *hw) priv->sbus_ops->power_mgmt(priv->sbus_priv, false); /* Resume BH thread */ - cw1200_bh_resume(priv); + WARN_ON(cw1200_bh_resume(priv)); /* Resume delayed work */ cw1200_resume_work(priv, &priv->bss_loss_work, diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c index 4140981eed1..a81442f8f4f 100644 --- a/drivers/staging/cw1200/queue.c +++ b/drivers/staging/cw1200/queue.c @@ -153,7 +153,7 @@ int cw1200_queue_clear(struct cw1200_queue *queue) } spin_unlock_bh(&stats->lock); spin_unlock_bh(&queue->lock); - wake_up_interruptible(&stats->wait_link_id_empty); + wake_up(&stats->wait_link_id_empty); return 0; } @@ -279,7 +279,7 @@ int cw1200_queue_get(struct cw1200_queue *queue, } spin_unlock_bh(&queue->lock); if (wakeup_stats) - wake_up_interruptible(&stats->wait_link_id_empty); + wake_up(&stats->wait_link_id_empty); return ret; } diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 78258b9db38..c7fceefe831 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -289,7 +289,7 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) cw1200_cancel_scan(priv); sta_printk(KERN_DEBUG "[STA] Freq %d (wsm ch: %d).\n", ch->center_freq, ch->hw_value); - WARN_ON(wait_event_interruptible_timeout( + WARN_ON(wait_event_timeout( priv->channel_switch_done, !priv->channel_switch_in_progress, 3 * HZ) <= 0); @@ -762,7 +762,7 @@ int __cw1200_flush(struct cw1200_common *priv, bool drop) } for (;;) { - ret = wait_event_interruptible_timeout( + ret = wait_event_timeout( priv->tx_queue_stats.wait_link_id_empty, cw1200_queue_stats_is_empty( &priv->tx_queue_stats, -1), diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 37194870c56..2ad3eceaf69 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -842,7 +842,7 @@ static int wsm_startup_indication(struct cw1200_common *priv, priv->wsm_caps.firmwareReady = 1; - wake_up_interruptible(&priv->wsm_startup_done); + wake_up(&priv->wsm_startup_done); return 0; underflow: @@ -929,7 +929,7 @@ static int wsm_channel_switch_indication(struct cw1200_common *priv, WARN_ON(WSM_GET32(buf)); priv->channel_switch_in_progress = 0; - wake_up_interruptible(&priv->channel_switch_done); + wake_up(&priv->channel_switch_done); if (priv->wsm_cbc.channel_switch) priv->wsm_cbc.channel_switch(priv); @@ -1035,7 +1035,7 @@ int wsm_cmd_send(struct cw1200_common *priv, do { /* It's safe to use unprotected access to * wsm_cmd.done here */ - ret = wait_event_interruptible_timeout( + ret = wait_event_timeout( priv->wsm_cmd_wq, priv->wsm_cmd.done, tmo); rx_timestamp = jiffies - priv->rx_timestamp; @@ -1059,7 +1059,7 @@ int wsm_cmd_send(struct cw1200_common *priv, /* If wsm_handle_rx got stuck in _confirm we will hang * system there. It's better than silently currupt * stack or heap, isn't it? */ - BUG_ON(wait_event_interruptible_timeout( + BUG_ON(wait_event_timeout( priv->wsm_cmd_wq, priv->wsm_cmd.done, WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); } @@ -1081,7 +1081,7 @@ void wsm_lock_tx(struct cw1200_common *priv) { wsm_cmd_lock(priv); if (atomic_add_return(1, &priv->tx_lock) == 1) { - WARN_ON(wait_event_interruptible_timeout(priv->bh_evt_wq, + WARN_ON(wait_event_timeout(priv->bh_evt_wq, !priv->hw_bufs_used, WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); wsm_printk(KERN_DEBUG "[WSM] TX is locked.\n"); } @@ -1097,7 +1097,7 @@ void wsm_lock_tx_async(struct cw1200_common *priv) void wsm_flush_tx(struct cw1200_common *priv) { BUG_ON(!atomic_read(&priv->tx_lock)); - WARN_ON(wait_event_interruptible_timeout(priv->bh_evt_wq, + WARN_ON(wait_event_timeout(priv->bh_evt_wq, !priv->hw_bufs_used, WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); } @@ -1269,7 +1269,7 @@ int wsm_handle_rx(struct cw1200_common *priv, int id, spin_unlock(&priv->wsm_cmd.lock); ret = 0; /* Error response from device should ne stop BH. */ - wake_up_interruptible(&priv->wsm_cmd_wq); + wake_up(&priv->wsm_cmd_wq); } else if (id & 0x0800) { switch (id) { case 0x0801: -- cgit v1.2.3 From 92c1543fd323befb2659ef58ee68f1a0521f1372 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Sat, 20 Aug 2011 19:42:20 +0200 Subject: cw1200: Fix: .remove callback for cw1200_power_driver Dedicated platform driver is used for detection of late interrupts. Implementation of the driver was missing .remove callback. It was not possible to remove cw1200 driver without this callback. ST-Ericsson ID: 355220 Change-Id: Ia8b5a88fcbb3ec86973016fff5dad98dc9ac78f1 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29196 Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33514 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/pm.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c index 694f97851ee..2e5c8085bf8 100644 --- a/drivers/staging/cw1200/pm.c +++ b/drivers/staging/cw1200/pm.c @@ -17,6 +17,7 @@ #include "sbus.h" static int cw1200_suspend_late(struct device *dev); +static int cw1200_pm_remove(struct platform_device *pdev); /* private */ struct cw1200_suspend_state { @@ -30,8 +31,11 @@ static struct dev_pm_ops cw1200_pm_ops = { .suspend_noirq = cw1200_suspend_late, }; static struct platform_driver cw1200_power_driver = { - .driver.name = "cw1200_power", - .driver.pm = &cw1200_pm_ops, + .remove = cw1200_pm_remove, + .driver = { + .name = "cw1200_power", + .pm = &cw1200_pm_ops, + }, }; static struct platform_device cw1200_power_device = { .name = "cw1200_power", @@ -151,6 +155,11 @@ static int cw1200_suspend_late(struct device *dev) return 0; } +static int cw1200_pm_remove(struct platform_device *pdev) +{ + return 0; +} + int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { struct cw1200_common *priv = hw->priv; -- cgit v1.2.3 From 70358176f96665d35fd7d859842643a84caea777 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 24 Aug 2011 07:24:52 +0200 Subject: cw1200: Disable U-APSD support Temporary disabling U-APSD. It has negative impact on performance. Change-Id: Ie71fc42c5dcf290ec0714ee35340bfa86ba62945 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29351 Reviewed-by: QATEST Reviewed-by: Dmitry TARNYAGIN Tested-by: Tobias ANDERSON Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33515 Tested-by: Dmitry TARNYAGIN Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index e9e6806e811..0c58b25594f 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -251,7 +251,9 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | - IEEE80211_HW_SUPPORTS_UAPSD | + /* TODO: Fix this + Disable UAPSD support due to performance drop */ + /* IEEE80211_HW_SUPPORTS_UAPSD | */ IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_CQM_RSSI | /* Aggregation is fully controlled by firmware. -- cgit v1.2.3 From 05cb169377de26ef5209b061f7f7760a62d428b5 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 24 Aug 2011 13:40:45 +0200 Subject: cw1200: Fix release callback for PM device Release callback was missing in CW1200 PM device. Also fixed register_platfor_device which chan not be done more than once. PM device is allocating dyncmicly now. ST-Ericsson ID: 358127 Change-Id: I3a7a7a8e72433380d00c7e2781db44e2f3abdca1 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29386 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33516 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/main.c | 6 +++- drivers/staging/cw1200/pm.c | 69 ++++++++++++++++++++++++++++++------------- drivers/staging/cw1200/pm.h | 3 +- 3 files changed, 56 insertions(+), 22 deletions(-) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 0c58b25594f..a75fa6d8c50 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -329,6 +329,11 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work); INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work); + if (unlikely(cw1200_pm_init(&priv->pm_state, priv))) { + ieee80211_free_hw(hw); + return NULL; + } + if (unlikely(cw1200_queue_stats_init(&priv->tx_queue_stats, CW1200_LINK_ID_AFTER_DTIM + 1))) { ieee80211_free_hw(hw); @@ -351,7 +356,6 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) init_waitqueue_head(&priv->wsm_startup_done); wsm_buf_init(&priv->wsm_cmd_buf); tx_policy_init(priv); - cw1200_pm_init(&priv->pm_state, priv); return hw; } diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c index 2e5c8085bf8..48e46e6c3f3 100644 --- a/drivers/staging/cw1200/pm.c +++ b/drivers/staging/cw1200/pm.c @@ -17,7 +17,8 @@ #include "sbus.h" static int cw1200_suspend_late(struct device *dev); -static int cw1200_pm_remove(struct platform_device *pdev); +static void cw1200_pm_release(struct device * dev); +static int cw1200_pm_probe(struct platform_device *pdev); /* private */ struct cw1200_suspend_state { @@ -30,40 +31,60 @@ struct cw1200_suspend_state { static struct dev_pm_ops cw1200_pm_ops = { .suspend_noirq = cw1200_suspend_late, }; + static struct platform_driver cw1200_power_driver = { - .remove = cw1200_pm_remove, + .probe = cw1200_pm_probe, .driver = { .name = "cw1200_power", .pm = &cw1200_pm_ops, }, }; -static struct platform_device cw1200_power_device = { - .name = "cw1200_power", -}; -static void cw1200_pm_init_common(struct cw1200_pm_state *pm, +static int cw1200_pm_init_common(struct cw1200_pm_state *pm, struct cw1200_common *priv) { + int ret; + spin_lock_init(&pm->lock); - cw1200_power_device.dev.platform_data = priv; - platform_device_register(&cw1200_power_device); - platform_driver_register(&cw1200_power_driver); + ret = platform_driver_register(&cw1200_power_driver); + if (ret) + return ret; + pm->pm_dev = platform_device_alloc("cw1200_power", 0); + if (!pm->pm_dev) { + platform_driver_unregister(&cw1200_power_driver); + return ENOMEM; + } + + pm->pm_dev->dev.platform_data = priv; + ret = platform_device_add(pm->pm_dev); + if (ret) { + kfree(pm->pm_dev); + pm->pm_dev = NULL; + } + + return ret; } static void cw1200_pm_deinit_common(struct cw1200_pm_state *pm) { platform_driver_unregister(&cw1200_power_driver); - platform_device_unregister(&cw1200_power_device); + if (pm->pm_dev) { + platform_device_unregister(pm->pm_dev); + kfree(pm->pm_dev); + pm->pm_dev = NULL; + } } #ifdef CONFIG_WAKELOCK -void cw1200_pm_init(struct cw1200_pm_state *pm, +int cw1200_pm_init(struct cw1200_pm_state *pm, struct cw1200_common *priv) { - cw1200_pm_init_common(pm, priv); - wake_lock_init(&pm->wakelock, - WAKE_LOCK_SUSPEND, "cw1200_wlan"); + int ret = cw1200_pm_init_common(pm, priv); + if (!ret) + wake_lock_init(&pm->wakelock, + WAKE_LOCK_SUSPEND, "cw1200_wlan"); + return ret; } void cw1200_pm_deinit(struct cw1200_pm_state *pm) @@ -91,12 +112,15 @@ static void cw1200_pm_stay_awake_tmo(unsigned long) { } -void cw1200_pm_init(struct cw1200_pm_state *pm) +int cw1200_pm_init(struct cw1200_pm_state *pm) { - cw1200_init_common(pm); - init_timer(&pm->stay_awake); - pm->stay_awake.data = (unsigned long)pm; - pm->stay_awake.function = cw1200_pm_stay_awake_tmo; + int ret = cw1200_pm_init_common(pm); + if (!ret) + init_timer(&pm->stay_awake); + pm->stay_awake.data = (unsigned long)pm; + pm->stay_awake.function = cw1200_pm_stay_awake_tmo; + } + return ret; } void cw1200_pm_deinit(struct cw1200_pm_state *pm) @@ -155,8 +179,13 @@ static int cw1200_suspend_late(struct device *dev) return 0; } -static int cw1200_pm_remove(struct platform_device *pdev) +static void cw1200_pm_release(struct device *dev) +{ +} + +static int cw1200_pm_probe(struct platform_device *pdev) { + pdev->dev.release = cw1200_pm_release; return 0; } diff --git a/drivers/staging/cw1200/pm.h b/drivers/staging/cw1200/pm.h index 433e5ec1139..0515f6cfb92 100644 --- a/drivers/staging/cw1200/pm.h +++ b/drivers/staging/cw1200/pm.h @@ -31,10 +31,11 @@ struct cw1200_pm_state { #else struct timer_list stay_awake; #endif + struct platform_device *pm_dev; spinlock_t lock; }; -void cw1200_pm_init(struct cw1200_pm_state *pm, +int cw1200_pm_init(struct cw1200_pm_state *pm, struct cw1200_common *priv); void cw1200_pm_deinit(struct cw1200_pm_state *pm); void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, -- cgit v1.2.3 From 23f9adb012bcb4258d3c96fd3b364b92730b8a36 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Tue, 23 Aug 2011 11:02:47 +0200 Subject: cw1200: Force unjoin on scan if association is not completed. Firmware behaves strange if scan request is sent when device is joined but association is not completed. Patch detects this condition and forces unjoin request to the device. ST-Ericsson ID: 356953 Change-Id: I9df66365d46e2d9b65990dfc54ad22604e3a3daa Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29403 Tested-by: Bartosz MARKOWSKI Reviewed-by: QATEST Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33517 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/scan.c | 51 ++++++++++++++++++++++++++++--------------- drivers/staging/cw1200/scan.h | 1 + 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index 4daf4e2daa2..c9210e57397 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -93,21 +93,12 @@ int cw1200_hw_scan(struct ieee80211_hw *hw, wsm_lock_tx(priv); - if (priv->join_status == CW1200_JOIN_STATUS_STA && - !(priv->powersave_mode.pmMode & WSM_PSM_PS)) { - struct wsm_set_pm pm = priv->powersave_mode; - pm.pmMode = WSM_PSM_PS; - wsm_set_pm(priv, &pm); - } else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { - /* FW bug: driver has to restart p2p-dev mode after scan */ - cw1200_disable_listening(priv); - } - BUG_ON(priv->scan.req); priv->scan.req = req; priv->scan.n_ssids = 0; priv->scan.status = 0; priv->scan.begin = &req->channels[0]; + priv->scan.curr = priv->scan.begin; priv->scan.end = &req->channels[req->n_channels]; priv->scan.output_power = priv->output_power; @@ -124,6 +115,7 @@ int cw1200_hw_scan(struct ieee80211_hw *hw, } mutex_unlock(&priv->conf_mutex); + if (frame.skb) dev_kfree_skb(frame.skb); queue_work(priv->workqueue, &priv->scan.work); @@ -139,11 +131,34 @@ void cw1200_scan_work(struct work_struct *work) .scanType = WSM_SCAN_TYPE_FOREGROUND, .scanFlags = WSM_SCAN_FLAG_SPLIT_METHOD, }; + bool first_run = priv->scan.begin == priv->scan.curr && + priv->scan.begin != priv->scan.end; int i; + if (first_run) { + /* Firmware gets crazy if scan request is sent + * when STA is joined but not yet associated. + * Force unjoin in this case. */ + if (cancel_delayed_work_sync(&priv->join_timeout) > 0) + cw1200_join_timeout(&priv->join_timeout.work); + } + mutex_lock(&priv->conf_mutex); - if (!priv->scan.req || (priv->scan.begin == priv->scan.end)) { + if (first_run) { + if (priv->join_status == CW1200_JOIN_STATUS_STA && + !(priv->powersave_mode.pmMode & WSM_PSM_PS)) { + struct wsm_set_pm pm = priv->powersave_mode; + pm.pmMode = WSM_PSM_PS; + wsm_set_pm(priv, &pm); + } else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { + /* FW bug: driver has to restart p2p-dev mode + * after scan */ + cw1200_disable_listening(priv); + } + } + + if (!priv->scan.req || (priv->scan.curr == priv->scan.end)) { if (priv->scan.output_power != priv->output_power) WARN_ON(wsm_set_output_power(priv, priv->output_power * 10)); @@ -166,8 +181,8 @@ void cw1200_scan_work(struct work_struct *work) up(&priv->scan.lock); return; } else { - struct ieee80211_channel *first = *priv->scan.begin; - for (it = priv->scan.begin + 1, i = 1; + struct ieee80211_channel *first = *priv->scan.curr; + for (it = priv->scan.curr + 1, i = 1; it != priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS; ++it, ++i) { if ((*it)->band != first->band) @@ -187,7 +202,7 @@ void cw1200_scan_work(struct work_struct *work) (first->flags & IEEE80211_CHAN_PASSIVE_SCAN) ? 0 : 2; scan.numOfSSIDs = priv->scan.n_ssids; scan.ssids = &priv->scan.ssids[0]; - scan.numOfChannels = it - priv->scan.begin; + scan.numOfChannels = it - priv->scan.curr; /* TODO: Is it optimal? */ scan.probeDelay = 100; /* It is not stated in WSM specification, however @@ -196,14 +211,14 @@ void cw1200_scan_work(struct work_struct *work) if (priv->join_status == CW1200_JOIN_STATUS_STA) scan.scanType = WSM_SCAN_TYPE_BACKGROUND; scan.ch = kzalloc( - sizeof(struct wsm_scan_ch[it - priv->scan.begin]), + sizeof(struct wsm_scan_ch[it - priv->scan.curr]), GFP_KERNEL); if (!scan.ch) { priv->scan.status = -ENOMEM; goto fail; } for (i = 0; i < scan.numOfChannels; ++i) { - scan.ch[i].number = priv->scan.begin[i]->hw_value; + scan.ch[i].number = priv->scan.curr[i]->hw_value; scan.ch[i].minChannelTime = 50; scan.ch[i].maxChannelTime = 110; } @@ -217,13 +232,13 @@ void cw1200_scan_work(struct work_struct *work) kfree(scan.ch); if (WARN_ON(priv->scan.status)) goto fail; - priv->scan.begin = it; + priv->scan.curr = it; } mutex_unlock(&priv->conf_mutex); return; fail: - priv->scan.begin = priv->scan.end; + priv->scan.curr = priv->scan.end; mutex_unlock(&priv->conf_mutex); queue_work(priv->workqueue, &priv->scan.work); return; diff --git a/drivers/staging/cw1200/scan.h b/drivers/staging/cw1200/scan.h index 3e7f6a42d6c..f2ea372aad7 100644 --- a/drivers/staging/cw1200/scan.h +++ b/drivers/staging/cw1200/scan.h @@ -27,6 +27,7 @@ struct cw1200_scan { struct delayed_work timeout; struct cfg80211_scan_request *req; struct ieee80211_channel **begin; + struct ieee80211_channel **curr; struct ieee80211_channel **end; struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS]; u8 *ie; -- cgit v1.2.3 From fccd0ac70b8c3a1b3786174f607ae5af7a52dc96 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Mon, 22 Aug 2011 07:52:54 +0200 Subject: cw1200: PM state in AP mode could be out of sync. PM state is controlled separately by firmware and driver. Firmware does not update own PM state when STA is removed, so PM state of the driver and firmware could be out of sync. The patch implements resyncronization of the PM state. ST-Ericsson ID: 354923 Change-Id: Ie2d8f54bc9d6dc1578aead31eecdb04c9ce7505e Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29404 Reviewed-by: QABUILD Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33518 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 258 +++++++++++++++++++++++++++++++++++----- drivers/staging/cw1200/ap.h | 8 +- drivers/staging/cw1200/cw1200.h | 19 +++ drivers/staging/cw1200/debug.c | 17 +++ drivers/staging/cw1200/main.c | 10 +- drivers/staging/cw1200/pm.c | 13 ++ drivers/staging/cw1200/queue.c | 5 +- drivers/staging/cw1200/queue.h | 11 +- drivers/staging/cw1200/sta.c | 1 + drivers/staging/cw1200/txrx.c | 130 ++++++++++++-------- drivers/staging/cw1200/wsm.c | 48 +++++--- drivers/staging/cw1200/wsm.h | 3 + 12 files changed, 421 insertions(+), 102 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index e4bf98d46ed..20a1d61690e 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -20,6 +20,8 @@ #define ap_printk(...) #endif +#define CW1200_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ)) + static int cw1200_upload_beacon(struct cw1200_common *priv); static int cw1200_start_ap(struct cw1200_common *priv); static int cw1200_update_beaconing(struct cw1200_common *priv); @@ -35,27 +37,22 @@ int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cw1200_common *priv = hw->priv; struct cw1200_sta_priv *sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; - struct wsm_map_link map_link = { - .link_id = 0, - }; if (priv->mode != NL80211_IFTYPE_AP) return 0; - map_link.link_id = ffs(~(priv->link_id_map | 1)) - 1; - if (map_link.link_id > CW1200_MAX_STA_IN_AP_MODE) { - sta_priv->link_id = 0; - printk(KERN_INFO "[AP] No more link ID available.\n"); + sta_priv->link_id = cw1200_find_link_id(priv, sta->addr); + if (!sta_priv->link_id) { + wiphy_info(priv->hw->wiphy, + "[AP] No more link IDs available.\n"); return -ENOENT; } - memcpy(map_link.mac_addr, sta->addr, ETH_ALEN); - if (!WARN_ON(wsm_map_link(priv, &map_link))) { - sta_priv->link_id = map_link.link_id; - priv->link_id_map |= BIT(map_link.link_id); - ap_printk(KERN_DEBUG "[AP] STA added, link_id: %d\n", - map_link.link_id); - } + spin_lock_bh(&priv->buffered_multicasts_lock); + priv->link_id_db[sta_priv->link_id - 1].status = CW1200_LINK_HARD; + if (priv->link_id_db[sta_priv->link_id - 1].ps) + ieee80211_sta_ps_transition(sta, true); + spin_unlock_bh(&priv->buffered_multicasts_lock); return 0; } @@ -65,21 +62,19 @@ int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cw1200_common *priv = hw->priv; struct cw1200_sta_priv *sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; - struct wsm_reset reset = { - .link_id = 0, - .reset_statistics = false, - }; - if (sta_priv->link_id) { - ap_printk(KERN_DEBUG "[AP] STA removed, link_id: %d\n", - sta_priv->link_id); - reset.link_id = sta_priv->link_id; - priv->link_id_map &= ~BIT(sta_priv->link_id); - sta_priv->link_id = 0; - wsm_lock_tx(priv); - WARN_ON(wsm_reset(priv, &reset)); - wsm_unlock_tx(priv); - } + if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id) + return 0; + + spin_lock_bh(&priv->buffered_multicasts_lock); + priv->link_id_db[sta_priv->link_id - 1].status = CW1200_LINK_SOFT; + priv->link_id_db[sta_priv->link_id - 1].timestamp = jiffies; + if (!delayed_work_pending(&priv->link_id_gc_work)) + queue_delayed_work(priv->workqueue, + &priv->link_id_gc_work, + CW1200_LINK_ID_GC_TIMEOUT); + priv->link_id_db[sta_priv->link_id - 1].ps = false; + spin_unlock_bh(&priv->buffered_multicasts_lock); return 0; } @@ -113,6 +108,40 @@ void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, spin_unlock_bh(&priv->buffered_multicasts_lock); } +static void __cw1200_ps_notify(struct cw1200_common *priv, + struct ieee80211_sta *sta, + int link_id, bool ps) +{ + txrx_printk(KERN_DEBUG "%s for LinkId: %d. Suspend: %.8X\n", + ps ? "Stop" : "Start", + link_id, priv->tx_suspend_mask[0]); + + priv->link_id_db[link_id - 1].ps = ps; + if (sta) { + cw1200_sta_notify(priv->hw, priv->vif, + ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, sta); + ieee80211_sta_ps_transition_ni(sta, ps); + } +} + +void cw1200_ps_notify(struct cw1200_common *priv, + int link_id, bool ps) +{ + struct ieee80211_sta *sta; + + if (!link_id || link_id > CW1200_MAX_STA_IN_AP_MODE) + return; + + if (!!ps == !!priv->link_id_db[link_id - 1].ps) + return; + + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, + priv->link_id_db[link_id - 1].mac); + __cw1200_ps_notify(priv, sta, link_id, ps); + rcu_read_unlock(); +} + static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set) { struct sk_buff *skb; @@ -476,11 +505,14 @@ void cw1200_multicast_start_work(struct work_struct *work) { struct cw1200_common *priv = container_of(work, struct cw1200_common, multicast_start_work); + long tmo = priv->join_dtim_period * + (priv->beacon_int + 20) * HZ / 1024; if (!priv->aid0_bit_set) { wsm_lock_tx(priv); cw1200_set_tim_impl(priv, true); priv->aid0_bit_set = true; + mod_timer(&priv->mcast_timeout, tmo); wsm_unlock_tx(priv); } } @@ -498,6 +530,19 @@ void cw1200_multicast_stop_work(struct work_struct *work) } } +void cw1200_mcast_timeout(unsigned long arg) +{ + struct cw1200_common *priv = + (struct cw1200_common *)arg; + + spin_lock_bh(&priv->buffered_multicasts_lock); + priv->tx_multicast = priv->aid0_bit_set && + priv->buffered_multicasts; + if (priv->tx_multicast) + cw1200_bh_wakeup(priv); + spin_unlock_bh(&priv->buffered_multicasts_lock); +} + int cw1200_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, @@ -522,6 +567,7 @@ void cw1200_suspend_resume(struct cw1200_common *priv, u32 set = 0; u32 clear; u32 tx_suspend_mask; + bool cancel_tmo = false; int i; if (!arg->link_id) /* For all links */ @@ -542,16 +588,20 @@ void cw1200_suspend_resume(struct cw1200_common *priv, /* Firmware sends this indication every DTIM if there * is a STA in powersave connected. There is no reason * to suspend, following wakeup will consume much more - * power than could be saved. */ + * power than it could be saved. */ cw1200_pm_stay_awake(&priv->pm_state, priv->join_dtim_period * (priv->beacon_int + 20) * HZ / 1024); priv->tx_multicast = priv->aid0_bit_set && priv->buffered_multicasts; - if (priv->tx_multicast) + if (priv->tx_multicast) { + cancel_tmo = true; cw1200_bh_wakeup(priv); + } } spin_unlock_bh(&priv->buffered_multicasts_lock); + if (cancel_tmo) + del_timer_sync(&priv->mcast_timeout); } else { if (arg->stop) set = unicast; @@ -563,6 +613,8 @@ void cw1200_suspend_resume(struct cw1200_common *priv, /* TODO: if (!priv->uapsd) */ queue = 0x0F; + spin_lock_bh(&priv->buffered_multicasts_lock); + priv->sta_asleep_mask |= set; for (i = 0; i < 4; ++i) { if (!(queue & BIT(i))) continue; @@ -576,6 +628,8 @@ void cw1200_suspend_resume(struct cw1200_common *priv, &priv->tx_queue[i], tx_suspend_mask & clear); } + spin_unlock_bh(&priv->buffered_multicasts_lock); + cw1200_ps_notify(priv, arg->link_id, arg->stop); } if (wakeup_required) cw1200_bh_wakeup(priv); @@ -663,6 +717,8 @@ static int cw1200_start_ap(struct cw1200_common *priv) memcpy(&start.ssid[0], priv->ssid, start.ssidLength); + memset(&priv->link_id_db, 0, sizeof(priv->link_id_db)); + ap_printk(KERN_DEBUG "[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s.\n", start.channelNumber, start.band, start.beaconInterval, start.DTIMPeriod, @@ -706,3 +762,145 @@ static int cw1200_update_beaconing(struct cw1200_common *priv) return 0; } +int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac) +{ + int i, ret = 0; + spin_lock_bh(&priv->buffered_multicasts_lock); + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) && + priv->link_id_db[i].status) { + priv->link_id_db[i].timestamp = jiffies; + ret = i + 1; + break; + } + } + spin_unlock_bh(&priv->buffered_multicasts_lock); + return ret; +} + +int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac) +{ + int i, ret = 0; + unsigned long max_inactivity = 0; + unsigned long now = jiffies; + + spin_lock_bh(&priv->buffered_multicasts_lock); + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + if (!priv->link_id_db[i].status) { + ret = i + 1; + break; + } else if (priv->link_id_db[i].status != CW1200_LINK_HARD && + !priv->tx_queue_stats.link_map_cache[i + 1]) { + + unsigned long inactivity = + now - priv->link_id_db[i].timestamp; + if (inactivity < max_inactivity) + continue; + max_inactivity = inactivity; + ret = i + 1; + } + } + if (ret) { + ap_printk(KERN_DEBUG "[AP] STA added, link_id: %d\n", + ret); + priv->link_id_db[ret - 1].status = CW1200_LINK_RESERVE; + memcpy(&priv->link_id_db[ret - 1].mac, mac, ETH_ALEN); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(priv); + } else { + wiphy_info(priv->hw->wiphy, + "[AP] Early: no more link IDs available.\n"); + } + + spin_unlock_bh(&priv->buffered_multicasts_lock); + return ret; +} + +void cw1200_link_id_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, link_id_work); + wsm_flush_tx(priv); + cw1200_link_id_gc_work(&priv->link_id_gc_work.work); + wsm_unlock_tx(priv); +} + +void cw1200_link_id_gc_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, link_id_gc_work.work); + struct wsm_reset reset = { + .reset_statistics = false, + }; + struct wsm_map_link map_link = { + .link_id = 0, + }; + unsigned long now = jiffies; + unsigned long next_gc = -1; + long ttl; + bool need_reset; + u32 mask; + int i, j; + + if (priv->join_status != CW1200_JOIN_STATUS_AP) + return; + + wsm_lock_tx(priv); + spin_lock_bh(&priv->buffered_multicasts_lock); + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + need_reset = false; + mask = BIT(i + 1); + if (priv->link_id_db[i].status == CW1200_LINK_RESERVE || + (priv->link_id_db[i].status == CW1200_LINK_HARD && + !(priv->link_id_map & mask))) { + if (priv->link_id_map & mask) { + priv->sta_asleep_mask &= ~mask; + for (j = 0; j < 4; ++j) + priv->tx_suspend_mask[j] &= ~mask; + need_reset = true; + } + priv->link_id_map |= mask; + if (priv->link_id_db[i].status != CW1200_LINK_HARD) + priv->link_id_db[i].status = CW1200_LINK_SOFT; + memcpy(map_link.mac_addr, priv->link_id_db[i].mac, + ETH_ALEN); + spin_unlock_bh(&priv->buffered_multicasts_lock); + if (need_reset) { + reset.link_id = i + 1; + WARN_ON(wsm_reset(priv, &reset)); + } + map_link.link_id = i + 1; + WARN_ON(wsm_map_link(priv, &map_link)); + next_gc = min(next_gc, CW1200_LINK_ID_GC_TIMEOUT); + spin_lock_bh(&priv->buffered_multicasts_lock); + } else if (priv->link_id_db[i].status == CW1200_LINK_SOFT) { + ttl = priv->link_id_db[i].timestamp - now + + CW1200_LINK_ID_GC_TIMEOUT; + if (ttl <= 0) { + need_reset = true; + priv->link_id_db[i].status = CW1200_LINK_OFF; + priv->link_id_map &= ~mask; + priv->sta_asleep_mask &= ~mask; + for (j = 0; j < 4; ++j) + priv->tx_suspend_mask[j] &= ~mask; + memset(map_link.mac_addr, 0, ETH_ALEN); + spin_unlock_bh(&priv->buffered_multicasts_lock); + reset.link_id = i + 1; + WARN_ON(wsm_reset(priv, &reset)); + spin_lock_bh(&priv->buffered_multicasts_lock); + } else { + next_gc = min(next_gc, (unsigned long)ttl); + } + } + if (need_reset) + ap_printk(KERN_DEBUG "[AP] STA removed, link_id: %d\n", + reset.link_id); + } + spin_unlock_bh(&priv->buffered_multicasts_lock); + if (next_gc != -1) + queue_delayed_work(priv->workqueue, + &priv->link_id_gc_work, next_gc); + wsm_unlock_tx(priv); +} + diff --git a/drivers/staging/cw1200/ap.h b/drivers/staging/cw1200/ap.h index 59462248bf4..032f631a656 100644 --- a/drivers/staging/cw1200/ap.h +++ b/drivers/staging/cw1200/ap.h @@ -36,6 +36,12 @@ void cw1200_suspend_resume(struct cw1200_common *priv, void cw1200_set_tim_work(struct work_struct *work); void cw1200_multicast_start_work(struct work_struct *work); void cw1200_multicast_stop_work(struct work_struct *work); - +void cw1200_mcast_timeout(unsigned long arg); +int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac); +int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac); +void cw1200_link_id_work(struct work_struct *work); +void cw1200_link_id_gc_work(struct work_struct *work); +void cw1200_ps_notify(struct cw1200_common *priv, + int link_id, bool ps); #endif diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index e4fda896b78..85d7caf89e9 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -46,6 +46,7 @@ #define CW1200_MAX_STA_IN_AP_MODE (5) #define CW1200_LINK_ID_AFTER_DTIM (CW1200_MAX_STA_IN_AP_MODE + 1) +#define CW1200_MAX_REQUEUE_ATTEMPTS (5) /* Please keep order */ enum cw1200_join_status { @@ -55,6 +56,20 @@ enum cw1200_join_status { CW1200_JOIN_STATUS_AP, }; +enum cw1200_link_status { + CW1200_LINK_OFF, + CW1200_LINK_RESERVE, + CW1200_LINK_SOFT, + CW1200_LINK_HARD, +}; + +struct cw1200_link_entry { + unsigned long timestamp; + enum cw1200_link_status status; + u8 mac[ETH_ALEN]; + bool ps; +}; + struct cw1200_common { struct cw1200_queue tx_queue[4]; struct cw1200_queue_stats tx_queue_stats; @@ -172,6 +187,9 @@ struct cw1200_common { /* AP powersave */ u32 link_id_map; + struct cw1200_link_entry link_id_db[CW1200_MAX_STA_IN_AP_MODE]; + struct work_struct link_id_work; + struct delayed_work link_id_gc_work; u32 tx_suspend_mask[4]; u32 sta_asleep_mask; u32 pspoll_mask; @@ -182,6 +200,7 @@ struct cw1200_common { struct work_struct set_tim_work; struct work_struct multicast_start_work; struct work_struct multicast_stop_work; + struct timer_list mcast_timeout; /* WSM events and CQM implementation */ diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index c918d3f8291..2d7adfe8b4c 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -38,6 +38,12 @@ static const char * const cw1200_debug_fw_types[] = { "Platform test", }; +static const char * const cw1200_debug_link_id[] = { + "OFF", + "REQ", + "SOFT", + "HARD", +}; static const char *cw1200_debug_mode(int mode) { @@ -222,6 +228,17 @@ static int cw1200_status_show(struct seq_file *seq, void *v) seq_puts(seq, "\n"); + for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { + if (priv->link_id_db[i].status) { + seq_printf(seq, "Link %d: %s, %pM\n", + i + 1, cw1200_debug_link_id[ + priv->link_id_db[i].status], + priv->link_id_db[i].mac); + } + } + + seq_puts(seq, "\n"); + seq_printf(seq, "BH status: %s\n", atomic_read(&priv->bh_term) ? "terminated" : "alive"); seq_printf(seq, "Pending RX: %d\n", diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index a75fa6d8c50..57f90e9168a 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -251,6 +251,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | + IEEE80211_HW_AP_LINK_PS | /* TODO: Fix this Disable UAPSD support due to performance drop */ /* IEEE80211_HW_SUPPORTS_UAPSD | */ @@ -285,7 +286,9 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) hw->max_rates = 8; hw->max_rate_tries = 15; - hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM; + hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM + + 8 /* TKIP IV */ + + 12 /* TKIP ICV and MIC */; hw->sta_data_size = sizeof(struct cw1200_sta_priv); @@ -328,6 +331,11 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work); INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work); INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work); + INIT_WORK(&priv->link_id_work, cw1200_link_id_work); + INIT_DELAYED_WORK(&priv->link_id_gc_work, cw1200_link_id_gc_work); + init_timer(&priv->mcast_timeout); + priv->mcast_timeout.data = (unsigned long)priv; + priv->mcast_timeout.function = cw1200_mcast_timeout; if (unlikely(cw1200_pm_init(&priv->pm_state, priv))) { ieee80211_free_hw(hw); diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c index 48e46e6c3f3..3b6c0bf496c 100644 --- a/drivers/staging/cw1200/pm.c +++ b/drivers/staging/cw1200/pm.c @@ -26,6 +26,7 @@ struct cw1200_suspend_state { unsigned long connection_loss_tmo; unsigned long join_tmo; unsigned long direct_probe; + unsigned long link_id_gc; }; static struct dev_pm_ops cw1200_pm_ops = { @@ -241,11 +242,17 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) cw1200_suspend_work(&priv->join_timeout); state->direct_probe = cw1200_suspend_work(&priv->scan.probe_work); + state->link_id_gc = + cw1200_suspend_work(&priv->link_id_gc_work); /* Stop serving thread */ if (cw1200_bh_suspend(priv)) goto revert4; + ret = timer_pending(&priv->mcast_timeout); + if (ret) + goto revert5; + /* Store suspend state */ pm_state->suspend_state = state; @@ -267,6 +274,8 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) return 0; +revert5: + WARN_ON(cw1200_bh_resume(priv)); revert4: cw1200_resume_work(priv, &priv->bss_loss_work, state->bss_loss_tmo); @@ -276,6 +285,8 @@ revert4: state->join_tmo); cw1200_resume_work(priv, &priv->scan.probe_work, state->direct_probe); + cw1200_resume_work(priv, &priv->link_id_gc_work, + state->link_id_gc); kfree(state); revert3: wsm_unlock_tx(priv); @@ -309,6 +320,8 @@ int cw1200_wow_resume(struct ieee80211_hw *hw) state->join_tmo); cw1200_resume_work(priv, &priv->scan.probe_work, state->direct_probe); + cw1200_resume_work(priv, &priv->link_id_gc_work, + state->link_id_gc); /* Unlock datapath */ wsm_unlock_tx(priv); diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c index a81442f8f4f..c6eabd12c56 100644 --- a/drivers/staging/cw1200/queue.c +++ b/drivers/staging/cw1200/queue.c @@ -221,6 +221,7 @@ int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, list_move_tail(&item->head, &queue->queue); item->skb = skb; + item->generation = 0; item->packetID = cw1200_queue_make_packet_id( queue->generation, queue->queue_id, item->generation, item - queue->pool); @@ -249,7 +250,8 @@ int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, int cw1200_queue_get(struct cw1200_queue *queue, u32 link_id_map, struct wsm_tx **tx, - struct ieee80211_tx_info **tx_info) + struct ieee80211_tx_info **tx_info, + int *link_id) { int ret = -ENOENT; struct cw1200_queue_item *item; @@ -267,6 +269,7 @@ int cw1200_queue_get(struct cw1200_queue *queue, if (!WARN_ON(ret)) { *tx = (struct wsm_tx *)item->skb->data; *tx_info = IEEE80211_SKB_CB(item->skb); + *link_id = item->link_id; list_move_tail(&item->head, &queue->pending); ++queue->num_pending; --queue->link_map_cache[item->link_id]; diff --git a/drivers/staging/cw1200/queue.h b/drivers/staging/cw1200/queue.h index fc5d4613efb..b28d0294d25 100644 --- a/drivers/staging/cw1200/queue.h +++ b/drivers/staging/cw1200/queue.h @@ -64,7 +64,8 @@ int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *cw1200, int cw1200_queue_get(struct cw1200_queue *queue, u32 link_id_map, struct wsm_tx **tx, - struct ieee80211_tx_info **tx_info); + struct ieee80211_tx_info **tx_info, + int *link_id); int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID); int cw1200_queue_requeue_all(struct cw1200_queue *queue); int cw1200_queue_remove(struct cw1200_queue *queue, struct cw1200_common *priv, @@ -79,12 +80,14 @@ void cw1200_queue_unlock(struct cw1200_queue *queue, bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, u32 link_id_map); -/* int cw1200_queue_get_stats(struct cw1200_queue *queue, -struct ieee80211_tx_queue_stats *stats); */ - static inline u8 cw1200_queue_get_queue_id(u32 packetID) { return (packetID >> 16) & 0xFF; } +static inline u8 cw1200_queue_get_generation(u32 packetID) +{ + return (packetID >> 8) & 0xFF; +} + #endif /* CW1200_QUEUE_H_INCLUDED */ diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index c7fceefe831..b368c0b820e 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -125,6 +125,7 @@ void cw1200_stop(struct ieee80211_hw *dev) cancel_delayed_work_sync(&priv->join_timeout); cancel_delayed_work_sync(&priv->bss_loss_work); cancel_delayed_work_sync(&priv->connection_loss_work); + cancel_delayed_work_sync(&priv->link_id_gc_work); mutex_lock(&priv->conf_mutex); switch (priv->join_status) { diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 18544011b16..1742af0ac8e 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -10,6 +10,7 @@ */ #include +#include #include "cw1200.h" #include "wsm.h" @@ -420,17 +421,35 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + const u8 *da = ieee80211_get_DA(hdr); struct cw1200_sta_priv *sta_priv = (struct cw1200_sta_priv *)&tx_info->control.sta->drv_priv; - int link_id = 0; + int link_id; int ret; + int i; - if (tx_info->control.sta) + if (likely(tx_info->control.sta && sta_priv->link_id)) link_id = sta_priv->link_id; - else if ((tx_info->flags | IEEE80211_TX_CTL_SEND_AFTER_DTIM) && - (priv->mode == NL80211_IFTYPE_AP) && - priv->enable_beacon) - link_id = CW1200_LINK_ID_AFTER_DTIM; + else if (priv->mode != NL80211_IFTYPE_AP) + link_id = 0; + else if (is_multicast_ether_addr(da)) { + if (priv->enable_beacon) + link_id = CW1200_LINK_ID_AFTER_DTIM; + else + link_id = 0; + } else { + link_id = cw1200_find_link_id(priv, da); + if (!link_id) + link_id = cw1200_alloc_link_id(priv, da); + if (!link_id) { + wiphy_err(priv->hw->wiphy, + "%s: No more link IDs available.\n", + __func__); + goto err; + } + } + if (link_id && link_id <= CW1200_MAX_STA_IN_AP_MODE) + priv->link_id_db[link_id - 1].timestamp = jiffies; txrx_printk(KERN_DEBUG "[TX] TX %d bytes (queue: %d, link_id: %d).\n", skb->len, queue, link_id); @@ -438,22 +457,13 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) if (WARN_ON(queue >= 4)) goto err; -#if 0 - { - /* HACK!!! - * Workarounnd against a bug in WSM_A21.05.0288 firmware. - * In AP mode FW calculates FCS incorrectly when DA - * is FF:FF:FF:FF:FF:FF. Just for verification, - * do not enable this code in the real live. */ - static const u8 mac_ff[] = - {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - static const u8 mac_mc[] = - {0x01, 0x00, 0x5e, 0x00, 0x00, 0x16}; - if (!memcmp(&skb->data[4], mac_ff, sizeof(mac_ff))) - memcpy(&skb->data[4], mac_mc, sizeof(mac_mc)); + if (unlikely(ieee80211_is_auth(hdr->frame_control))) { + spin_lock_bh(&priv->buffered_multicasts_lock); + priv->sta_asleep_mask &= ~BIT(link_id); + for (i = 0; i < 4; ++i) + priv->tx_suspend_mask[i] &= ~BIT(link_id); + spin_unlock_bh(&priv->buffered_multicasts_lock); } -#endif - /* IV/ICV injection. */ /* TODO: Quite unoptimal. It's better co modify mac80211 @@ -471,8 +481,9 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) icv_len += 8; /* MIC */ } - if (skb_headroom(skb) < iv_len + WSM_TX_EXTRA_HEADROOM - || skb_tailroom(skb) < icv_len) { + if ((skb_headroom(skb) + skb_tailroom(skb) < + iv_len + icv_len + WSM_TX_EXTRA_HEADROOM) || + (skb_headroom(skb) < iv_len + WSM_TX_EXTRA_HEADROOM)) { wiphy_err(priv->hw->wiphy, "Bug: no space allocated " "for crypto headers.\n" @@ -482,6 +493,17 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) skb_headroom(skb), skb_tailroom(skb), iv_len + WSM_TX_EXTRA_HEADROOM, icv_len); goto err; + } else if (skb_tailroom(skb) < icv_len) { + size_t offset = icv_len - skb_tailroom(skb); + u8 *p; + wiphy_warn(priv->hw->wiphy, + "Slowpath: tailroom is not big enough. " + "Req: %d, got: %d.\n", + icv_len, skb_tailroom(skb)); + + p = skb_push(skb, offset); + memmove(p, &p[offset], skb->len - offset); + skb_trim(skb, skb->len - offset); } newhdr = skb_push(skb, iv_len); @@ -572,9 +594,9 @@ static int cw1200_handle_pspoll(struct cw1200_common *priv, struct sk_buff *skb) { struct ieee80211_sta *sta; - struct cw1200_sta_priv *sta_priv; struct ieee80211_pspoll *pspoll = (struct ieee80211_pspoll *) skb->data; + int link_id = 0; u32 pspoll_mask; int drop = 1; int i; @@ -586,13 +608,21 @@ static int cw1200_handle_pspoll(struct cw1200_common *priv, rcu_read_lock(); sta = ieee80211_find_sta(priv->vif, pspoll->ta); - if (!sta) { - rcu_read_unlock(); - goto done; + if (sta) { + struct cw1200_sta_priv *sta_priv; + sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; + link_id = sta_priv->link_id; + pspoll_mask = BIT(sta_priv->link_id); } - sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; - pspoll_mask = BIT(sta_priv->link_id); rcu_read_unlock(); + if (!link_id) + /* Slowpath */ + link_id = cw1200_find_link_id(priv, pspoll->ta); + + if (!link_id) + goto done; + + pspoll_mask = BIT(link_id); priv->pspoll_mask |= pspoll_mask; drop = 0; @@ -608,6 +638,7 @@ static int cw1200_handle_pspoll(struct cw1200_common *priv, break; } } + txrx_printk(KERN_DEBUG "[RX] PSPOLL: %s\n", drop ? "local" : "fwd"); done: return drop; } @@ -631,6 +662,10 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, if (WARN_ON(queue_id >= 4)) return; + if (arg->status) + txrx_printk(KERN_DEBUG "TX failed: %d.\n", + arg->status); + if ((arg->status == WSM_REQUEUE) && (arg->flags & WSM_TX_STATUS_REQUEUE)) { /* "Requeue" means "implicit suspend" */ @@ -640,20 +675,10 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, .multicast = !arg->link_id, }; cw1200_suspend_resume(priv, &suspend); - if (suspend.multicast) { - /* HACK!!! WSM324 firmware has tendency to requeue - * multicast frames in a loop, causing performance - * drop and high power consumption of the driver. - * In this situation it is better just to drop - * the problematic frame. */ - wiphy_warn(priv->hw->wiphy, "Attempt to requeue a " - "multicat frame. Frame is dropped\n"); - WARN_ON(cw1200_queue_remove(queue, priv, - arg->packetID)); - } else { - WARN_ON(cw1200_queue_requeue(queue, - arg->packetID)); - } + wiphy_warn(priv->hw->wiphy, "Requeue (try %d).\n", + cw1200_queue_get_generation(arg->packetID) + 1); + WARN_ON(cw1200_queue_requeue(queue, + arg->packetID)); } else if (!WARN_ON(cw1200_queue_get_skb( queue, arg->packetID, &skb))) { struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb); @@ -719,8 +744,8 @@ void cw1200_rx_cb(struct cw1200_common *priv, { struct sk_buff *skb = *skb_p; struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); + struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; unsigned long grace_period; - __le16 frame_control; hdr->flag = 0; if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { @@ -728,6 +753,9 @@ void cw1200_rx_cb(struct cw1200_common *priv, goto drop; } + if (arg->link_id && arg->link_id <= CW1200_MAX_STA_IN_AP_MODE) + priv->link_id_db[arg->link_id - 1].timestamp = jiffies; + if (unlikely(arg->status)) { if (arg->status == WSM_STATUS_MICFAILURE) { txrx_printk(KERN_DEBUG "[RX] MIC failure.\n"); @@ -748,9 +776,7 @@ void cw1200_rx_cb(struct cw1200_common *priv, goto drop; } - frame_control = *(__le16*)skb->data; - - if (unlikely(ieee80211_is_pspoll(frame_control))) + if (unlikely(ieee80211_is_pspoll(frame->frame_control))) if (cw1200_handle_pspoll(priv, skb)) goto drop; @@ -775,7 +801,7 @@ void cw1200_rx_cb(struct cw1200_common *priv, if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { size_t iv_len = 0, icv_len = 0; - size_t hdrlen = ieee80211_hdrlen(frame_control); + size_t hdrlen = ieee80211_hdrlen(frame->frame_control); hdr->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED; @@ -821,7 +847,7 @@ void cw1200_rx_cb(struct cw1200_common *priv, if (arg->flags & WSM_RX_STATUS_AGGREGATE) cw1200_debug_rxed_agg(priv); - if (ieee80211_is_action(frame_control) && + if (ieee80211_is_action(frame->frame_control) && (arg->flags & WSM_RX_STATUS_ADDRESS1)) if (cw1200_handle_action_rx(priv, skb)) return; @@ -829,12 +855,16 @@ void cw1200_rx_cb(struct cw1200_common *priv, /* Stay awake for 1sec. after frame is received to give * userspace chance to react and acquire appropriate * wakelock. */ - if (ieee80211_is_auth(frame_control)) + if (ieee80211_is_auth(frame->frame_control)) grace_period = 5 * HZ; else grace_period = 1 * HZ; cw1200_pm_stay_awake(&priv->pm_state, grace_period); + /* Notify driver and mac80211 about PM state */ + cw1200_ps_notify(priv, arg->link_id, + ieee80211_has_pm(frame->frame_control)); + /* Not that we really need _irqsafe variant here, * but it offloads realtime bh thread and improve * system performance. */ diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 2ad3eceaf69..6514b9227a7 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -851,6 +851,7 @@ underflow: } static int wsm_receive_indication(struct cw1200_common *priv, + int link_id, struct wsm_buf *buf, struct sk_buff **skb_p) { @@ -865,6 +866,7 @@ static int wsm_receive_indication(struct cw1200_common *priv, rx.rxedRate = WSM_GET8(buf); rx.rcpiRssi = WSM_GET8(buf); rx.flags = WSM_GET32(buf); + rx.link_id = link_id; fctl = *(__le16 *)buf->data; hdr_len = buf->data - buf->begin; skb_pull(*skb_p, hdr_len); @@ -1276,7 +1278,8 @@ int wsm_handle_rx(struct cw1200_common *priv, int id, ret = wsm_startup_indication(priv, &wsm_buf); break; case 0x0804: - ret = wsm_receive_indication(priv, &wsm_buf, skb_p); + ret = wsm_receive_indication(priv, link_id, + &wsm_buf, skb_p); break; case 0x0805: ret = wsm_event_indication(priv, &wsm_buf); @@ -1309,7 +1312,8 @@ out: static bool wsm_handle_tx_data(struct cw1200_common *priv, const struct wsm_tx *wsm, - const struct ieee80211_tx_info *tx_info) + const struct ieee80211_tx_info *tx_info, + int *link_id) { bool handled = false; const struct ieee80211_hdr *frame = @@ -1344,6 +1348,24 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, case NL80211_IFTYPE_AP: if (unlikely(!priv->join_status)) action = doDrop; + if (*link_id == CW1200_LINK_ID_AFTER_DTIM) + *link_id = 0; + else if (WARN_ON(!(BIT(*link_id) & + (BIT(0) | priv->link_id_map)))) + action = doDrop; + if (cw1200_queue_get_generation(wsm->packetID) > + CW1200_MAX_REQUEUE_ATTEMPTS) { + /* HACK!!! WSM324 firmware has tendency to requeue + * multicast frames in a loop, causing performance + * drop and high power consumption of the driver. + * In this situation it is better just to drop + * the problematic frame. */ + wiphy_warn(priv->hw->wiphy, + "Too many attempts " + "to requeue a frame. " + "Frame is dropped.\n"); + action = doDrop; + } break; case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MESH_POINT: @@ -1550,8 +1572,8 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, struct wsm_tx *wsm = NULL; struct ieee80211_tx_info *tx_info; struct cw1200_queue *queue; - struct cw1200_sta_priv *sta_priv; u32 tx_allowed_mask = 0; + int link_id; /* * Count was intended as an input for wsm->more flag. * During implementation it was found that wsm->more @@ -1600,26 +1622,22 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, if (cw1200_queue_get(queue, tx_allowed_mask, - &wsm, &tx_info)) + &wsm, &tx_info, &link_id)) continue; - if (wsm_handle_tx_data(priv, wsm, tx_info)) + if (wsm_handle_tx_data(priv, wsm, tx_info, &link_id)) continue; /* Handled by WSM */ - if (tx_info->control.sta) { - /* Update link id */ - sta_priv = (struct cw1200_sta_priv *) - &tx_info->control.sta->drv_priv; - wsm->hdr.id &= __cpu_to_le16( - ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX)); - wsm->hdr.id |= cpu_to_le16( - WSM_TX_LINK_ID(sta_priv->link_id)); - priv->pspoll_mask &= ~BIT(sta_priv->link_id); - } + wsm->hdr.id &= __cpu_to_le16( + ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX)); + wsm->hdr.id |= cpu_to_le16( + WSM_TX_LINK_ID(link_id)); + priv->pspoll_mask &= ~BIT(link_id); *data = (u8 *)wsm; *tx_len = __le16_to_cpu(wsm->hdr.len); + if (more) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) &wsm[1]; diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 6b6d4a8049e..e9f7c922994 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -807,6 +807,9 @@ struct wsm_rx { /* Size of the frame */ /* [out] */ size_t frame_size; + + /* Link ID */ + /* [out] */ int link_id; }; /* = sizeof(generic hi hdr) + sizeof(wsm hdr) */ -- cgit v1.2.3 From bd9be234a64233eb83d0d691f7f41684eb881057 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 10 Aug 2011 09:11:16 +0200 Subject: cw1200: Check p2p flag for wsm join and start. We should check p2p flag setiing in the vif. p2p flag is used as a hint in WSM join and start requests and should be set for P2P GO and CLIENT. ST-Ericsson ID: 359041 Change-Id: I562616674d8e3b354e3c8c59f300516c3318a970 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29784 Reviewed-by: QABUILD Tested-by: Janusz DZIEDZIC Reviewed-by: QATEST Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33519 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 3 ++- drivers/staging/cw1200/sta.c | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 20a1d61690e..494403dd28d 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -698,7 +698,8 @@ static int cw1200_start_ap(struct cw1200_common *priv) int ret; struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; struct wsm_start start = { - .mode = WSM_START_MODE_AP, + .mode = priv->vif->p2p ? + WSM_START_MODE_P2P_GO : WSM_START_MODE_AP, .band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G, .channelNumber = priv->channel->hw_value, diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index b368c0b820e..f7be0b8d63d 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1113,6 +1113,9 @@ void cw1200_join_work(struct work_struct *work) memcpy(&join.ssid[0], &ssidie[2], join.ssidLength); } + if (priv->vif->p2p) + join.flags |= WSM_JOIN_FLAGS_P2P_GO; + wsm_flush_tx(priv); WARN_ON(wsm_set_block_ack_policy(priv, -- cgit v1.2.3 From 162d6725feb0d48f36e2ce301731b97755e2733b Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 2 Sep 2011 09:04:34 +0200 Subject: cw1200: Don't allow scan when in MiniAP mode We should not allow scan (p2p_find) when firmware works in AP mode. ST-Ericsson ID: 359597 Change-Id: I2c48405a765ed5d5f569d02ea748865c1641b61f Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29981 Reviewed-by: QABUILD Reviewed-by: Janusz DZIEDZIC Tested-by: Janusz DZIEDZIC Reviewed-by: Dmitry TARNYAGIN Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33520 Tested-by: Dmitry TARNYAGIN Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/scan.c | 4 ++++ drivers/staging/cw1200/sta.c | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index c9210e57397..11de0db44de 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -51,6 +51,10 @@ int cw1200_hw_scan(struct ieee80211_hw *hw, if (!priv->vif) return -EINVAL; + /* Scan when P2P_GO corrupt firmware MiniAP mode */ + if (priv->join_status == CW1200_JOIN_STATUS_AP) + return -EOPNOTSUPP; + if (req->n_ssids == 1 && !req->ssids[0].ssid_len) req->n_ssids = 0; diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index f7be0b8d63d..10de50132d9 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -763,11 +763,14 @@ int __cw1200_flush(struct cw1200_common *priv, bool drop) } for (;;) { + /* TODO: correct flush handlin is required when dev_stop. + * Temporary workaround: 2s + */ ret = wait_event_timeout( priv->tx_queue_stats.wait_link_id_empty, cw1200_queue_stats_is_empty( &priv->tx_queue_stats, -1), - 10 * HZ); + 2 * HZ); if (unlikely(ret <= 0)) { if (!ret) -- cgit v1.2.3 From ad0c5204e8009f9349693a95d6a350c41359583a Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 2 Sep 2011 08:58:33 +0200 Subject: cw1200: Re-enable U-APSD support in driver Configuration is now done in upper layers. ST-Ericsson ID: 355584 Signed-off-by: Janusz Dziedzic Change-Id: I96eff15907ea881e33446fef74a7acdd2b02655d Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29979 Reviewed-by: Bartosz MARKOWSKI Reviewed-by: QATEST Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33521 Tested-by: Dmitry TARNYAGIN Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 57f90e9168a..95149da6081 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -252,9 +252,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | IEEE80211_HW_AP_LINK_PS | - /* TODO: Fix this - Disable UAPSD support due to performance drop */ - /* IEEE80211_HW_SUPPORTS_UAPSD | */ + IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_CQM_RSSI | /* Aggregation is fully controlled by firmware. -- cgit v1.2.3 From 83edd0839426615fdc9c2c9c78a7cb6a8fee919e Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 14 Sep 2011 14:38:16 +0200 Subject: cw1200: Enable quiescent mode. Device is consuming 8 mA in doze mode and 0.16 - 1.07 mA in quiescent. ST-Ericsson ID: 359619 Change-Id: Ifeafdca18441586bbd65cc0886b088dc83c6dd87 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/31449 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33522 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/sta.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 10de50132d9..155161bbac5 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -379,9 +379,7 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) if (changed & IEEE80211_CONF_CHANGE_IDLE) { struct wsm_operational_mode mode = { - .power_mode = (conf->flags & IEEE80211_CONF_IDLE) ? - wsm_power_mode_quiescent : - wsm_power_mode_doze, + .power_mode = wsm_power_mode_quiescent, .disableMoreFlagUsage = true, }; wsm_lock_tx(priv); -- cgit v1.2.3 From c051a3c634f81f63930454dfbbef926b6bd2b2aa Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Tue, 20 Sep 2011 12:39:33 +0200 Subject: cw1200: Remove confusing warnings When bus error occurs, there is an existing recovery from this situation. These traces are confusining only and do not have any other impact. ST-Ericsson ID: 355083 Change-Id: I710bc79cdfe7d787994cfd1842ba1293756f968e Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/31468 Reviewed-by: QABUILD Reviewed-by: QATEST Reviewed-by: Dmitry TARNYAGIN Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33523 Tested-by: Dmitry TARNYAGIN Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/bh.c | 4 ---- drivers/staging/cw1200/cw1200_sdio.c | 8 +------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index 4c4a3bf86ad..231213a9cda 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -206,10 +206,6 @@ static int cw1200_bh_read_ctrl_reg(struct cw1200_common *priv, if (ret) printk(KERN_ERR "[BH] Failed to read control register.\n"); - else - printk(KERN_WARNING - "[BH] Second attempt to read control " - "register passed. This is a firmware bug.\n"); } return ret; diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index 279dff144ae..1257e68648a 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -50,13 +50,7 @@ static int cw1200_sdio_memcpy_fromio(struct sbus_priv *self, unsigned int addr, void *dst, int count) { - int ret = sdio_memcpy_fromio(self->func, dst, addr, count); - if (ret) { - printk(KERN_ERR "!!! Can't read %d bytes from 0x%.8X." - " Err %d.\n", - count, addr, ret); - } - return ret; + return sdio_memcpy_fromio(self->func, dst, addr, count); } static int cw1200_sdio_memcpy_toio(struct sbus_priv *self, -- cgit v1.2.3 From f012e49f7a56fd3f7ef74a3c0139341419fc37b2 Mon Sep 17 00:00:00 2001 From: Marek Puzyniak Date: Mon, 19 Sep 2011 15:16:51 +0200 Subject: cw1200: Enable Block ACK Policy. Block ACK Policy was not enabled after unjoin in cw1200 driver. ST-Ericsson ID: 358471 ST-Ericsson FOSS-OUT ID: NA Signed-of-by: Marek Puzyniak Change-Id: Idcf5b2b6e582c15f33ea5e9ef1e03af9f6aefe4b Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/31376 Reviewed-by: Marek PUZYNIAK Tested-by: Marek PUZYNIAK Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33524 Reviewed-by: Philippe LANGLAIS Tested-by: Dmitry TARNYAGIN --- drivers/staging/cw1200/sta.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 155161bbac5..8bc86bbd193 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1119,9 +1119,6 @@ void cw1200_join_work(struct work_struct *work) wsm_flush_tx(priv); - WARN_ON(wsm_set_block_ack_policy(priv, - priv->ba_tid_mask, priv->ba_tid_mask)); - /* Queue unjoin if not associated in 3 sec. */ queue_delayed_work(priv->workqueue, &priv->join_timeout, 3 * HZ); @@ -1142,6 +1139,8 @@ void cw1200_join_work(struct work_struct *work) __le32_to_cpu(wsm->packetID)); priv->join_status = CW1200_JOIN_STATUS_STA; } + WARN_ON(wsm_set_block_ack_policy(priv, + priv->ba_tid_mask, priv->ba_tid_mask)); cw1200_update_filtering(priv); } mutex_unlock(&priv->conf_mutex); @@ -1202,6 +1201,8 @@ void cw1200_unjoin_work(struct work_struct *work) cancel_work_sync(&priv->event_handler); cancel_delayed_work_sync(&priv->connection_loss_work); cw1200_update_listening(priv, priv->listening); + WARN_ON(wsm_set_block_ack_policy(priv, + priv->ba_tid_mask, priv->ba_tid_mask)); cw1200_update_filtering(priv); priv->setbssparams_done = false; -- cgit v1.2.3 From d98537f2a26890b13013e1bd9f67fe63d6dd6e42 Mon Sep 17 00:00:00 2001 From: Piotr Nakraszewicz Date: Thu, 22 Sep 2011 13:42:54 +0200 Subject: cw1200: cancel pending timer in cw1200_stop Cancel pending mcast_timeout in cw1200_stop to prevent kernel crash. ST-Ericsson ID: 360744 ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Piotr Nakraszewicz Change-Id: I149f79d04718c606da97ccd9e3a0e599bba26e49 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/31746 Tested-by: Piotr NAKRASZEWICZ Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33525 Reviewed-by: Philippe LANGLAIS Tested-by: Dmitry TARNYAGIN --- drivers/staging/cw1200/sta.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 8bc86bbd193..3a0d274c4d9 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -127,6 +127,9 @@ void cw1200_stop(struct ieee80211_hw *dev) cancel_delayed_work_sync(&priv->connection_loss_work); cancel_delayed_work_sync(&priv->link_id_gc_work); + if (timer_pending(&priv->mcast_timeout)) + del_timer_sync(&priv->mcast_timeout); + mutex_lock(&priv->conf_mutex); switch (priv->join_status) { case CW1200_JOIN_STATUS_STA: -- cgit v1.2.3 From d58ea2660d9668119f1785d1864d85aea6de08e7 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Fri, 2 Sep 2011 10:48:48 +0200 Subject: cw1200: Kernel 3.0 adaptations Change set_irq_wake to irq_set_irq_wake ST-Ericsson ID: 352334 ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Bartosz Markowski Change-Id: I5c6460dc9f414ce679b22b42d39a1037769deb4b Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30009 Reviewed-by: QATOOLS Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33526 Reviewed-by: Philippe LANGLAIS Tested-by: Dmitry TARNYAGIN --- drivers/staging/cw1200/cw1200_sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index 1257e68648a..fb594ff1567 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -297,7 +297,7 @@ static int cw1200_sdio_pm(struct sbus_priv *self, bool suspend) sdio_release_host(func); if (!ret && irq) - ret = set_irq_wake(irq->start, suspend); + ret = irq_set_irq_wake(irq->start, suspend); return ret; } -- cgit v1.2.3 From fb0075e295de25aa1d5ec5922907a08b431d0efc Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 22 Sep 2011 13:03:45 +0200 Subject: cw1200: configure_filter callback must be serialized with scan. It is not allowed to configure filter when scan is in progress. It confuses both firmware and driver state machines. Fix protects configure_filter by a scan lock. ST-Ericsson ID: 357819 Change-Id: I46dedb266b5ffa64b66372e24d2fdde74cfec348 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/31745 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33527 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/sta.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 3a0d274c4d9..1c2607d1a51 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -487,6 +487,7 @@ void cw1200_configure_filter(struct ieee80211_hw *dev, FIF_BCN_PRBRESP_PROMISC | FIF_PROBE_REQ; + down(&priv->scan.lock); mutex_lock(&priv->conf_mutex); priv->rx_filter.promiscuous = (*total_flags & FIF_PROMISC_IN_BSS) @@ -506,6 +507,7 @@ void cw1200_configure_filter(struct ieee80211_hw *dev, } cw1200_update_filtering(priv); mutex_unlock(&priv->conf_mutex); + up(&priv->scan.lock); } int cw1200_conf_tx(struct ieee80211_hw *dev, u16 queue, -- cgit v1.2.3 From 6d949e5cc9827908585b973c1db4e3a41be4a114 Mon Sep 17 00:00:00 2001 From: Amit Shakya Date: Wed, 21 Sep 2011 14:41:58 +0530 Subject: cw1200: Add BT Coexistence support BT Coexistence support required setting priority to frames for PTA arbitration FW, parsing SDD file for getting the listen interval and using the same in assoc request, modifying Tx rate for PSPOLL and NULL templates (plumbing them) ST-Ericsson ID: 357776 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-10091 Change-Id: I14f05cbcc2f02b85f72dbe820893cef9c3775df7 Signed-off-by: Amit Shakya Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/31602 Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33528 Reviewed-by: Philippe LANGLAIS Tested-by: Dmitry TARNYAGIN --- drivers/staging/cw1200/ap.c | 95 +++++++++++++++++++++++++++++++++++++++++ drivers/staging/cw1200/cw1200.h | 7 ++- drivers/staging/cw1200/queue.c | 5 ++- drivers/staging/cw1200/queue.h | 3 +- drivers/staging/cw1200/sta.c | 75 ++++++++++++++++++++++++++++++++ drivers/staging/cw1200/txrx.c | 67 ++++++++++++++++++++++++++++- drivers/staging/cw1200/txrx.h | 8 +++- drivers/staging/cw1200/wsm.h | 30 +++++++++++++ 8 files changed, 282 insertions(+), 8 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 494403dd28d..aabca5ca06e 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -23,6 +23,8 @@ #define CW1200_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ)) static int cw1200_upload_beacon(struct cw1200_common *priv); +static int cw1200_upload_pspoll(struct cw1200_common *priv); +static int cw1200_upload_null(struct cw1200_common *priv); static int cw1200_start_ap(struct cw1200_common *priv); static int cw1200_update_beaconing(struct cw1200_common *priv); static int cw1200_enable_beaconing(struct cw1200_common *priv, @@ -195,6 +197,56 @@ int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, return 0; } +static int cw1200_set_btcoexinfo(struct cw1200_common *priv) +{ + struct wsm_override_internal_txrate arg; + int ret = 0; + + if (priv->mode == NL80211_IFTYPE_STATION) { + /* Plumb PSPOLL and NULL template */ + WARN_ON(cw1200_upload_pspoll(priv)); + WARN_ON(cw1200_upload_null(priv)); + } else { + return 0; + } + + memset(&arg, 0, sizeof(struct wsm_override_internal_txrate)); + + if (!priv->vif->p2p) { + /* STATION mode */ + if (priv->bss_params.operationalRateSet & ~0xF) { + ap_printk(KERN_DEBUG "[STA] STA has ERP rates\n"); + /* G or BG mode */ + arg.internalTxRate = (__ffs( + priv->bss_params.operationalRateSet & ~0xF)); + } else { + ap_printk(KERN_DEBUG "[STA] STA has non ERP rates\n"); + /* B only mode */ + arg.internalTxRate = (__ffs( + priv->association_mode.basicRateSet)); + } + arg.nonErpInternalTxRate = (__ffs( + priv->association_mode.basicRateSet)); + } else { + /* P2P mode */ + arg.internalTxRate = (__ffs( + priv->bss_params.operationalRateSet & ~0xF)); + arg.nonErpInternalTxRate = (__ffs( + priv->bss_params.operationalRateSet & ~0xF)); + } + + ap_printk(KERN_DEBUG "[STA] BTCOEX_INFO" + "MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n", + priv->mode, + arg.internalTxRate, + arg.nonErpInternalTxRate); + + ret = WARN_ON(wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, + &arg, sizeof(arg))); + + return ret; +} + void cw1200_bss_info_changed(struct ieee80211_hw *dev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, @@ -389,6 +441,9 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, WARN_ON(wsm_set_beacon_wakeup_period(priv, dtim_interval, listen_interval)); cw1200_set_pm(priv, &priv->powersave_mode); + + if (priv->is_BT_Present) + WARN_ON(cw1200_set_btcoexinfo(priv)); #if 0 /* It's better to override internal TX rete; otherwise * device sends RTS at too high rate. However device @@ -683,6 +738,46 @@ static int cw1200_upload_beacon(struct cw1200_common *priv) return ret; } +static int cw1200_upload_pspoll(struct cw1200_common *priv) +{ + int ret = 0; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PS_POLL, + .rate = 0xFF, + }; + + + frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif); + if (WARN_ON(!frame.skb)) + return -ENOMEM; + + ret = wsm_set_template_frame(priv, &frame); + + dev_kfree_skb(frame.skb); + + return ret; +} + +static int cw1200_upload_null(struct cw1200_common *priv) +{ + int ret = 0; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_NULL, + .rate = 0xFF, + }; + + + frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif); + if (WARN_ON(!frame.skb)) + return -ENOMEM; + + ret = wsm_set_template_frame(priv, &frame); + + dev_kfree_skb(frame.skb); + + return ret; +} + static int cw1200_enable_beaconing(struct cw1200_common *priv, bool enable) { diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 85d7caf89e9..400fd4a44aa 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -133,8 +133,11 @@ struct cw1200_common { struct wsm_multicast_filter multicast_filter; struct cw1200_pm_state pm_state; struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo; - struct wsm_uapsd_info uapsd_info; - bool setbssparams_done; + struct wsm_uapsd_info uapsd_info; + bool setbssparams_done; + bool is_BT_Present; + u8 conf_listen_interval; + u32 listen_interval; /* BH */ atomic_t bh_rx; diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c index c6eabd12c56..e09984583f6 100644 --- a/drivers/staging/cw1200/queue.c +++ b/drivers/staging/cw1200/queue.c @@ -199,14 +199,15 @@ size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, } int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, - struct sk_buff *skb, u8 link_id) + struct sk_buff *skb, struct tx_info *txinfo) { int ret; struct wsm_tx *wsm; struct cw1200_queue_stats *stats = queue->stats; + u8 link_id = txinfo->link_id; wsm = (struct wsm_tx *)skb_push(skb, sizeof(struct wsm_tx)); - ret = cw1200_skb_to_wsm(priv, skb, wsm); + ret = cw1200_skb_to_wsm(priv, skb, wsm, txinfo); if (ret) return ret; diff --git a/drivers/staging/cw1200/queue.h b/drivers/staging/cw1200/queue.h index b28d0294d25..4abea64aa66 100644 --- a/drivers/staging/cw1200/queue.h +++ b/drivers/staging/cw1200/queue.h @@ -18,6 +18,7 @@ /* extern */ struct wsm_tx; /* extern */ struct cw1200_common; /* extern */ struct ieee80211_tx_queue_stats; +/* extern */ struct tx_info; /* forward */ struct cw1200_queue_stats; @@ -60,7 +61,7 @@ void cw1200_queue_deinit(struct cw1200_queue *queue); size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, u32 link_id_map); int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *cw1200, - struct sk_buff *skb, u8 link_id); + struct sk_buff *skb, struct tx_info *txinfo); int cw1200_queue_get(struct cw1200_queue *queue, u32 link_id_map, struct wsm_tx **tx, diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 1c2607d1a51..ed453c2372b 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -945,6 +945,66 @@ void cw1200_tx_failure_work(struct work_struct *work) /* ******************************************************************** */ /* Internal API */ + + +/* +* This function is called to Parse the SDD file + *to extract listen_interval and PTA related information +*/ +static int cw1200_parse_SDD_file(struct cw1200_common *priv) +{ + u8 *sdd_data = (u8 *)priv->sdd->data; + struct cw1200_sdd { + u8 id ; + u8 length ; + u8 data[] ; + } *pElement; + int parsedLength = 0; + #define SDD_PTA_CFG_ELT_ID 0xEB + #define FIELD_OFFSET(type, field) ((u8 *)&((type*)0)->field - (u8 *)0) + + priv->is_BT_Present = false; + + pElement = (struct cw1200_sdd *)sdd_data; + + pElement = (struct cw1200_sdd *)((u8*)pElement + + FIELD_OFFSET(struct cw1200_sdd, data) + pElement->length); + + parsedLength += (FIELD_OFFSET(struct cw1200_sdd, data) + + pElement->length); + + while (parsedLength <= priv->sdd->size) { + switch (pElement->id) { + case SDD_PTA_CFG_ELT_ID: + { + priv->conf_listen_interval = + (*((u16 *)pElement->data+1) >> 7) & 0x1F; + priv->is_BT_Present = true; + sta_printk(KERN_DEBUG "PTA element found.\n"); + sta_printk(KERN_DEBUG "Listen Interval %d\n", + priv->conf_listen_interval); + } + break; + + default: + break; + } + + pElement = (struct cw1200_sdd *) + ((u8 *)pElement + FIELD_OFFSET(struct cw1200_sdd, data) + + pElement->length); + parsedLength += + (FIELD_OFFSET(struct cw1200_sdd, data) + pElement->length); + } + + if (priv->is_BT_Present == false) { + sta_printk(KERN_DEBUG "PTA element NOT found.\n"); + priv->conf_listen_interval = 0; + } + return 0; +} + + int cw1200_setup_mac(struct cw1200_common *priv) { /* TBD: Do you know how to assing MAC address without @@ -1002,6 +1062,8 @@ int cw1200_setup_mac(struct cw1200_common *priv) cfg.dpdData = priv->sdd->data; cfg.dpdData_size = priv->sdd->size; ret = WARN_ON(wsm_configuration(priv, &cfg)); + /* Parse SDD file for PTA element */ + cw1200_parse_SDD_file(priv); } if (ret) return ret; @@ -1098,6 +1160,19 @@ void cw1200_join_work(struct work_struct *work) .basicRateSet = 7, }; + /* BT Coex related changes */ + if (priv->is_BT_Present) { + if (((priv->conf_listen_interval * 100) % + bss->beacon_interval) == 0) + priv->listen_interval = + ((priv->conf_listen_interval * 100) / + bss->beacon_interval); + else + priv->listen_interval = + ((priv->conf_listen_interval * 100) / + bss->beacon_interval + 1); + } + if (tim && tim->dtim_period > 1) { join.dtimPeriod = tim->dtim_period; priv->join_dtim_period = tim->dtim_period; diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 1742af0ac8e..3e8b84fa43c 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -371,12 +371,13 @@ cw1200_get_tx_rate(const struct cw1200_common *priv, /* NOTE: cw1200_skb_to_wsm executes in atomic context. */ int cw1200_skb_to_wsm(struct cw1200_common *priv, struct sk_buff *skb, - struct wsm_tx *wsm) + struct wsm_tx *wsm, struct tx_info *txinfo) { bool tx_policy_renew = false; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); const struct ieee80211_rate *rate = cw1200_get_tx_rate(priv, &tx_info->control.rates[0]); + u8 priority = 0; memset(wsm, 0, sizeof(*wsm)); wsm->hdr.len = __cpu_to_le16(skb->len); @@ -409,6 +410,32 @@ int cw1200_skb_to_wsm(struct cw1200_common *priv, struct sk_buff *skb, } wsm->queueId = wsm_queue_id_to_wsm(skb_get_queue_mapping(skb)); + + /* BT Coex specific handling */ + if (priv->is_BT_Present) { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *)(skb->data + sizeof(struct wsm_tx)); + + if (cpu_to_be16(txinfo->ethertype) == ETH_P_PAE) + priority = WSM_EPTA_PRIORITY_EAPOL; + else if (ieee80211_is_action(hdr->frame_control)) + priority = WSM_EPTA_PRIORITY_ACTION; + else if (ieee80211_is_mgmt(hdr->frame_control)) + priority = WSM_EPTA_PRIORITY_MGT; + else if ((wsm->queueId == WSM_QUEUE_VOICE)) + priority = WSM_EPTA_PRIORITY_VOICE; + else if ((wsm->queueId == WSM_QUEUE_VIDEO)) + priority = WSM_EPTA_PRIORITY_VIDEO; + else + priority = WSM_EPTA_PRIORITY_DATA; + + txrx_printk(KERN_DEBUG "[TX] EPTA priority %x.\n", + ((priority) & 0x7)); + + /* Set EPTA priority */ + wsm->flags |= (((priority) & 0x7) << 1); + } + return 0; } @@ -427,6 +454,7 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) int link_id; int ret; int i; + struct tx_info txinfo; if (likely(tx_info->control.sta && sta_priv->link_id)) link_id = sta_priv->link_id; @@ -465,6 +493,39 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) spin_unlock_bh(&priv->buffered_multicasts_lock); } + /* BT Coex support related configuration */ + if (priv->is_BT_Present) { + txinfo.ethertype = 0; + + if (ieee80211_is_data_qos(hdr->frame_control) || + ieee80211_is_data(hdr->frame_control)) { + unsigned int headerlen = + ieee80211_get_hdrlen_from_skb(skb); + + /* Skip LLC SNAP header (+6) */ + if (headerlen > 0) + txinfo.ethertype = + *((u16 *)(skb->data + headerlen + 6)); + } + else if (ieee80211_is_assoc_req(hdr->frame_control) || + ieee80211_is_reassoc_req(hdr->frame_control)) { + struct ieee80211_mgmt *mgt_frame = + (struct ieee80211_mgmt *)skb->data; + + if (mgt_frame->u.assoc_req.listen_interval < + priv->listen_interval) { + txrx_printk(KERN_DEBUG + "Modified Listen Interval to %x from %x\n", + priv->listen_interval, + mgt_frame->u.assoc_req.listen_interval); + /* Replace listen interval derieved from + the one read from SDD */ + mgt_frame->u.assoc_req.listen_interval = + priv->listen_interval; + } + } + } + /* IV/ICV injection. */ /* TODO: Quite unoptimal. It's better co modify mac80211 * to reserve space for IV */ @@ -543,8 +604,10 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) queue_work(priv->workqueue, &priv->multicast_start_work); } + + txinfo.link_id = link_id; ret = cw1200_queue_put(&priv->tx_queue[queue], priv, skb, - link_id); + &txinfo); spin_unlock_bh(&priv->buffered_multicasts_lock); if (!WARN_ON(ret)) diff --git a/drivers/staging/cw1200/txrx.h b/drivers/staging/cw1200/txrx.h index 12ec3d9808e..e41ac43bc7f 100644 --- a/drivers/staging/cw1200/txrx.h +++ b/drivers/staging/cw1200/txrx.h @@ -43,6 +43,11 @@ struct tx_policy_cache { spinlock_t lock; }; +struct tx_info { + u8 link_id; + u16 ethertype; +}; + /* ******************************************************************** */ /* TX policy cache */ /* Intention of TX policy cache is an overcomplicated WSM API. @@ -60,7 +65,8 @@ void tx_policy_put(struct cw1200_common *priv, int idx); u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates); int cw1200_skb_to_wsm(struct cw1200_common *priv, - struct sk_buff *skb, struct wsm_tx *wsm); + struct sk_buff *skb, struct wsm_tx *wsm, + struct tx_info *txinfo); void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb); /* ******************************************************************** */ diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index e9f7c922994..adb34765c5c 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -173,6 +173,22 @@ struct cw1200_common; /* STBC allowed */ #define WSM_HT_TX_STBC (BIT(7)) +/* EPTA prioirty flags for BT Coex */ +/* default epta priority */ +#define WSM_EPTA_PRIORITY_DEFAULT 4 +/* use for normal data */ +#define WSM_EPTA_PRIORITY_DATA 4 +/* use for connect/disconnect/roaming*/ +#define WSM_EPTA_PRIORITY_MGT 5 +/* use for action frames */ +#define WSM_EPTA_PRIORITY_ACTION 5 +/* use for AC_VI data */ +#define WSM_EPTA_PRIORITY_VIDEO 5 +/* use for AC_VO data */ +#define WSM_EPTA_PRIORITY_VOICE 6 +/* use for EAPOL exchange */ +#define WSM_EPTA_PRIORITY_EAPOL 7 + /* TX status */ /* Frame was sent aggregated */ /* Only valid for WSM_SUCCESS status. */ @@ -1592,6 +1608,20 @@ static inline int wsm_set_uapsd_info(struct cw1200_common *priv, arg, sizeof(*arg)); } +/* 4.22 OverrideInternalTxRate */ +struct wsm_override_internal_txrate { + u8 internalTxRate; + u8 nonErpInternalTxRate; + u8 reserved[2]; +} __packed; + +static inline int wsm_set_override_internal_txrate(struct cw1200_common *priv, + struct wsm_override_internal_txrate *arg) +{ + return wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, + arg, sizeof(*arg)); +} + /* ******************************************************************** */ /* WSM TX port control */ -- cgit v1.2.3 From d111ee3539a31eb10d778cd626b8d583b38c39cb Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Tue, 27 Sep 2011 08:27:19 +0200 Subject: cw1200: UAPSD in AP mode is implemented. U-APSD in SoftAP is fully offloaded to the mac80211 wireless stack. Driver provides a transparent virtual link (with link_id = CW1200_LINK_ID_UAPSD) which bypases powersave buffering. Mac80211 stack takes care to provide UAPSD data when it is needed. ST-Ericsson ID: 355584 Change-Id: Iabd65e7effcecc5564e76e05e081b8f6a98b4ddb Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30661 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33529 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/cw1200.h | 2 ++ drivers/staging/cw1200/debug.c | 2 +- drivers/staging/cw1200/main.c | 4 +++- drivers/staging/cw1200/queue.c | 8 +++++--- drivers/staging/cw1200/queue.h | 2 +- drivers/staging/cw1200/txrx.c | 41 ++++++++++++++++++++++++----------------- drivers/staging/cw1200/wsm.c | 9 ++++----- 7 files changed, 40 insertions(+), 28 deletions(-) diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 400fd4a44aa..24bf929e4a3 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -46,6 +46,8 @@ #define CW1200_MAX_STA_IN_AP_MODE (5) #define CW1200_LINK_ID_AFTER_DTIM (CW1200_MAX_STA_IN_AP_MODE + 1) +#define CW1200_LINK_ID_UAPSD (CW1200_MAX_STA_IN_AP_MODE + 2) +#define CW1200_LINK_ID_MAX (CW1200_MAX_STA_IN_AP_MODE + 3) #define CW1200_MAX_REQUEUE_ATTEMPTS (5) /* Please keep order */ diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 2d7adfe8b4c..2cfb0ee17cb 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -95,7 +95,7 @@ static void cw1200_debug_print_map(struct seq_file *seq, seq_printf(seq, "%s0-> ", label); for (i = 0; i < priv->tx_queue_stats.map_capacity; ++i) seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : ".."); - seq_printf(seq, "<-%d\n", priv->tx_queue_stats.map_capacity); + seq_printf(seq, "<-%d\n", priv->tx_queue_stats.map_capacity - 1); } static int cw1200_status_show(struct seq_file *seq, void *v) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 95149da6081..d8e4ecf1af4 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -277,6 +277,8 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) WIPHY_WOWLAN_DISCONNECT; hw->wiphy->wowlan.n_patterns = 0; + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_UAPSD; + hw->channel_change_time = 1000; /* TODO: find actual value */ /* priv->beacon_req_id = cpu_to_le32(0); */ hw->queues = 4; @@ -341,7 +343,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) } if (unlikely(cw1200_queue_stats_init(&priv->tx_queue_stats, - CW1200_LINK_ID_AFTER_DTIM + 1))) { + CW1200_LINK_ID_MAX))) { ieee80211_free_hw(hw); return NULL; } diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c index e09984583f6..2ba26f32178 100644 --- a/drivers/staging/cw1200/queue.c +++ b/drivers/staging/cw1200/queue.c @@ -28,7 +28,8 @@ */ u8 generation; u8 link_id; - u8 reserved[2]; + u8 raw_link_id; + u8 reserved[1]; }; static inline void __cw1200_queue_lock(struct cw1200_queue *queue, @@ -199,7 +200,7 @@ size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, } int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, - struct sk_buff *skb, struct tx_info *txinfo) + struct sk_buff *skb, struct tx_info *txinfo, u8 raw_link_id) { int ret; struct wsm_tx *wsm; @@ -228,6 +229,7 @@ int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, item->generation, item - queue->pool); wsm->packetID = __cpu_to_le32(item->packetID); item->link_id = link_id; + item->raw_link_id = raw_link_id; ++queue->num_queued; ++queue->link_map_cache[link_id]; @@ -270,7 +272,7 @@ int cw1200_queue_get(struct cw1200_queue *queue, if (!WARN_ON(ret)) { *tx = (struct wsm_tx *)item->skb->data; *tx_info = IEEE80211_SKB_CB(item->skb); - *link_id = item->link_id; + *link_id = item->raw_link_id; list_move_tail(&item->head, &queue->pending); ++queue->num_pending; --queue->link_map_cache[item->link_id]; diff --git a/drivers/staging/cw1200/queue.h b/drivers/staging/cw1200/queue.h index 4abea64aa66..3ddfdaf910e 100644 --- a/drivers/staging/cw1200/queue.h +++ b/drivers/staging/cw1200/queue.h @@ -61,7 +61,7 @@ void cw1200_queue_deinit(struct cw1200_queue *queue); size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, u32 link_id_map); int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *cw1200, - struct sk_buff *skb, struct tx_info *txinfo); + struct sk_buff *skb, struct tx_info *txinfo, u8 raw_link_id); int cw1200_queue_get(struct cw1200_queue *queue, u32 link_id_map, struct wsm_tx **tx, diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 3e8b84fa43c..5cd1857dd17 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -451,45 +451,52 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) const u8 *da = ieee80211_get_DA(hdr); struct cw1200_sta_priv *sta_priv = (struct cw1200_sta_priv *)&tx_info->control.sta->drv_priv; - int link_id; + int link_id, raw_link_id; int ret; int i; struct tx_info txinfo; if (likely(tx_info->control.sta && sta_priv->link_id)) - link_id = sta_priv->link_id; + raw_link_id = link_id = sta_priv->link_id; else if (priv->mode != NL80211_IFTYPE_AP) - link_id = 0; + raw_link_id = link_id = 0; else if (is_multicast_ether_addr(da)) { - if (priv->enable_beacon) + if (priv->enable_beacon) { + raw_link_id = 0; link_id = CW1200_LINK_ID_AFTER_DTIM; - else - link_id = 0; + } else { + raw_link_id = link_id = 0; + } } else { - link_id = cw1200_find_link_id(priv, da); - if (!link_id) - link_id = cw1200_alloc_link_id(priv, da); - if (!link_id) { + raw_link_id = cw1200_find_link_id(priv, da); + if (!raw_link_id) + raw_link_id = cw1200_alloc_link_id(priv, da); + if (!raw_link_id) { wiphy_err(priv->hw->wiphy, "%s: No more link IDs available.\n", __func__); goto err; } + if (tx_info->control.sta && + (tx_info->control.sta->uapsd_queues & BIT(queue))) + link_id = CW1200_LINK_ID_UAPSD; + else + link_id = raw_link_id; } - if (link_id && link_id <= CW1200_MAX_STA_IN_AP_MODE) - priv->link_id_db[link_id - 1].timestamp = jiffies; + if (raw_link_id) + priv->link_id_db[raw_link_id - 1].timestamp = jiffies; - txrx_printk(KERN_DEBUG "[TX] TX %d bytes (queue: %d, link_id: %d).\n", - skb->len, queue, link_id); + txrx_printk(KERN_DEBUG "[TX] TX %d bytes (queue: %d, link_id: %d (%d)).\n", + skb->len, queue, link_id, raw_link_id); if (WARN_ON(queue >= 4)) goto err; if (unlikely(ieee80211_is_auth(hdr->frame_control))) { spin_lock_bh(&priv->buffered_multicasts_lock); - priv->sta_asleep_mask &= ~BIT(link_id); + priv->sta_asleep_mask &= ~BIT(raw_link_id); for (i = 0; i < 4; ++i) - priv->tx_suspend_mask[i] &= ~BIT(link_id); + priv->tx_suspend_mask[i] &= ~BIT(raw_link_id); spin_unlock_bh(&priv->buffered_multicasts_lock); } @@ -607,7 +614,7 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) txinfo.link_id = link_id; ret = cw1200_queue_put(&priv->tx_queue[queue], priv, skb, - &txinfo); + &txinfo, raw_link_id); spin_unlock_bh(&priv->buffered_multicasts_lock); if (!WARN_ON(ret)) diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 6514b9227a7..b05548f34a8 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1313,7 +1313,7 @@ out: static bool wsm_handle_tx_data(struct cw1200_common *priv, const struct wsm_tx *wsm, const struct ieee80211_tx_info *tx_info, - int *link_id) + int link_id) { bool handled = false; const struct ieee80211_hdr *frame = @@ -1348,9 +1348,7 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, case NL80211_IFTYPE_AP: if (unlikely(!priv->join_status)) action = doDrop; - if (*link_id == CW1200_LINK_ID_AFTER_DTIM) - *link_id = 0; - else if (WARN_ON(!(BIT(*link_id) & + else if (WARN_ON(!(BIT(link_id) & (BIT(0) | priv->link_id_map)))) action = doDrop; if (cw1200_queue_get_generation(wsm->packetID) > @@ -1546,6 +1544,7 @@ static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv, for (i = 0; i < 4; ++i) { queue = &priv->tx_queue[i]; tx_allowed_mask = ~priv->sta_asleep_mask; + tx_allowed_mask |= BIT(CW1200_LINK_ID_UAPSD); if (priv->sta_asleep_mask) { tx_allowed_mask |= ~priv->tx_suspend_mask[i]; tx_allowed_mask |= priv->pspoll_mask; @@ -1625,7 +1624,7 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, &wsm, &tx_info, &link_id)) continue; - if (wsm_handle_tx_data(priv, wsm, tx_info, &link_id)) + if (wsm_handle_tx_data(priv, wsm, tx_info, link_id)) continue; /* Handled by WSM */ wsm->hdr.id &= __cpu_to_le16( -- cgit v1.2.3 From 9c00a614e701ba4b74df7d555afe3304c3de5cf0 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Tue, 27 Sep 2011 08:34:33 +0200 Subject: cw1200: Adaptation to U-APSD/AP support as on Thu, Sep 22, 2011 New mac80211 API for reporting buffered frames in SoftAP mode is supported. SoftAP stability is significantly improved by that. ST-Ericsson ID: 355584 Change-Id: I0f12c71fff05f7f6b79cd508181b7daab6838c7e Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/31927 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33530 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 25 +--------------- drivers/staging/cw1200/cw1200.h | 3 ++ drivers/staging/cw1200/main.c | 3 +- drivers/staging/cw1200/queue.c | 9 ++++-- drivers/staging/cw1200/queue.h | 5 ++-- drivers/staging/cw1200/txrx.c | 64 +++++++++++++++++++++++++++++++++++------ drivers/staging/cw1200/txrx.h | 2 ++ drivers/staging/cw1200/wsm.c | 8 ++++-- 8 files changed, 78 insertions(+), 41 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index aabca5ca06e..f35eef5fae6 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -84,30 +84,6 @@ void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, enum sta_notify_cmd notify_cmd, struct ieee80211_sta *sta) { - struct cw1200_common *priv = dev->priv; - struct cw1200_sta_priv *sta_priv = - (struct cw1200_sta_priv *)&sta->drv_priv; - u32 bit = BIT(sta_priv->link_id); - - spin_lock_bh(&priv->buffered_multicasts_lock); - switch (notify_cmd) { - case STA_NOTIFY_SLEEP: - if (priv->buffered_multicasts && - !priv->sta_asleep_mask) - queue_work(priv->workqueue, - &priv->multicast_start_work); - priv->sta_asleep_mask |= bit; - break; - case STA_NOTIFY_AWAKE: - priv->sta_asleep_mask &= ~bit; - if (priv->tx_multicast && - !priv->sta_asleep_mask) - queue_work(priv->workqueue, - &priv->multicast_stop_work); - cw1200_bh_wakeup(priv); - break; - } - spin_unlock_bh(&priv->buffered_multicasts_lock); } static void __cw1200_ps_notify(struct cw1200_common *priv, @@ -901,6 +877,7 @@ int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac) ret); priv->link_id_db[ret - 1].status = CW1200_LINK_RESERVE; memcpy(&priv->link_id_db[ret - 1].mac, mac, ETH_ALEN); + memset(&priv->link_id_db[ret - 1].buffered, 0, CW1200_MAX_TID); wsm_lock_tx_async(priv); if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) wsm_unlock_tx(priv); diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 24bf929e4a3..90df67952f5 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -50,6 +50,8 @@ #define CW1200_LINK_ID_MAX (CW1200_MAX_STA_IN_AP_MODE + 3) #define CW1200_MAX_REQUEUE_ATTEMPTS (5) +#define CW1200_MAX_TID (8) + /* Please keep order */ enum cw1200_join_status { CW1200_JOIN_STATUS_PASSIVE = 0, @@ -69,6 +71,7 @@ struct cw1200_link_entry { unsigned long timestamp; enum cw1200_link_status status; u8 mac[ETH_ALEN]; + u8 buffered[CW1200_MAX_TID]; bool ps; }; diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index d8e4ecf1af4..a931a059f6f 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -251,7 +251,6 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | - IEEE80211_HW_AP_LINK_PS | IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_CQM_RSSI | @@ -277,7 +276,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) WIPHY_WOWLAN_DISCONNECT; hw->wiphy->wowlan.n_patterns = 0; - hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_UAPSD; + hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; hw->channel_change_time = 1000; /* TODO: find actual value */ /* priv->beacon_req_id = cpu_to_le32(0); */ diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c index 2ba26f32178..ef7d48488bd 100644 --- a/drivers/staging/cw1200/queue.c +++ b/drivers/staging/cw1200/queue.c @@ -29,7 +29,7 @@ u8 generation; u8 link_id; u8 raw_link_id; - u8 reserved[1]; + u8 tid; }; static inline void __cw1200_queue_lock(struct cw1200_queue *queue, @@ -200,7 +200,8 @@ size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, } int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, - struct sk_buff *skb, struct tx_info *txinfo, u8 raw_link_id) + struct sk_buff *skb, struct tx_info *txinfo, + u8 raw_link_id, u8 tid) { int ret; struct wsm_tx *wsm; @@ -230,6 +231,7 @@ int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, wsm->packetID = __cpu_to_le32(item->packetID); item->link_id = link_id; item->raw_link_id = raw_link_id; + item->tid = tid; ++queue->num_queued; ++queue->link_map_cache[link_id]; @@ -409,7 +411,7 @@ int cw1200_queue_remove(struct cw1200_queue *queue, struct cw1200_common *priv, } int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, - struct sk_buff **skb) + struct sk_buff **skb, int *tid) { int ret = 0; u8 queue_generation, queue_id, item_generation, item_id; @@ -431,6 +433,7 @@ int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, ret = -ENOENT; } else { *skb = item->skb; + *tid = item->tid; item->skb = NULL; } spin_unlock_bh(&queue->lock); diff --git a/drivers/staging/cw1200/queue.h b/drivers/staging/cw1200/queue.h index 3ddfdaf910e..d1802c36f02 100644 --- a/drivers/staging/cw1200/queue.h +++ b/drivers/staging/cw1200/queue.h @@ -61,7 +61,8 @@ void cw1200_queue_deinit(struct cw1200_queue *queue); size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, u32 link_id_map); int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *cw1200, - struct sk_buff *skb, struct tx_info *txinfo, u8 raw_link_id); + struct sk_buff *skb, struct tx_info *txinfo, + u8 raw_link_id, u8 tid); int cw1200_queue_get(struct cw1200_queue *queue, u32 link_id_map, struct wsm_tx **tx, @@ -72,7 +73,7 @@ int cw1200_queue_requeue_all(struct cw1200_queue *queue); int cw1200_queue_remove(struct cw1200_queue *queue, struct cw1200_common *priv, u32 packetID); int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, - struct sk_buff **skb); + struct sk_buff **skb, int *tid); void cw1200_queue_lock(struct cw1200_queue *queue, struct cw1200_common *cw1200); void cw1200_queue_unlock(struct cw1200_queue *queue, diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 5cd1857dd17..ef871325eee 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -448,6 +448,8 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + int was_buffered = 0; + int tid = CW1200_MAX_TID; const u8 *da = ieee80211_get_DA(hdr); struct cw1200_sta_priv *sta_priv = (struct cw1200_sta_priv *)&tx_info->control.sta->drv_priv; @@ -477,15 +479,15 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) __func__); goto err; } - if (tx_info->control.sta && - (tx_info->control.sta->uapsd_queues & BIT(queue))) - link_id = CW1200_LINK_ID_UAPSD; - else - link_id = raw_link_id; + link_id = raw_link_id; } if (raw_link_id) priv->link_id_db[raw_link_id - 1].timestamp = jiffies; + if (tx_info->control.sta && + (tx_info->control.sta->uapsd_queues & BIT(queue))) + link_id = CW1200_LINK_ID_UAPSD; + txrx_printk(KERN_DEBUG "[TX] TX %d bytes (queue: %d, link_id: %d (%d)).\n", skb->len, queue, link_id, raw_link_id); @@ -498,6 +500,13 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) for (i = 0; i < 4; ++i) priv->tx_suspend_mask[i] &= ~BIT(raw_link_id); spin_unlock_bh(&priv->buffered_multicasts_lock); + } else if (ieee80211_is_data_qos(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control)) { + u8 *qos = ieee80211_get_qos_ctl(hdr); + tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK; + } else if (ieee80211_is_data(hdr->frame_control) || + ieee80211_is_nullfunc(hdr->frame_control)) { + tid = 0; } /* BT Coex support related configuration */ @@ -613,10 +622,19 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) } txinfo.link_id = link_id; + + if (raw_link_id && tid < CW1200_MAX_TID) + was_buffered = priv->link_id_db[raw_link_id - 1] + .buffered[tid]++; + ret = cw1200_queue_put(&priv->tx_queue[queue], priv, skb, - &txinfo, raw_link_id); + &txinfo, raw_link_id, tid); + spin_unlock_bh(&priv->buffered_multicasts_lock); + if (raw_link_id && !was_buffered && tid < CW1200_MAX_TID) + ieee80211_sta_set_buffered(tx_info->control.sta, tid, true); + if (!WARN_ON(ret)) cw1200_bh_wakeup(priv); else @@ -721,6 +739,7 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, u8 queue_id = cw1200_queue_get_queue_id(arg->packetID); struct cw1200_queue *queue = &priv->tx_queue[queue_id]; struct sk_buff *skb; + int tid = CW1200_MAX_TID; txrx_printk(KERN_DEBUG "[TX] TX confirm.\n"); @@ -750,7 +769,7 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, WARN_ON(cw1200_queue_requeue(queue, arg->packetID)); } else if (!WARN_ON(cw1200_queue_get_skb( - queue, arg->packetID, &skb))) { + queue, arg->packetID, &skb, &tid))) { struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb); struct wsm_tx *wsm_tx = (struct wsm_tx *)skb->data; int rate_id = (wsm_tx->flags >> 4) & 0x07; @@ -802,12 +821,41 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, } skb_pull(skb, sizeof(struct wsm_tx)); + cw1200_notify_buffered_tx(priv, skb, arg->link_id, tid); ieee80211_tx_status(priv->hw, skb); - WARN_ON(cw1200_queue_remove(queue, priv, arg->packetID)); } } +void cw1200_notify_buffered_tx(struct cw1200_common *priv, + struct sk_buff *skb, int link_id, int tid) +{ + struct ieee80211_sta *sta; + struct ieee80211_hdr *hdr; + u8 *buffered; + u8 still_buffered = 0; + + if (link_id && tid < CW1200_MAX_TID) { + buffered = priv->link_id_db + [link_id - 1].buffered; + + spin_lock_bh(&priv->buffered_multicasts_lock); + if (!WARN_ON(!buffered[tid])) + still_buffered = --buffered[tid]; + spin_unlock_bh(&priv->buffered_multicasts_lock); + + if (!still_buffered && tid < CW1200_MAX_TID) { + hdr = (struct ieee80211_hdr *) skb->data; + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, hdr->addr1); + if (sta) + ieee80211_sta_set_buffered(sta, tid, false); + rcu_read_unlock(); + } + } + +} + void cw1200_rx_cb(struct cw1200_common *priv, struct wsm_rx *arg, struct sk_buff **skb_p) diff --git a/drivers/staging/cw1200/txrx.h b/drivers/staging/cw1200/txrx.h index e41ac43bc7f..4bf792f23a8 100644 --- a/drivers/staging/cw1200/txrx.h +++ b/drivers/staging/cw1200/txrx.h @@ -68,6 +68,8 @@ int cw1200_skb_to_wsm(struct cw1200_common *priv, struct sk_buff *skb, struct wsm_tx *wsm, struct tx_info *txinfo); void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb); +void cw1200_notify_buffered_tx(struct cw1200_common *priv, + struct sk_buff *skb, int link_id, int tid); /* ******************************************************************** */ /* WSM callbacks */ diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index b05548f34a8..69672992982 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1395,6 +1395,7 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, * probe responses. * The easiest way to get it back is to convert * probe request into WSM start_scan command. */ + int tid; int rate_id = (wsm->flags >> 4) & 0x07; struct cw1200_queue *queue = &priv->tx_queue[cw1200_queue_get_queue_id( @@ -1405,7 +1406,7 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, BUG_ON(priv->scan.probe_skb); BUG_ON(cw1200_queue_get_skb(queue, wsm->packetID, - &priv->scan.probe_skb)); + &priv->scan.probe_skb, &tid)); BUG_ON(cw1200_queue_remove(queue, priv, wsm->packetID)); /* Release used TX rate policy */ @@ -1421,12 +1422,15 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, * We are dropping everything except AUTH in non-joined mode. */ struct sk_buff *skb; int rate_id = (wsm->flags >> 4) & 0x07; + int tid = 8; struct cw1200_queue *queue = &priv->tx_queue[cw1200_queue_get_queue_id( wsm->packetID)]; wsm_printk(KERN_DEBUG "[WSM] Drop frame (0x%.4X):" " not joined.\n", fctl); - BUG_ON(cw1200_queue_get_skb(queue, wsm->packetID, &skb)); + BUG_ON(cw1200_queue_get_skb(queue, wsm->packetID, &skb, &tid)); + skb_pull(skb, sizeof(struct wsm_tx)); + cw1200_notify_buffered_tx(priv, skb, link_id, tid); BUG_ON(cw1200_queue_remove(queue, priv, wsm->packetID)); /* Release used TX rate policy */ tx_policy_put(priv, rate_id); -- cgit v1.2.3 From 493a254989f43d713535d3d9558578d5cbbbd290 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Mon, 26 Sep 2011 18:52:44 +0200 Subject: cw1200: Switch device to low power at suspend. ST-Ericsson ID: 363297 Change-Id: I2c747fc8425728e0ad424af799fd8535a2d45991 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32006 Reviewed-by: Janusz DZIEDZIC Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33531 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/bh.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index 231213a9cda..80efc7dba67 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -296,8 +296,7 @@ static int cw1200_bh(void *arg) continue; } else if (suspend) { bh_printk(KERN_DEBUG "[BH] Device suspend.\n"); - if (priv->powersave_enabled && - !priv->device_can_sleep) { + if (priv->powersave_enabled) { WARN_ON(cw1200_reg_write_16(priv, ST90TDS_CONTROL_REG_ID, 0)); priv->device_can_sleep = true; -- cgit v1.2.3 From 246ad855174e4e42affdc434536c12a5bf81c8dd Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Wed, 28 Sep 2011 12:12:39 +0200 Subject: cw1200: improve ap mode startup We have to set SSID in wsm_start request correctly. Change-Id: I75c243eb21964d83af9027266335d288941b7a16 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32314 Reviewed-by: Dmitry TARNYAGIN Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33532 Reviewed-by: Philippe LANGLAIS Tested-by: Dmitry TARNYAGIN --- drivers/staging/cw1200/ap.c | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index f35eef5fae6..c4b68a42ccd 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -674,8 +674,6 @@ void cw1200_suspend_resume(struct cw1200_common *priv, static int cw1200_upload_beacon(struct cw1200_common *priv) { int ret = 0; - const u8 *ssidie; - const struct ieee80211_mgmt *mgmt; struct wsm_template_frame frame = { .frame_type = WSM_FRAME_TYPE_BEACON, }; @@ -685,20 +683,6 @@ static int cw1200_upload_beacon(struct cw1200_common *priv) if (WARN_ON(!frame.skb)) return -ENOMEM; - mgmt = (struct ieee80211_mgmt *)frame.skb->data; - ssidie = cfg80211_find_ie(WLAN_EID_SSID, - mgmt->u.beacon.variable, - frame.skb->len - (mgmt->u.beacon.variable - frame.skb->data)); - memset(priv->ssid, 0, sizeof(priv->ssid)); - if (ssidie) { - priv->ssid_length = ssidie[1]; - if (WARN_ON(priv->ssid_length > sizeof(priv->ssid))) - priv->ssid_length = sizeof(priv->ssid); - memcpy(priv->ssid, &ssidie[2], priv->ssid_length); - } else { - priv->ssid_length = 0; - } - ret = wsm_set_template_frame(priv, &frame); if (!ret) { /* TODO: Distille probe resp; remove TIM @@ -767,6 +751,9 @@ static int cw1200_enable_beaconing(struct cw1200_common *priv, static int cw1200_start_ap(struct cw1200_common *priv) { int ret; + const u8 *ssidie; + struct sk_buff *skb; + int offset; struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; struct wsm_start start = { .mode = priv->vif->p2p ? @@ -782,11 +769,32 @@ static int cw1200_start_ap(struct cw1200_common *priv) .probeDelay = 100, .basicRateSet = cw1200_rate_mask_to_wsm(priv, conf->basic_rates), - .ssidLength = priv->ssid_length, }; + + /* Get SSID */ + skb = ieee80211_beacon_get(priv->hw, priv->vif); + if (WARN_ON(!skb)) + return -ENOMEM; + + offset = offsetof(struct ieee80211_mgmt, u.beacon.variable); + ssidie = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset, + skb->len - offset); + + memset(priv->ssid, 0, sizeof(priv->ssid)); + if (ssidie) { + priv->ssid_length = ssidie[1]; + if (WARN_ON(priv->ssid_length > sizeof(priv->ssid))) + priv->ssid_length = sizeof(priv->ssid); + memcpy(priv->ssid, &ssidie[2], priv->ssid_length); + } else { + priv->ssid_length = 0; + } + dev_kfree_skb(skb); + priv->beacon_int = conf->beacon_int; priv->join_dtim_period = conf->dtim_period; + start.ssidLength = priv->ssid_length; memcpy(&start.ssid[0], priv->ssid, start.ssidLength); memset(&priv->link_id_db, 0, sizeof(priv->link_id_db)); -- cgit v1.2.3 From c88f5459da6f2cb396729e42a1afb8a3041e640b Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 29 Sep 2011 10:39:16 +0200 Subject: cw1200: Fix for potential crash on driver unloading. Both device priv and pm platform device were freed twice. Change-Id: I46d838911e1a4943de307512f90c398f4850729e Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32476 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33533 Reviewed-by: Philippe LANGLAIS Tested-by: Dmitry TARNYAGIN --- drivers/staging/cw1200/pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c index 3b6c0bf496c..3cb6b62b530 100644 --- a/drivers/staging/cw1200/pm.c +++ b/drivers/staging/cw1200/pm.c @@ -70,8 +70,8 @@ static void cw1200_pm_deinit_common(struct cw1200_pm_state *pm) { platform_driver_unregister(&cw1200_power_driver); if (pm->pm_dev) { + pm->pm_dev->dev.platform_data = NULL; platform_device_unregister(pm->pm_dev); - kfree(pm->pm_dev); pm->pm_dev = NULL; } } -- cgit v1.2.3 From 18201d5d8e96d94c4eeffff96107f7965d24f443 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Tue, 27 Sep 2011 15:17:59 +0200 Subject: cw1200: cw1200_flush() blocked execution on SoftAP stop cw1200_flush() was called when beacon had not been available. In presence of PS-enabled STAs in the air that leaded to a timeout in delivering of frames to these STAs. Fix forces clearing of the frame queue if beacon is not available. ST-Ericsson ID: 360712 ST-Ericsson ID: 359675 Change-Id: I678fbd6b31d853108ece82e4da62a02c21fa211e Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32220 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33534 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 7 ++- drivers/staging/cw1200/sta.c | 118 ++++++++++++++++++++++-------------------- drivers/staging/cw1200/txrx.c | 7 ++- 3 files changed, 71 insertions(+), 61 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index c4b68a42ccd..0e0947a4482 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -134,8 +134,11 @@ static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set) skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, &tim_offset, &tim_length); - if (WARN_ON(!skb)) - return -ENOMEM; + if (!skb) { + if (!__cw1200_flush(priv, true)); + wsm_unlock_tx(priv); + return -ENOENT; + } if (tim_offset && tim_length >= 6) { /* Ignore DTIM count from mac80211: diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index ed453c2372b..263e514010e 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -101,10 +101,6 @@ void cw1200_stop(struct ieee80211_hw *dev) LIST_HEAD(list); int i; - struct wsm_reset reset = { - .reset_statistics = true, - }; - wsm_lock_tx(priv); while (down_trylock(&priv->scan.lock)) { @@ -114,51 +110,18 @@ void cw1200_stop(struct ieee80211_hw *dev) } up(&priv->scan.lock); - mutex_lock(&priv->conf_mutex); - cw1200_free_keys(priv); - priv->mode = NL80211_IFTYPE_UNSPECIFIED; - priv->listening = false; - mutex_unlock(&priv->conf_mutex); - cancel_delayed_work_sync(&priv->scan.probe_work); cancel_delayed_work_sync(&priv->scan.timeout); cancel_delayed_work_sync(&priv->join_timeout); cancel_delayed_work_sync(&priv->bss_loss_work); cancel_delayed_work_sync(&priv->connection_loss_work); cancel_delayed_work_sync(&priv->link_id_gc_work); - - if (timer_pending(&priv->mcast_timeout)) - del_timer_sync(&priv->mcast_timeout); - - mutex_lock(&priv->conf_mutex); - switch (priv->join_status) { - case CW1200_JOIN_STATUS_STA: - wsm_lock_tx(priv); - if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) - wsm_unlock_tx(priv); - break; - case CW1200_JOIN_STATUS_AP: - /* If you see this warning please change the code to iterate - * through the map and reset each link separately. */ - WARN_ON(priv->link_id_map); - priv->sta_asleep_mask = 0; - priv->enable_beacon = false; - priv->tx_multicast = false; - priv->aid0_bit_set = false; - priv->buffered_multicasts = false; - priv->pspoll_mask = 0; - wsm_reset(priv, &reset); - break; - case CW1200_JOIN_STATUS_MONITOR: - cw1200_update_listening(priv, false); - break; - default: - break; - } - mutex_unlock(&priv->conf_mutex); - flush_workqueue(priv->workqueue); + del_timer_sync(&priv->mcast_timeout); + mutex_lock(&priv->conf_mutex); + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + priv->listening = false; priv->softled_state = 0; /* cw1200_set_leds(priv); */ @@ -170,7 +133,6 @@ void cw1200_stop(struct ieee80211_hw *dev) priv->delayed_link_loss = 0; - priv->link_id_map = 0; priv->join_status = CW1200_JOIN_STATUS_PASSIVE; for (i = 0; i < 4; i++) @@ -230,23 +192,56 @@ void cw1200_remove_interface(struct ieee80211_hw *dev, struct ieee80211_vif *vif) { struct cw1200_common *priv = dev->priv; - struct wsm_reset reset = { .reset_statistics = true, }; + int i; mutex_lock(&priv->conf_mutex); - + wsm_lock_tx(priv); + switch (priv->join_status) { + case CW1200_JOIN_STATUS_STA: + wsm_lock_tx(priv); + if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(priv); + break; + case CW1200_JOIN_STATUS_AP: + for (i = 0; priv->link_id_map; ++i) { + if (priv->link_id_map & BIT(i)) { + reset.link_id = i; + wsm_reset(priv, &reset); + priv->link_id_map &= ~BIT(i); + } + } + memset(priv->link_id_db, 0, + sizeof(priv->link_id_db)); + memset(priv->tx_suspend_mask, 0, + sizeof(priv->tx_suspend_mask)); + priv->sta_asleep_mask = 0; + priv->enable_beacon = false; + priv->tx_multicast = false; + priv->aid0_bit_set = false; + priv->buffered_multicasts = false; + priv->pspoll_mask = 0; + reset.link_id = 0; + wsm_reset(priv, &reset); + break; + case CW1200_JOIN_STATUS_MONITOR: + cw1200_update_listening(priv, false); + break; + default: + break; + } priv->vif = NULL; priv->mode = NL80211_IFTYPE_MONITOR; memset(priv->mac_addr, 0, ETH_ALEN); memset(priv->bssid, 0, ETH_ALEN); - wsm_lock_tx(priv); - WARN_ON(wsm_reset(priv, &reset)); cw1200_free_keys(priv); cw1200_setup_mac(priv); priv->listening = false; priv->join_status = CW1200_JOIN_STATUS_PASSIVE; + if (!__cw1200_flush(priv, true)) + wsm_unlock_tx(priv); wsm_unlock_tx(priv); mutex_unlock(&priv->conf_mutex); @@ -385,6 +380,7 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) .power_mode = wsm_power_mode_quiescent, .disableMoreFlagUsage = true, }; + wsm_lock_tx(priv); /* Disable p2p-dev mode forced by TX request */ if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) && @@ -760,29 +756,27 @@ int __cw1200_flush(struct cw1200_common *priv, bool drop) { int i, ret; - if (drop) { - for (i = 0; i < 4; ++i) - cw1200_queue_clear(&priv->tx_queue[i]); - } - for (;;) { - /* TODO: correct flush handlin is required when dev_stop. + /* TODO: correct flush handling is required when dev_stop. * Temporary workaround: 2s */ - ret = wait_event_timeout( + if (drop) { + for (i = 0; i < 4; ++i) + cw1200_queue_clear(&priv->tx_queue[i]); + } else { + ret = wait_event_timeout( priv->tx_queue_stats.wait_link_id_empty, cw1200_queue_stats_is_empty( &priv->tx_queue_stats, -1), 2 * HZ); + } - if (unlikely(ret <= 0)) { - if (!ret) - ret = -ETIMEDOUT; + if (!drop && unlikely(ret <= 0)) { + ret = -ETIMEDOUT; break; } else { ret = 0; } - ret = 0; wsm_lock_tx(priv); if (unlikely(!cw1200_queue_stats_is_empty( @@ -800,6 +794,16 @@ void cw1200_flush(struct ieee80211_hw *hw, bool drop) { struct cw1200_common *priv = hw->priv; + switch (priv->mode) { + case NL80211_IFTYPE_MONITOR: + drop = true; + break; + case NL80211_IFTYPE_AP: + if (!priv->enable_beacon) + drop = true; + break; + } + if (!WARN_ON(__cw1200_flush(priv, drop))) wsm_unlock_tx(priv); diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index ef871325eee..92a5bf377b6 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -764,8 +764,11 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, .multicast = !arg->link_id, }; cw1200_suspend_resume(priv, &suspend); - wiphy_warn(priv->hw->wiphy, "Requeue (try %d).\n", - cw1200_queue_get_generation(arg->packetID) + 1); + wiphy_warn(priv->hw->wiphy, "Requeue for link_id %d (try %d)." + " STAs asleep: 0x%.8X\n", + arg->link_id, + cw1200_queue_get_generation(arg->packetID) + 1, + priv->sta_asleep_mask); WARN_ON(cw1200_queue_requeue(queue, arg->packetID)); } else if (!WARN_ON(cw1200_queue_get_skb( -- cgit v1.2.3 From f6fb229871f375ee5c1a3a5741b2d97dfcf027f1 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 29 Sep 2011 12:35:06 +0200 Subject: cw1200: Remove WARN_ON on possible execution path WARN_ON was used to pin-point TXing of a frame with unexpected link_id. This is a valid case when driver is being stopped and it leads to a confusing printout. Change replaces WARN_ON() with wiphy_warn() Change-Id: I78690af3b564a21eb7289cad9c7352817d9d72f0 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32500 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33535 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/wsm.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 69672992982..0a3b564face 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1348,9 +1348,13 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, case NL80211_IFTYPE_AP: if (unlikely(!priv->join_status)) action = doDrop; - else if (WARN_ON(!(BIT(link_id) & - (BIT(0) | priv->link_id_map)))) + else if (unlikely(!(BIT(link_id) & + (BIT(0) | priv->link_id_map)))) { + wiphy_warn(priv->hw->wiphy, + "A frame with expired link id " + "is dropped.\n"); action = doDrop; + } if (cw1200_queue_get_generation(wsm->packetID) > CW1200_MAX_REQUEUE_ATTEMPTS) { /* HACK!!! WSM324 firmware has tendency to requeue -- cgit v1.2.3 From fce7bdfafd016a01354fd8818802b4d3b9664a48 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Tue, 4 Oct 2011 15:36:04 +0200 Subject: cw1200: Fix for incorrect retry statistics reporting. In case of WSM_STATUS_RETRY_EXCEEDED TX status firmware reports not actual number of retransmission attempts, but the number minus one. Inaccurate reporting slightly affected rate control algorithm. ST-Ericsson ID: 363511 Change-Id: I9ad216435b76e7e995d905deda4234bdf7ae3f3c Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32978 Reviewed-by: QABUILD Reviewed-by: QATEST Reviewed-by: Janusz DZIEDZIC Tested-by: Bartosz MARKOWSKI Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33536 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/txrx.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 92a5bf377b6..b0028ae81dc 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -741,7 +741,8 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, struct sk_buff *skb; int tid = CW1200_MAX_TID; - txrx_printk(KERN_DEBUG "[TX] TX confirm.\n"); + txrx_printk(KERN_DEBUG "[TX] TX confirm: %d, %d.\n", + arg->status, arg->ackFailures); if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { /* STA is stopped. */ @@ -806,6 +807,8 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, queue_work(priv->workqueue, &priv->tx_failure_work); } + if (tx_count) + ++tx_count; } for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) { -- cgit v1.2.3 From 289995810c4af11116a1ccd764c55395ee53bf3d Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Fri, 30 Sep 2011 17:52:09 +0200 Subject: cw1200: New debugfs entry for firmware counters New R/O entry /sys/kernel/debug/ieee80211/phy0/cw1200/counters provides access to low-level PHY counters in firmware. ST-Ericsson ID: 365735 Change-Id: I6fdfade61791cdf3dc28e1477ae3d7bf5a217d1f Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32996 Reviewed-by: QABUILD Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33537 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/debug.c | 62 ++++++++++++++++++++++++++++++++++++++++++ drivers/staging/cw1200/wsm.h | 34 ++++++++++++++++++++++- 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 2cfb0ee17cb..3715678d522 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -309,6 +309,64 @@ static const struct file_operations fops_status = { .owner = THIS_MODULE, }; +static int cw1200_counters_show(struct seq_file *seq, void *v) +{ + int ret; + struct cw1200_common *priv = seq->private; + struct wsm_counters_table counters; + + ret = wsm_get_counters_table(priv, &counters); + if (ret) + return ret; + +#define CAT_STR(x, y) x ## y +#define PUT_COUNTER(tab, name) \ + seq_printf(seq, "%s:" tab "%d\n", #name, \ + __le32_to_cpu(counters.CAT_STR(count, name))) + + PUT_COUNTER("\t\t", PlcpErrors); + PUT_COUNTER("\t\t", FcsErrors); + PUT_COUNTER("\t\t", TxPackets); + PUT_COUNTER("\t\t", RxPackets); + PUT_COUNTER("\t\t", RxPacketErrors); + PUT_COUNTER("\t", RxDecryptionFailures); + PUT_COUNTER("\t\t", RxMicFailures); + PUT_COUNTER("\t", RxNoKeyFailures); + PUT_COUNTER("\t", TxMulticastFrames); + PUT_COUNTER("\t", TxFramesSuccess); + PUT_COUNTER("\t", TxFrameFailures); + PUT_COUNTER("\t", TxFramesRetried); + PUT_COUNTER("\t", TxFramesMultiRetried); + PUT_COUNTER("\t", RxFrameDuplicates); + PUT_COUNTER("\t\t", RtsSuccess); + PUT_COUNTER("\t\t", RtsFailures); + PUT_COUNTER("\t\t", AckFailures); + PUT_COUNTER("\t", RxMulticastFrames); + PUT_COUNTER("\t", RxFramesSuccess); + PUT_COUNTER("\t", RxCMACICVErrors); + PUT_COUNTER("\t\t", RxCMACReplays); + PUT_COUNTER("\t", RxMgmtCCMPReplays); + +#undef PUT_COUNTER +#undef CAT_STR + + return 0; +} + +static int cw1200_counters_open(struct inode *inode, struct file *file) +{ + return single_open(file, &cw1200_counters_show, + inode->i_private); +} + +static const struct file_operations fops_counters = { + .open = cw1200_counters_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + int cw1200_debug_init(struct cw1200_common *priv) { struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv), @@ -326,6 +384,10 @@ int cw1200_debug_init(struct cw1200_common *priv) priv, &fops_status)) goto err; + if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy, + priv, &fops_counters)) + goto err; + return 0; err: diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index adb34765c5c..11e18005200 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -1266,7 +1266,39 @@ static inline int wsm_set_rcpi_rssi_threshold(struct cw1200_common *priv, sizeof(*arg)); } -static inline int wsm_get_station_id(struct cw1200_common *priv, u8 * mac) +struct wsm_counters_table { + __le32 countPlcpErrors; + __le32 countFcsErrors; + __le32 countTxPackets; + __le32 countRxPackets; + __le32 countRxPacketErrors; + __le32 countRxDecryptionFailures; + __le32 countRxMicFailures; + __le32 countRxNoKeyFailures; + __le32 countTxMulticastFrames; + __le32 countTxFramesSuccess; + __le32 countTxFrameFailures; + __le32 countTxFramesRetried; + __le32 countTxFramesMultiRetried; + __le32 countRxFrameDuplicates; + __le32 countRtsSuccess; + __le32 countRtsFailures; + __le32 countAckFailures; + __le32 countRxMulticastFrames; + __le32 countRxFramesSuccess; + __le32 countRxCMACICVErrors; + __le32 countRxCMACReplays; + __le32 countRxMgmtCCMPReplays; +}; + +static inline int wsm_get_counters_table(struct cw1200_common *priv, + struct wsm_counters_table *arg) +{ + return wsm_read_mib(priv, WSM_MIB_ID_COUNTERS_TABLE, + arg, sizeof(*arg)); +} + +static inline int wsm_get_station_id(struct cw1200_common *priv, u8 *mac) { return wsm_read_mib(priv, WSM_MIB_ID_DOT11_STATION_ID, mac, ETH_ALEN); } -- cgit v1.2.3 From dd3f47a6ee554ee8682363b6f3977a58d1401fe6 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Tue, 4 Oct 2011 18:11:41 +0200 Subject: cw1200: New debugfs entry for 11n support New R/W entry /sys/kernel/debug/ieee80211/phy0/cw1200/11n allows to disable and reenable 11n support in run-time. Disable 11n: echo 0 > /sys/kernel/debug/ieee80211/phy0/cw1200/11n Enable 11n: echo 1 > /sys/kernel/debug/ieee80211/phy0/cw1200/11n ST-Ericsson ID: 365735 Change-Id: Ie7b667308aa276e4ac0f4f365f86b2b103a21f7e Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33005 Reviewed-by: QATEST Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33538 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/debug.c | 53 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 3715678d522..6ec0d086d8b 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -367,6 +367,55 @@ static const struct file_operations fops_counters = { .owner = THIS_MODULE, }; +static int cw1200_generic_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t cw1200_11n_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct cw1200_common *priv = file->private_data; + struct ieee80211_supported_band *band = + priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ]; + return simple_read_from_buffer(user_buf, count, ppos, + band->ht_cap.ht_supported ? "1\n" : "0\n", 2); +} + +static ssize_t cw1200_11n_write(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct cw1200_common *priv = file->private_data; + struct ieee80211_supported_band *band[2] = { + priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ], + priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ], + }; + char buf[1]; + int ena = 0; + + if (!count) + return -EINVAL; + if (copy_from_user(buf, user_buf, 1)) + return -EFAULT; + if (buf[0] == 1) + ena = 1; + + band[0]->ht_cap.ht_supported = ena; +#ifdef CONFIG_CW1200_5GHZ_SUPPORT + band[1]->ht_cap.ht_supported = ena; +#endif /* CONFIG_CW1200_5GHZ_SUPPORT */ + + return count; +} + +static const struct file_operations fops_11n = { + .open = cw1200_generic_open, + .read = cw1200_11n_read, + .write = cw1200_11n_write, + .llseek = default_llseek, +}; + int cw1200_debug_init(struct cw1200_common *priv) { struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv), @@ -388,6 +437,10 @@ int cw1200_debug_init(struct cw1200_common *priv) priv, &fops_counters)) goto err; + if (!debugfs_create_file("11n", S_IRUSR | S_IWUSR, + d->debugfs_phy, priv, &fops_11n)) + goto err; + return 0; err: -- cgit v1.2.3 From 1cc76aa14115422749b47c091fa6c2af466ba92a Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Tue, 4 Oct 2011 18:17:07 +0200 Subject: cw1200: cw1200_sta_notify implementation was removed by mistake. Asleep map in the driver was not updated. Bug was introduced by following commit: "cw1200: Adaptation to U-APSD/AP support as on Thu, Sep 22, 2011". ST-Ericsson ID: 360749 Change-Id: I5005a91f20ebc182e92f13f94510fa16fd7846ae Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33539 Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 33 +++++++++++++++++++++++++++++---- drivers/staging/cw1200/ap.h | 3 --- drivers/staging/cw1200/main.c | 1 - 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 0e0947a4482..5e7177f85cf 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -80,10 +80,35 @@ int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, return 0; } -void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, - enum sta_notify_cmd notify_cmd, - struct ieee80211_sta *sta) +static void __cw1200_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta) { + struct cw1200_common *priv = dev->priv; + struct cw1200_sta_priv *sta_priv = + (struct cw1200_sta_priv *)&sta->drv_priv; + u32 bit = BIT(sta_priv->link_id); + + spin_lock_bh(&priv->buffered_multicasts_lock); + switch (notify_cmd) { + case STA_NOTIFY_SLEEP: + if (priv->buffered_multicasts && + !priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_start_work); + priv->sta_asleep_mask |= bit; + break; + case STA_NOTIFY_AWAKE: + priv->sta_asleep_mask &= ~bit; + if (priv->tx_multicast && + !priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_stop_work); + cw1200_bh_wakeup(priv); + break; + } + spin_unlock_bh(&priv->buffered_multicasts_lock); } static void __cw1200_ps_notify(struct cw1200_common *priv, @@ -96,7 +121,7 @@ static void __cw1200_ps_notify(struct cw1200_common *priv, priv->link_id_db[link_id - 1].ps = ps; if (sta) { - cw1200_sta_notify(priv->hw, priv->vif, + __cw1200_sta_notify(priv->hw, priv->vif, ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, sta); ieee80211_sta_ps_transition_ni(sta, ps); } diff --git a/drivers/staging/cw1200/ap.h b/drivers/staging/cw1200/ap.h index 032f631a656..698c01d05ca 100644 --- a/drivers/staging/cw1200/ap.h +++ b/drivers/staging/cw1200/ap.h @@ -18,9 +18,6 @@ int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); -void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, - enum sta_notify_cmd notify_cmd, - struct ieee80211_sta *sta); void cw1200_bss_info_changed(struct ieee80211_hw *dev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index a931a059f6f..c09b44fb367 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -207,7 +207,6 @@ static const struct ieee80211_ops cw1200_ops = { .tx = cw1200_tx, .hw_scan = cw1200_hw_scan, .set_tim = cw1200_set_tim, - .sta_notify = cw1200_sta_notify, .sta_add = cw1200_sta_add, .sta_remove = cw1200_sta_remove, .set_key = cw1200_set_key, -- cgit v1.2.3 From 070d62ffe41d4485c7ebb83af05747ee2977371b Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Tue, 4 Oct 2011 22:48:03 +0200 Subject: cw1200: queue dependency refactoring. According to the driver design queue should know as less as possible about other components. Last commits violate this "rule" by transpatent arguments like tid. This patch is fixing these violations. No functional changes in this commit. Change-Id: I078835dc0263ef71fa2d50a1d9bfae2c04bab440 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33540 Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/debug.c | 2 +- drivers/staging/cw1200/queue.c | 49 ++++++++++++++++-------------------------- drivers/staging/cw1200/queue.h | 16 +++++++++----- drivers/staging/cw1200/txrx.c | 47 ++++++++++++++++++++++++---------------- drivers/staging/cw1200/txrx.h | 8 ++----- drivers/staging/cw1200/wsm.c | 22 ++++++++++--------- 6 files changed, 74 insertions(+), 70 deletions(-) diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 6ec0d086d8b..e1fc0a8a748 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -384,7 +384,7 @@ static ssize_t cw1200_11n_read(struct file *file, } static ssize_t cw1200_11n_write(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos) + const char __user *user_buf, size_t count, loff_t *ppos) { struct cw1200_common *priv = file->private_data; struct ieee80211_supported_band *band[2] = { diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c index ef7d48488bd..c667c433766 100644 --- a/drivers/staging/cw1200/queue.c +++ b/drivers/staging/cw1200/queue.c @@ -20,16 +20,8 @@ struct list_head head; struct sk_buff *skb; u32 packetID; - /* For safety purposes only. I do not trust device too much. - * It was observed (last time quite long time ago) that it - * indicates TX for a packet several times, so it was not enough - * to use offset or address as an uniquie ID in a - * queue. - */ + struct cw1200_txpriv txpriv; u8 generation; - u8 link_id; - u8 raw_link_id; - u8 tid; }; static inline void __cw1200_queue_lock(struct cw1200_queue *queue, @@ -200,20 +192,18 @@ size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, } int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, - struct sk_buff *skb, struct tx_info *txinfo, - u8 raw_link_id, u8 tid) + struct sk_buff *skb, struct cw1200_txpriv *txpriv) { int ret; struct wsm_tx *wsm; struct cw1200_queue_stats *stats = queue->stats; - u8 link_id = txinfo->link_id; wsm = (struct wsm_tx *)skb_push(skb, sizeof(struct wsm_tx)); - ret = cw1200_skb_to_wsm(priv, skb, wsm, txinfo); + ret = cw1200_skb_to_wsm(priv, skb, wsm, txpriv); if (ret) return ret; - if (link_id >= queue->stats->map_capacity) + if (txpriv->link_id >= queue->stats->map_capacity) return -EINVAL; spin_lock_bh(&queue->lock); @@ -224,21 +214,19 @@ int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, list_move_tail(&item->head, &queue->queue); item->skb = skb; + item->txpriv = *txpriv; item->generation = 0; item->packetID = cw1200_queue_make_packet_id( queue->generation, queue->queue_id, item->generation, item - queue->pool); wsm->packetID = __cpu_to_le32(item->packetID); - item->link_id = link_id; - item->raw_link_id = raw_link_id; - item->tid = tid; ++queue->num_queued; - ++queue->link_map_cache[link_id]; + ++queue->link_map_cache[txpriv->link_id]; spin_lock_bh(&stats->lock); ++stats->num_queued; - ++stats->link_map_cache[link_id]; + ++stats->link_map_cache[txpriv->link_id]; spin_unlock_bh(&stats->lock); if (queue->num_queued >= queue->capacity) { @@ -256,7 +244,7 @@ int cw1200_queue_get(struct cw1200_queue *queue, u32 link_id_map, struct wsm_tx **tx, struct ieee80211_tx_info **tx_info, - int *link_id) + const struct cw1200_txpriv **txpriv) { int ret = -ENOENT; struct cw1200_queue_item *item; @@ -265,7 +253,7 @@ int cw1200_queue_get(struct cw1200_queue *queue, spin_lock_bh(&queue->lock); list_for_each_entry(item, &queue->queue, head) { - if (link_id_map & BIT(item->link_id)) { + if (link_id_map & BIT(item->txpriv.link_id)) { ret = 0; break; } @@ -274,14 +262,14 @@ int cw1200_queue_get(struct cw1200_queue *queue, if (!WARN_ON(ret)) { *tx = (struct wsm_tx *)item->skb->data; *tx_info = IEEE80211_SKB_CB(item->skb); - *link_id = item->raw_link_id; + *txpriv = &item->txpriv; list_move_tail(&item->head, &queue->pending); ++queue->num_pending; - --queue->link_map_cache[item->link_id]; + --queue->link_map_cache[item->txpriv.link_id]; spin_lock_bh(&stats->lock); --stats->num_queued; - if (!--stats->link_map_cache[item->link_id]) + if (!--stats->link_map_cache[item->txpriv.link_id]) wakeup_stats = true; spin_unlock_bh(&stats->lock); } @@ -316,11 +304,11 @@ int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID) } else { struct wsm_tx *wsm = (struct wsm_tx *)item->skb->data; --queue->num_pending; - ++queue->link_map_cache[item->link_id]; + ++queue->link_map_cache[item->txpriv.link_id]; spin_lock_bh(&stats->lock); ++stats->num_queued; - ++stats->link_map_cache[item->link_id]; + ++stats->link_map_cache[item->txpriv.link_id]; spin_unlock_bh(&stats->lock); item->generation = ++item_generation; @@ -343,11 +331,11 @@ int cw1200_queue_requeue_all(struct cw1200_queue *queue) struct wsm_tx *wsm = (struct wsm_tx *)item->skb->data; --queue->num_pending; - ++queue->link_map_cache[item->link_id]; + ++queue->link_map_cache[item->txpriv.link_id]; spin_lock_bh(&stats->lock); ++stats->num_queued; - ++stats->link_map_cache[item->link_id]; + ++stats->link_map_cache[item->txpriv.link_id]; spin_unlock_bh(&stats->lock); ++item->generation; @@ -411,7 +399,8 @@ int cw1200_queue_remove(struct cw1200_queue *queue, struct cw1200_common *priv, } int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, - struct sk_buff **skb, int *tid) + struct sk_buff **skb, + const struct cw1200_txpriv **txpriv) { int ret = 0; u8 queue_generation, queue_id, item_generation, item_id; @@ -433,7 +422,7 @@ int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, ret = -ENOENT; } else { *skb = item->skb; - *tid = item->tid; + *txpriv = &item->txpriv; item->skb = NULL; } spin_unlock_bh(&queue->lock); diff --git a/drivers/staging/cw1200/queue.h b/drivers/staging/cw1200/queue.h index d1802c36f02..195d6acb6ec 100644 --- a/drivers/staging/cw1200/queue.h +++ b/drivers/staging/cw1200/queue.h @@ -18,7 +18,7 @@ /* extern */ struct wsm_tx; /* extern */ struct cw1200_common; /* extern */ struct ieee80211_tx_queue_stats; -/* extern */ struct tx_info; +/* extern */ struct cw1200_txpriv; /* forward */ struct cw1200_queue_stats; @@ -48,6 +48,12 @@ struct cw1200_queue_stats { wait_queue_head_t wait_link_id_empty; }; +struct cw1200_txpriv { + u8 link_id; + u8 raw_link_id; + u8 tid; +}; + int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, size_t map_capacity); int cw1200_queue_init(struct cw1200_queue *queue, @@ -61,19 +67,19 @@ void cw1200_queue_deinit(struct cw1200_queue *queue); size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, u32 link_id_map); int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *cw1200, - struct sk_buff *skb, struct tx_info *txinfo, - u8 raw_link_id, u8 tid); + struct sk_buff *skb, struct cw1200_txpriv *txpriv); int cw1200_queue_get(struct cw1200_queue *queue, u32 link_id_map, struct wsm_tx **tx, struct ieee80211_tx_info **tx_info, - int *link_id); + const struct cw1200_txpriv **txpriv); int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID); int cw1200_queue_requeue_all(struct cw1200_queue *queue); int cw1200_queue_remove(struct cw1200_queue *queue, struct cw1200_common *priv, u32 packetID); int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, - struct sk_buff **skb, int *tid); + struct sk_buff **skb, + const struct cw1200_txpriv **txpriv); void cw1200_queue_lock(struct cw1200_queue *queue, struct cw1200_common *cw1200); void cw1200_queue_unlock(struct cw1200_queue *queue, diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index b0028ae81dc..5a55386127c 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -24,6 +24,12 @@ #define tx_policy_printk(...) #endif +/* txrx private */ +struct __cw1200_txpriv { + struct cw1200_txpriv super; + u16 ethertype; +}; + static int cw1200_handle_action_rx(struct cw1200_common *priv, struct sk_buff *skb); static int cw1200_handle_action_tx(struct cw1200_common *priv, @@ -371,8 +377,10 @@ cw1200_get_tx_rate(const struct cw1200_common *priv, /* NOTE: cw1200_skb_to_wsm executes in atomic context. */ int cw1200_skb_to_wsm(struct cw1200_common *priv, struct sk_buff *skb, - struct wsm_tx *wsm, struct tx_info *txinfo) + struct wsm_tx *wsm, struct cw1200_txpriv *txpriv) { + struct __cw1200_txpriv *info = + container_of(txpriv, struct __cw1200_txpriv, super); bool tx_policy_renew = false; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); const struct ieee80211_rate *rate = cw1200_get_tx_rate(priv, @@ -416,7 +424,7 @@ int cw1200_skb_to_wsm(struct cw1200_common *priv, struct sk_buff *skb, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data + sizeof(struct wsm_tx)); - if (cpu_to_be16(txinfo->ethertype) == ETH_P_PAE) + if (cpu_to_be16(info->ethertype) == ETH_P_PAE) priority = WSM_EPTA_PRIORITY_EAPOL; else if (ieee80211_is_action(hdr->frame_control)) priority = WSM_EPTA_PRIORITY_ACTION; @@ -449,14 +457,15 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; int was_buffered = 0; - int tid = CW1200_MAX_TID; const u8 *da = ieee80211_get_DA(hdr); struct cw1200_sta_priv *sta_priv = (struct cw1200_sta_priv *)&tx_info->control.sta->drv_priv; int link_id, raw_link_id; int ret; int i; - struct tx_info txinfo; + struct __cw1200_txpriv txpriv = { + .super.tid = CW1200_MAX_TID, + }; if (likely(tx_info->control.sta && sta_priv->link_id)) raw_link_id = link_id = sta_priv->link_id; @@ -503,15 +512,15 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) } else if (ieee80211_is_data_qos(hdr->frame_control) || ieee80211_is_qos_nullfunc(hdr->frame_control)) { u8 *qos = ieee80211_get_qos_ctl(hdr); - tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK; + txpriv.super.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK; } else if (ieee80211_is_data(hdr->frame_control) || ieee80211_is_nullfunc(hdr->frame_control)) { - tid = 0; + txpriv.super.tid = 0; } /* BT Coex support related configuration */ if (priv->is_BT_Present) { - txinfo.ethertype = 0; + txpriv.ethertype = 0; if (ieee80211_is_data_qos(hdr->frame_control) || ieee80211_is_data(hdr->frame_control)) { @@ -520,7 +529,7 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) /* Skip LLC SNAP header (+6) */ if (headerlen > 0) - txinfo.ethertype = + txpriv.ethertype = *((u16 *)(skb->data + headerlen + 6)); } else if (ieee80211_is_assoc_req(hdr->frame_control) || @@ -621,19 +630,20 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) &priv->multicast_start_work); } - txinfo.link_id = link_id; - - if (raw_link_id && tid < CW1200_MAX_TID) + if (raw_link_id && txpriv.super.tid < CW1200_MAX_TID) was_buffered = priv->link_id_db[raw_link_id - 1] - .buffered[tid]++; + .buffered[txpriv.super.tid]++; + txpriv.super.link_id = link_id; + txpriv.super.raw_link_id = raw_link_id; ret = cw1200_queue_put(&priv->tx_queue[queue], priv, skb, - &txinfo, raw_link_id, tid); + &txpriv.super); spin_unlock_bh(&priv->buffered_multicasts_lock); - if (raw_link_id && !was_buffered && tid < CW1200_MAX_TID) - ieee80211_sta_set_buffered(tx_info->control.sta, tid, true); + if (raw_link_id && !was_buffered && txpriv.super.tid < CW1200_MAX_TID) + ieee80211_sta_set_buffered(tx_info->control.sta, + txpriv.super.tid, true); if (!WARN_ON(ret)) cw1200_bh_wakeup(priv); @@ -739,7 +749,7 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, u8 queue_id = cw1200_queue_get_queue_id(arg->packetID); struct cw1200_queue *queue = &priv->tx_queue[queue_id]; struct sk_buff *skb; - int tid = CW1200_MAX_TID; + const struct cw1200_txpriv *txpriv = NULL; txrx_printk(KERN_DEBUG "[TX] TX confirm: %d, %d.\n", arg->status, arg->ackFailures); @@ -773,7 +783,7 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, WARN_ON(cw1200_queue_requeue(queue, arg->packetID)); } else if (!WARN_ON(cw1200_queue_get_skb( - queue, arg->packetID, &skb, &tid))) { + queue, arg->packetID, &skb, &txpriv))) { struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb); struct wsm_tx *wsm_tx = (struct wsm_tx *)skb->data; int rate_id = (wsm_tx->flags >> 4) & 0x07; @@ -827,7 +837,8 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, } skb_pull(skb, sizeof(struct wsm_tx)); - cw1200_notify_buffered_tx(priv, skb, arg->link_id, tid); + cw1200_notify_buffered_tx(priv, skb, arg->link_id, + txpriv->tid); ieee80211_tx_status(priv->hw, skb); WARN_ON(cw1200_queue_remove(queue, priv, arg->packetID)); } diff --git a/drivers/staging/cw1200/txrx.h b/drivers/staging/cw1200/txrx.h index 4bf792f23a8..87dbdfa949c 100644 --- a/drivers/staging/cw1200/txrx.h +++ b/drivers/staging/cw1200/txrx.h @@ -18,6 +18,7 @@ /* extern */ struct sk_buff; /* extern */ struct wsm_tx; /* extern */ struct wsm_tx_confirm; +/* extern */ struct cw1200_txpriv; struct tx_policy { union { @@ -43,11 +44,6 @@ struct tx_policy_cache { spinlock_t lock; }; -struct tx_info { - u8 link_id; - u16 ethertype; -}; - /* ******************************************************************** */ /* TX policy cache */ /* Intention of TX policy cache is an overcomplicated WSM API. @@ -66,7 +62,7 @@ u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates); int cw1200_skb_to_wsm(struct cw1200_common *priv, struct sk_buff *skb, struct wsm_tx *wsm, - struct tx_info *txinfo); + struct cw1200_txpriv *txpriv); void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb); void cw1200_notify_buffered_tx(struct cw1200_common *priv, struct sk_buff *skb, int link_id, int tid); diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 0a3b564face..415fbcbee59 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1399,7 +1399,7 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, * probe responses. * The easiest way to get it back is to convert * probe request into WSM start_scan command. */ - int tid; + const struct cw1200_txpriv *txpriv; int rate_id = (wsm->flags >> 4) & 0x07; struct cw1200_queue *queue = &priv->tx_queue[cw1200_queue_get_queue_id( @@ -1410,7 +1410,7 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, BUG_ON(priv->scan.probe_skb); BUG_ON(cw1200_queue_get_skb(queue, wsm->packetID, - &priv->scan.probe_skb, &tid)); + &priv->scan.probe_skb, &txpriv)); BUG_ON(cw1200_queue_remove(queue, priv, wsm->packetID)); /* Release used TX rate policy */ @@ -1426,15 +1426,16 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, * We are dropping everything except AUTH in non-joined mode. */ struct sk_buff *skb; int rate_id = (wsm->flags >> 4) & 0x07; - int tid = 8; + const struct cw1200_txpriv *txpriv = NULL; struct cw1200_queue *queue = &priv->tx_queue[cw1200_queue_get_queue_id( wsm->packetID)]; wsm_printk(KERN_DEBUG "[WSM] Drop frame (0x%.4X):" " not joined.\n", fctl); - BUG_ON(cw1200_queue_get_skb(queue, wsm->packetID, &skb, &tid)); + BUG_ON(cw1200_queue_get_skb(queue, wsm->packetID, + &skb, &txpriv)); skb_pull(skb, sizeof(struct wsm_tx)); - cw1200_notify_buffered_tx(priv, skb, link_id, tid); + cw1200_notify_buffered_tx(priv, skb, link_id, txpriv->tid); BUG_ON(cw1200_queue_remove(queue, priv, wsm->packetID)); /* Release used TX rate policy */ tx_policy_put(priv, rate_id); @@ -1580,7 +1581,7 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, struct ieee80211_tx_info *tx_info; struct cw1200_queue *queue; u32 tx_allowed_mask = 0; - int link_id; + const struct cw1200_txpriv *txpriv = NULL; /* * Count was intended as an input for wsm->more flag. * During implementation it was found that wsm->more @@ -1629,17 +1630,18 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, if (cw1200_queue_get(queue, tx_allowed_mask, - &wsm, &tx_info, &link_id)) + &wsm, &tx_info, &txpriv)) continue; - if (wsm_handle_tx_data(priv, wsm, tx_info, link_id)) + if (wsm_handle_tx_data(priv, wsm, + tx_info, txpriv->raw_link_id)) continue; /* Handled by WSM */ wsm->hdr.id &= __cpu_to_le16( ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX)); wsm->hdr.id |= cpu_to_le16( - WSM_TX_LINK_ID(link_id)); - priv->pspoll_mask &= ~BIT(link_id); + WSM_TX_LINK_ID(txpriv->raw_link_id)); + priv->pspoll_mask &= ~BIT(txpriv->raw_link_id); *data = (u8 *)wsm; *tx_len = __le16_to_cpu(wsm->hdr.len); -- cgit v1.2.3 From 599e22d5834c7f074cd80cedcfbce001f1d62a5c Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 5 Oct 2011 09:29:01 +0200 Subject: cw1200: AP PS refactoring. * buffered_multicasts_lock was renamed to ps_state_lock. Previous name was quite confusive. * Per-STA rx_queue was created for early RX-ed frames. Not that we really need these frames, but PM status they are holding is important. * priv->tx_suspend_mask was removed, driver is not using it. It was intended for UAPSD and is not needed in current implementation on mac80211. * Fix: cw1200_queue_unlock() was not called from cw1200_queue_clear() when queue was internally locked. ST-Ericsson ID: 360749 Change-Id: I61346db485d34f761d80af786b716d8c73e8b600 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33541 Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 186 ++++++++++++++++++---------------------- drivers/staging/cw1200/ap.h | 5 +- drivers/staging/cw1200/cw1200.h | 5 +- drivers/staging/cw1200/debug.c | 4 - drivers/staging/cw1200/main.c | 8 +- drivers/staging/cw1200/queue.c | 11 ++- drivers/staging/cw1200/queue.h | 5 +- drivers/staging/cw1200/sta.c | 6 +- drivers/staging/cw1200/txrx.c | 60 +++++++------ drivers/staging/cw1200/wsm.c | 5 +- 10 files changed, 143 insertions(+), 152 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 5e7177f85cf..5a75ac508d1 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -29,6 +29,10 @@ static int cw1200_start_ap(struct cw1200_common *priv); static int cw1200_update_beaconing(struct cw1200_common *priv); static int cw1200_enable_beaconing(struct cw1200_common *priv, bool enable); +static void __cw1200_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta); /* ******************************************************************** */ /* AP API */ @@ -39,22 +43,26 @@ int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cw1200_common *priv = hw->priv; struct cw1200_sta_priv *sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; + struct cw1200_link_entry *entry; + struct sk_buff *skb; if (priv->mode != NL80211_IFTYPE_AP) return 0; sta_priv->link_id = cw1200_find_link_id(priv, sta->addr); - if (!sta_priv->link_id) { + if (WARN_ON(!sta_priv->link_id)) { + /* Impossible error */ wiphy_info(priv->hw->wiphy, "[AP] No more link IDs available.\n"); return -ENOENT; } - spin_lock_bh(&priv->buffered_multicasts_lock); - priv->link_id_db[sta_priv->link_id - 1].status = CW1200_LINK_HARD; - if (priv->link_id_db[sta_priv->link_id - 1].ps) - ieee80211_sta_ps_transition(sta, true); - spin_unlock_bh(&priv->buffered_multicasts_lock); + entry = &priv->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&priv->ps_state_lock); + entry->status = CW1200_LINK_HARD; + while ((skb = skb_dequeue(&entry->rx_queue))) + ieee80211_rx_irqsafe(priv->hw, skb); + spin_unlock_bh(&priv->ps_state_lock); return 0; } @@ -64,19 +72,24 @@ int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cw1200_common *priv = hw->priv; struct cw1200_sta_priv *sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; + struct cw1200_link_entry *entry; if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id) return 0; - spin_lock_bh(&priv->buffered_multicasts_lock); - priv->link_id_db[sta_priv->link_id - 1].status = CW1200_LINK_SOFT; - priv->link_id_db[sta_priv->link_id - 1].timestamp = jiffies; + /* HACK! To be removed when accurate TX ststus + * reporting for dropped frames is implemented. */ + ieee80211_sta_eosp_irqsafe(sta); + + entry = &priv->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&priv->ps_state_lock); + entry->status = CW1200_LINK_SOFT; + entry->timestamp = jiffies; if (!delayed_work_pending(&priv->link_id_gc_work)) queue_delayed_work(priv->workqueue, &priv->link_id_gc_work, CW1200_LINK_ID_GC_TIMEOUT); - priv->link_id_db[sta_priv->link_id - 1].ps = false; - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); return 0; } @@ -89,45 +102,44 @@ static void __cw1200_sta_notify(struct ieee80211_hw *dev, struct cw1200_sta_priv *sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; u32 bit = BIT(sta_priv->link_id); + u32 prev = priv->sta_asleep_mask & bit; - spin_lock_bh(&priv->buffered_multicasts_lock); switch (notify_cmd) { case STA_NOTIFY_SLEEP: - if (priv->buffered_multicasts && + if (!prev) { + if (priv->buffered_multicasts && !priv->sta_asleep_mask) queue_work(priv->workqueue, &priv->multicast_start_work); - priv->sta_asleep_mask |= bit; + priv->sta_asleep_mask |= bit; + } break; case STA_NOTIFY_AWAKE: - priv->sta_asleep_mask &= ~bit; - if (priv->tx_multicast && - !priv->sta_asleep_mask) - queue_work(priv->workqueue, - &priv->multicast_stop_work); - cw1200_bh_wakeup(priv); + if (prev) { + priv->sta_asleep_mask &= ~bit; + priv->pspoll_mask &= ~bit; + if (priv->tx_multicast && + !priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_stop_work); + cw1200_bh_wakeup(priv); + } break; } - spin_unlock_bh(&priv->buffered_multicasts_lock); } -static void __cw1200_ps_notify(struct cw1200_common *priv, - struct ieee80211_sta *sta, - int link_id, bool ps) +void cw1200_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta) { - txrx_printk(KERN_DEBUG "%s for LinkId: %d. Suspend: %.8X\n", - ps ? "Stop" : "Start", - link_id, priv->tx_suspend_mask[0]); - - priv->link_id_db[link_id - 1].ps = ps; - if (sta) { - __cw1200_sta_notify(priv->hw, priv->vif, - ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, sta); - ieee80211_sta_ps_transition_ni(sta, ps); - } + struct cw1200_common *priv = dev->priv; + spin_lock_bh(&priv->ps_state_lock); + __cw1200_sta_notify(dev, vif, notify_cmd, sta); + spin_unlock_bh(&priv->ps_state_lock); } -void cw1200_ps_notify(struct cw1200_common *priv, +static void cw1200_ps_notify(struct cw1200_common *priv, int link_id, bool ps) { struct ieee80211_sta *sta; @@ -135,13 +147,17 @@ void cw1200_ps_notify(struct cw1200_common *priv, if (!link_id || link_id > CW1200_MAX_STA_IN_AP_MODE) return; - if (!!ps == !!priv->link_id_db[link_id - 1].ps) - return; + txrx_printk(KERN_DEBUG "%s for LinkId: %d. STAs asleep: %.8X\n", + ps ? "Stop" : "Start", + link_id, priv->sta_asleep_mask); rcu_read_lock(); sta = ieee80211_find_sta(priv->vif, priv->link_id_db[link_id - 1].mac); - __cw1200_ps_notify(priv, sta, link_id, ps); + if (sta) { + __cw1200_sta_notify(priv->hw, priv->vif, + ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, sta); + } rcu_read_unlock(); } @@ -594,12 +610,12 @@ void cw1200_mcast_timeout(unsigned long arg) struct cw1200_common *priv = (struct cw1200_common *)arg; - spin_lock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->ps_state_lock); priv->tx_multicast = priv->aid0_bit_set && priv->buffered_multicasts; if (priv->tx_multicast) cw1200_bh_wakeup(priv); - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); } int cw1200_ampdu_action(struct ieee80211_hw *hw, @@ -620,18 +636,6 @@ int cw1200_ampdu_action(struct ieee80211_hw *hw, void cw1200_suspend_resume(struct cw1200_common *priv, struct wsm_suspend_resume *arg) { - int queue = BIT(wsm_queue_id_to_linux(arg->queue)); - u32 unicast = BIT(arg->link_id); - u32 wakeup_required = 0; - u32 set = 0; - u32 clear; - u32 tx_suspend_mask; - bool cancel_tmo = false; - int i; - - if (!arg->link_id) /* For all links */ - unicast = BIT(CW1200_MAX_STA_IN_AP_MODE + 1) - 2; - /* if () is intendend to protect against spam. FW sends * "start multicast" request on every DTIM. */ if (arg->stop || !arg->multicast || priv->buffered_multicasts) @@ -640,7 +644,8 @@ void cw1200_suspend_resume(struct cw1200_common *priv, arg->multicast ? "broadcast" : "unicast"); if (arg->multicast) { - spin_lock_bh(&priv->buffered_multicasts_lock); + bool cancel_tmo = false; + spin_lock_bh(&priv->ps_state_lock); if (arg->stop) { priv->tx_multicast = false; } else { @@ -658,44 +663,19 @@ void cw1200_suspend_resume(struct cw1200_common *priv, cw1200_bh_wakeup(priv); } } - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); if (cancel_tmo) del_timer_sync(&priv->mcast_timeout); } else { - if (arg->stop) - set = unicast; - else - set = 0; - - clear = set ^ unicast; - - /* TODO: if (!priv->uapsd) */ - queue = 0x0F; - - spin_lock_bh(&priv->buffered_multicasts_lock); - priv->sta_asleep_mask |= set; - for (i = 0; i < 4; ++i) { - if (!(queue & BIT(i))) - continue; - - tx_suspend_mask = priv->tx_suspend_mask[i]; - priv->tx_suspend_mask[i] = - (tx_suspend_mask & ~clear) | set; - - wakeup_required = wakeup_required || - cw1200_queue_get_num_queued( - &priv->tx_queue[i], - tx_suspend_mask & clear); - } - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->ps_state_lock); cw1200_ps_notify(priv, arg->link_id, arg->stop); + spin_unlock_bh(&priv->ps_state_lock); + if (!arg->stop) + cw1200_bh_wakeup(priv); } - if (wakeup_required) - cw1200_bh_wakeup(priv); return; } - /* ******************************************************************** */ /* AP privates */ @@ -873,7 +853,7 @@ static int cw1200_update_beaconing(struct cw1200_common *priv) int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac) { int i, ret = 0; - spin_lock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->ps_state_lock); for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) && priv->link_id_db[i].status) { @@ -882,7 +862,7 @@ int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac) break; } } - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); return ret; } @@ -892,7 +872,7 @@ int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac) unsigned long max_inactivity = 0; unsigned long now = jiffies; - spin_lock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->ps_state_lock); for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { if (!priv->link_id_db[i].status) { ret = i + 1; @@ -909,11 +889,13 @@ int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac) } } if (ret) { + struct cw1200_link_entry *entry = &priv->link_id_db[ret - 1]; ap_printk(KERN_DEBUG "[AP] STA added, link_id: %d\n", ret); - priv->link_id_db[ret - 1].status = CW1200_LINK_RESERVE; - memcpy(&priv->link_id_db[ret - 1].mac, mac, ETH_ALEN); - memset(&priv->link_id_db[ret - 1].buffered, 0, CW1200_MAX_TID); + entry->status = CW1200_LINK_RESERVE; + memcpy(&entry->mac, mac, ETH_ALEN); + memset(&entry->buffered, 0, CW1200_MAX_TID); + skb_queue_head_init(&entry->rx_queue); wsm_lock_tx_async(priv); if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) wsm_unlock_tx(priv); @@ -922,7 +904,7 @@ int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac) "[AP] Early: no more link IDs available.\n"); } - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); return ret; } @@ -950,13 +932,13 @@ void cw1200_link_id_gc_work(struct work_struct *work) long ttl; bool need_reset; u32 mask; - int i, j; + int i; if (priv->join_status != CW1200_JOIN_STATUS_AP) return; wsm_lock_tx(priv); - spin_lock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->ps_state_lock); for (i = 0; i < CW1200_MAX_STA_IN_AP_MODE; ++i) { need_reset = false; mask = BIT(i + 1); @@ -965,8 +947,7 @@ void cw1200_link_id_gc_work(struct work_struct *work) !(priv->link_id_map & mask))) { if (priv->link_id_map & mask) { priv->sta_asleep_mask &= ~mask; - for (j = 0; j < 4; ++j) - priv->tx_suspend_mask[j] &= ~mask; + priv->pspoll_mask &= ~mask; need_reset = true; } priv->link_id_map |= mask; @@ -974,7 +955,7 @@ void cw1200_link_id_gc_work(struct work_struct *work) priv->link_id_db[i].status = CW1200_LINK_SOFT; memcpy(map_link.mac_addr, priv->link_id_db[i].mac, ETH_ALEN); - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); if (need_reset) { reset.link_id = i + 1; WARN_ON(wsm_reset(priv, &reset)); @@ -982,7 +963,7 @@ void cw1200_link_id_gc_work(struct work_struct *work) map_link.link_id = i + 1; WARN_ON(wsm_map_link(priv, &map_link)); next_gc = min(next_gc, CW1200_LINK_ID_GC_TIMEOUT); - spin_lock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->ps_state_lock); } else if (priv->link_id_db[i].status == CW1200_LINK_SOFT) { ttl = priv->link_id_db[i].timestamp - now + CW1200_LINK_ID_GC_TIMEOUT; @@ -991,22 +972,23 @@ void cw1200_link_id_gc_work(struct work_struct *work) priv->link_id_db[i].status = CW1200_LINK_OFF; priv->link_id_map &= ~mask; priv->sta_asleep_mask &= ~mask; - for (j = 0; j < 4; ++j) - priv->tx_suspend_mask[j] &= ~mask; + priv->pspoll_mask &= ~mask; memset(map_link.mac_addr, 0, ETH_ALEN); - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); reset.link_id = i + 1; WARN_ON(wsm_reset(priv, &reset)); - spin_lock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->ps_state_lock); } else { next_gc = min(next_gc, (unsigned long)ttl); } } - if (need_reset) + if (need_reset) { + skb_queue_purge(&priv->link_id_db[i].rx_queue); ap_printk(KERN_DEBUG "[AP] STA removed, link_id: %d\n", reset.link_id); + } } - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); if (next_gc != -1) queue_delayed_work(priv->workqueue, &priv->link_id_gc_work, next_gc); diff --git a/drivers/staging/cw1200/ap.h b/drivers/staging/cw1200/ap.h index 698c01d05ca..a9e85bd5516 100644 --- a/drivers/staging/cw1200/ap.h +++ b/drivers/staging/cw1200/ap.h @@ -18,6 +18,9 @@ int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); +void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta); void cw1200_bss_info_changed(struct ieee80211_hw *dev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, @@ -38,7 +41,5 @@ int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac); int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac); void cw1200_link_id_work(struct work_struct *work); void cw1200_link_id_gc_work(struct work_struct *work); -void cw1200_ps_notify(struct cw1200_common *priv, - int link_id, bool ps); #endif diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 90df67952f5..ae08d82fe19 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -72,7 +72,7 @@ struct cw1200_link_entry { enum cw1200_link_status status; u8 mac[ETH_ALEN]; u8 buffered[CW1200_MAX_TID]; - bool ps; + struct sk_buff_head rx_queue; }; struct cw1200_common { @@ -198,11 +198,10 @@ struct cw1200_common { struct cw1200_link_entry link_id_db[CW1200_MAX_STA_IN_AP_MODE]; struct work_struct link_id_work; struct delayed_work link_id_gc_work; - u32 tx_suspend_mask[4]; u32 sta_asleep_mask; u32 pspoll_mask; bool aid0_bit_set; - spinlock_t buffered_multicasts_lock; + spinlock_t ps_state_lock; bool buffered_multicasts; bool tx_multicast; struct work_struct set_tim_work; diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index e1fc0a8a748..db927620f26 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -211,11 +211,7 @@ static int cw1200_status_show(struct seq_file *seq, void *v) seq_puts(seq, "\n"); for (i = 0; i < 4; ++i) { - char buf[32]; - snprintf(buf, sizeof(buf), "TX lock(%d): ", i); cw1200_queue_status_show(seq, &priv->tx_queue[i]); - cw1200_debug_print_map(seq, priv, buf, - priv->tx_suspend_mask[i]); seq_puts(seq, "\n"); } diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index c09b44fb367..d2f6cee19ee 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -207,6 +207,7 @@ static const struct ieee80211_ops cw1200_ops = { .tx = cw1200_tx, .hw_scan = cw1200_hw_scan, .set_tim = cw1200_set_tim, + .sta_notify = cw1200_sta_notify, .sta_add = cw1200_sta_add, .sta_remove = cw1200_sta_remove, .set_key = cw1200_set_key, @@ -325,7 +326,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) INIT_DELAYED_WORK(&priv->connection_loss_work, cw1200_connection_loss_work); INIT_WORK(&priv->tx_failure_work, cw1200_tx_failure_work); - spin_lock_init(&priv->buffered_multicasts_lock); + spin_lock_init(&priv->ps_state_lock); INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work); INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work); INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work); @@ -350,7 +351,8 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) if (unlikely(cw1200_queue_init(&priv->tx_queue[i], &priv->tx_queue_stats, i, 16))) { for (; i > 0; i--) - cw1200_queue_deinit(&priv->tx_queue[i - 1]); + cw1200_queue_deinit(&priv->tx_queue[i - 1], + priv); cw1200_queue_stats_deinit(&priv->tx_queue_stats); ieee80211_free_hw(hw); return NULL; @@ -440,7 +442,7 @@ void cw1200_unregister_common(struct ieee80211_hw *dev) } for (i = 0; i < 4; ++i) - cw1200_queue_deinit(&priv->tx_queue[i]); + cw1200_queue_deinit(&priv->tx_queue[i], priv); cw1200_queue_stats_deinit(&priv->tx_queue_stats); cw1200_pm_deinit(&priv->pm_state); } diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c index c667c433766..83b294dc1da 100644 --- a/drivers/staging/cw1200/queue.c +++ b/drivers/staging/cw1200/queue.c @@ -116,7 +116,7 @@ int cw1200_queue_init(struct cw1200_queue *queue, return 0; } -int cw1200_queue_clear(struct cw1200_queue *queue) +int cw1200_queue_clear(struct cw1200_queue *queue, struct cw1200_common *priv) { int i; struct cw1200_queue_stats *stats = queue->stats; @@ -145,6 +145,10 @@ int cw1200_queue_clear(struct cw1200_queue *queue) queue->link_map_cache[i] = 0; } spin_unlock_bh(&stats->lock); + if (unlikely(queue->overfull)) { + queue->overfull = false; + __cw1200_queue_unlock(queue, priv); + } spin_unlock_bh(&queue->lock); wake_up(&stats->wait_link_id_empty); return 0; @@ -156,9 +160,10 @@ void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats) stats->link_map_cache = NULL; } -void cw1200_queue_deinit(struct cw1200_queue *queue) +void cw1200_queue_deinit(struct cw1200_queue *queue, + struct cw1200_common *priv) { - cw1200_queue_clear(queue); + cw1200_queue_clear(queue, priv); INIT_LIST_HEAD(&queue->free_pool); kfree(queue->pool); kfree(queue->link_map_cache); diff --git a/drivers/staging/cw1200/queue.h b/drivers/staging/cw1200/queue.h index 195d6acb6ec..502496b142b 100644 --- a/drivers/staging/cw1200/queue.h +++ b/drivers/staging/cw1200/queue.h @@ -60,9 +60,10 @@ int cw1200_queue_init(struct cw1200_queue *queue, struct cw1200_queue_stats *stats, u8 queue_id, size_t capacity); -int cw1200_queue_clear(struct cw1200_queue *queue); +int cw1200_queue_clear(struct cw1200_queue *queue, struct cw1200_common *priv); void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats); -void cw1200_queue_deinit(struct cw1200_queue *queue); +void cw1200_queue_deinit(struct cw1200_queue *queue, + struct cw1200_common *priv); size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, u32 link_id_map); diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 263e514010e..cc5a345f441 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -136,7 +136,7 @@ void cw1200_stop(struct ieee80211_hw *dev) priv->join_status = CW1200_JOIN_STATUS_PASSIVE; for (i = 0; i < 4; i++) - cw1200_queue_clear(&priv->tx_queue[i]); + cw1200_queue_clear(&priv->tx_queue[i], priv); /* HACK! */ if (atomic_xchg(&priv->tx_lock, 1) != 1) @@ -215,8 +215,6 @@ void cw1200_remove_interface(struct ieee80211_hw *dev, } memset(priv->link_id_db, 0, sizeof(priv->link_id_db)); - memset(priv->tx_suspend_mask, 0, - sizeof(priv->tx_suspend_mask)); priv->sta_asleep_mask = 0; priv->enable_beacon = false; priv->tx_multicast = false; @@ -762,7 +760,7 @@ int __cw1200_flush(struct cw1200_common *priv, bool drop) */ if (drop) { for (i = 0; i < 4; ++i) - cw1200_queue_clear(&priv->tx_queue[i]); + cw1200_queue_clear(&priv->tx_queue[i], priv); } else { ret = wait_event_timeout( priv->tx_queue_stats.wait_link_id_empty, diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 5a55386127c..04b7b0d091e 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -462,7 +462,6 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) (struct cw1200_sta_priv *)&tx_info->control.sta->drv_priv; int link_id, raw_link_id; int ret; - int i; struct __cw1200_txpriv txpriv = { .super.tid = CW1200_MAX_TID, }; @@ -504,11 +503,10 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) goto err; if (unlikely(ieee80211_is_auth(hdr->frame_control))) { - spin_lock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->ps_state_lock); priv->sta_asleep_mask &= ~BIT(raw_link_id); - for (i = 0; i < 4; ++i) - priv->tx_suspend_mask[i] &= ~BIT(raw_link_id); - spin_unlock_bh(&priv->buffered_multicasts_lock); + priv->pspoll_mask &= ~BIT(raw_link_id); + spin_unlock_bh(&priv->ps_state_lock); } else if (ieee80211_is_data_qos(hdr->frame_control) || ieee80211_is_qos_nullfunc(hdr->frame_control)) { u8 *qos = ieee80211_get_qos_ctl(hdr); @@ -620,7 +618,7 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) if (cw1200_handle_action_tx(priv, skb)) goto drop; - spin_lock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->ps_state_lock); if (link_id == CW1200_LINK_ID_AFTER_DTIM && !priv->buffered_multicasts) { @@ -639,7 +637,7 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) ret = cw1200_queue_put(&priv->tx_queue[queue], priv, skb, &txpriv.super); - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); if (raw_link_id && !was_buffered && txpriv.super.tid < CW1200_MAX_TID) ieee80211_sta_set_buffered(tx_info->control.sta, @@ -695,7 +693,7 @@ static int cw1200_handle_pspoll(struct cw1200_common *priv, struct ieee80211_pspoll *pspoll = (struct ieee80211_pspoll *) skb->data; int link_id = 0; - u32 pspoll_mask; + u32 pspoll_mask = 0; int drop = 1; int i; @@ -711,17 +709,16 @@ static int cw1200_handle_pspoll(struct cw1200_common *priv, sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; link_id = sta_priv->link_id; pspoll_mask = BIT(sta_priv->link_id); + + /* HACK! To be removed when accurate TX ststus + * reporting for dropped frames is implemented. */ + if (priv->pspoll_mask & pspoll_mask) + ieee80211_sta_eosp_irqsafe(sta); } rcu_read_unlock(); - if (!link_id) - /* Slowpath */ - link_id = cw1200_find_link_id(priv, pspoll->ta); - if (!link_id) goto done; - pspoll_mask = BIT(link_id); - priv->pspoll_mask |= pspoll_mask; drop = 0; @@ -856,10 +853,10 @@ void cw1200_notify_buffered_tx(struct cw1200_common *priv, buffered = priv->link_id_db [link_id - 1].buffered; - spin_lock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->ps_state_lock); if (!WARN_ON(!buffered[tid])) still_buffered = --buffered[tid]; - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); if (!still_buffered && tid < CW1200_MAX_TID) { hdr = (struct ieee80211_hdr *) skb->data; @@ -880,7 +877,9 @@ void cw1200_rx_cb(struct cw1200_common *priv, struct sk_buff *skb = *skb_p; struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; + struct cw1200_link_entry *entry = NULL; unsigned long grace_period; + bool early_data = false; hdr->flag = 0; if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { @@ -888,8 +887,13 @@ void cw1200_rx_cb(struct cw1200_common *priv, goto drop; } - if (arg->link_id && arg->link_id <= CW1200_MAX_STA_IN_AP_MODE) - priv->link_id_db[arg->link_id - 1].timestamp = jiffies; + if (arg->link_id && arg->link_id <= CW1200_MAX_STA_IN_AP_MODE) { + entry = &priv->link_id_db[arg->link_id - 1]; + if (entry->status == CW1200_LINK_SOFT && + ieee80211_is_data(frame->frame_control)) + early_data = true; + entry->timestamp = jiffies; + } if (unlikely(arg->status)) { if (arg->status == WSM_STATUS_MICFAILURE) { @@ -996,15 +1000,19 @@ void cw1200_rx_cb(struct cw1200_common *priv, grace_period = 1 * HZ; cw1200_pm_stay_awake(&priv->pm_state, grace_period); - /* Notify driver and mac80211 about PM state */ - cw1200_ps_notify(priv, arg->link_id, - ieee80211_has_pm(frame->frame_control)); - - /* Not that we really need _irqsafe variant here, - * but it offloads realtime bh thread and improve - * system performance. */ - ieee80211_rx_irqsafe(priv->hw, skb); + if (unlikely(early_data)) { + spin_lock_bh(&priv->ps_state_lock); + /* Double-check status with lock held */ + if (entry->status == CW1200_LINK_SOFT) + skb_queue_tail(&entry->rx_queue, skb); + else + ieee80211_rx_irqsafe(priv->hw, skb); + spin_unlock_bh(&priv->ps_state_lock); + } else { + ieee80211_rx_irqsafe(priv->hw, skb); + } *skb_p = NULL; + return; drop: diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 415fbcbee59..d0bcffa1654 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1555,7 +1555,6 @@ static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv, tx_allowed_mask = ~priv->sta_asleep_mask; tx_allowed_mask |= BIT(CW1200_LINK_ID_UAPSD); if (priv->sta_asleep_mask) { - tx_allowed_mask |= ~priv->tx_suspend_mask[i]; tx_allowed_mask |= priv->pspoll_mask; tx_allowed_mask &= ~BIT(CW1200_LINK_ID_AFTER_DTIM); } else { @@ -1607,7 +1606,7 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, if (atomic_add_return(0, &priv->tx_lock)) break; - spin_lock_bh(&priv->buffered_multicasts_lock); + spin_lock_bh(&priv->ps_state_lock); ret = wsm_get_tx_queue_and_mask(priv, &queue, &tx_allowed_mask, &more); @@ -1622,7 +1621,7 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, &priv->multicast_stop_work); } - spin_unlock_bh(&priv->buffered_multicasts_lock); + spin_unlock_bh(&priv->ps_state_lock); if (ret) break; -- cgit v1.2.3 From 28168d671d5db33a82fa1a1d13cdff4ca57bc387 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 6 Oct 2011 19:05:02 +0200 Subject: cw1200: Accurate reporting of TX status. * Accurate reporting of TX status is implemented (needed for UAPSD and PSPOLL). * Leaking of TX rate policies is fixed. * skb destructor is implemented. * Time to live for queued frames is implemented. * cw1200_tx is split by separate TX handlers (like in mac80211). * cw1200_skb_to_wsm is not existing anymore. * BT coex: null frames are prioritized as management frames. * Debug: added printing of rate policies in use. ST-Ericsson ID: 354950 ST-Ericsson ID: 360749 Change-Id: I920d398418df99c21b37a16ef16591e58a82151d Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33542 Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 4 - drivers/staging/cw1200/debug.c | 9 + drivers/staging/cw1200/debug.h | 10 + drivers/staging/cw1200/main.c | 19 +- drivers/staging/cw1200/pm.c | 5 + drivers/staging/cw1200/queue.c | 203 +++++++++----- drivers/staging/cw1200/queue.h | 40 ++- drivers/staging/cw1200/sta.c | 8 +- drivers/staging/cw1200/txrx.c | 617 +++++++++++++++++++++++------------------ drivers/staging/cw1200/txrx.h | 10 +- drivers/staging/cw1200/wsm.c | 30 +- drivers/staging/cw1200/wsm.h | 4 +- 12 files changed, 566 insertions(+), 393 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 5a75ac508d1..7b1ac0dfada 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -77,10 +77,6 @@ int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id) return 0; - /* HACK! To be removed when accurate TX ststus - * reporting for dropped frames is implemented. */ - ieee80211_sta_eosp_irqsafe(sta); - entry = &priv->link_id_db[sta_priv->link_id - 1]; spin_lock_bh(&priv->ps_state_lock); entry->status = CW1200_LINK_SOFT; diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index db927620f26..91c690ad4e6 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -101,6 +101,7 @@ static void cw1200_debug_print_map(struct seq_file *seq, static int cw1200_status_show(struct seq_file *seq, void *v) { int i; + struct list_head *item; struct cw1200_common *priv = seq->private; struct cw1200_debug_priv *d = priv->debug; seq_puts(seq, "CW1200 Wireless LAN driver status\n"); @@ -208,6 +209,12 @@ static int cw1200_status_show(struct seq_file *seq, void *v) priv->long_frame_max_tx_count); seq_printf(seq, "Short retr: %d\n", priv->short_frame_max_tx_count); + spin_lock_bh(&priv->tx_policy_cache.lock); + i = 0; + list_for_each(item, &priv->tx_policy_cache.used) + ++i; + spin_unlock_bh(&priv->tx_policy_cache.lock); + seq_printf(seq, "RC in use: %d\n", i); seq_puts(seq, "\n"); for (i = 0; i < 4; ++i) { @@ -283,6 +290,8 @@ static int cw1200_status_show(struct seq_file *seq, void *v) d->tx_cache_miss); seq_printf(seq, "TX copy: %d\n", d->tx_copy); + seq_printf(seq, "TX TTL: %d\n", + d->tx_ttl); seq_printf(seq, "Scan: %s\n", atomic_read(&priv->scan.in_progress) ? "active" : "idle"); seq_printf(seq, "Led state: 0x%.2X\n", diff --git a/drivers/staging/cw1200/debug.h b/drivers/staging/cw1200/debug.h index e7fc4d2daef..6f7d8acab00 100644 --- a/drivers/staging/cw1200/debug.h +++ b/drivers/staging/cw1200/debug.h @@ -15,6 +15,7 @@ struct cw1200_debug_priv { int tx_multi_frames; int tx_cache_miss; int tx_copy; + int tx_ttl; }; int cw1200_debug_init(struct cw1200_common *priv); @@ -57,6 +58,11 @@ static inline void cw1200_debug_tx_copy(struct cw1200_common *priv) ++priv->debug->tx_copy; } +static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv) +{ + ++priv->debug->tx_ttl; +} + #else /* CONFIG_CW1200_DEBUGFS */ static inline int cw1200_debug_init(struct cw1200_common *priv) @@ -97,6 +103,10 @@ static inline void cw1200_debug_tx_copy(struct cw1200_common *priv) { } +static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv) +{ +} + #endif /* CONFIG_CW1200_DEBUGFS */ #endif /* CW1200_DEBUG_H_INCLUDED */ diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index d2f6cee19ee..94398fb222f 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -199,6 +199,13 @@ static struct ieee80211_supported_band cw1200_band_5ghz = { }; #endif /* CONFIG_CW1200_5GHZ_SUPPORT */ +static const unsigned long cw1200_ttl[] = { + 1 * HZ, /* VO */ + 2 * HZ, /* VI */ + 5 * HZ, /* BE */ + 10 * HZ /* BK */ +}; + static const struct ieee80211_ops cw1200_ops = { .start = cw1200_start, .stop = cw1200_stop, @@ -342,17 +349,19 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) } if (unlikely(cw1200_queue_stats_init(&priv->tx_queue_stats, - CW1200_LINK_ID_MAX))) { + CW1200_LINK_ID_MAX, + cw1200_skb_dtor, + priv))) { ieee80211_free_hw(hw); return NULL; } for (i = 0; i < 4; ++i) { if (unlikely(cw1200_queue_init(&priv->tx_queue[i], - &priv->tx_queue_stats, i, 16))) { + &priv->tx_queue_stats, i, 16, + cw1200_ttl[i]))) { for (; i > 0; i--) - cw1200_queue_deinit(&priv->tx_queue[i - 1], - priv); + cw1200_queue_deinit(&priv->tx_queue[i - 1]); cw1200_queue_stats_deinit(&priv->tx_queue_stats); ieee80211_free_hw(hw); return NULL; @@ -442,7 +451,7 @@ void cw1200_unregister_common(struct ieee80211_hw *dev) } for (i = 0; i < 4; ++i) - cw1200_queue_deinit(&priv->tx_queue[i], priv); + cw1200_queue_deinit(&priv->tx_queue[i]); cw1200_queue_stats_deinit(&priv->tx_queue_stats); cw1200_pm_deinit(&priv->pm_state); } diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c index 3cb6b62b530..3d7570767ed 100644 --- a/drivers/staging/cw1200/pm.c +++ b/drivers/staging/cw1200/pm.c @@ -95,6 +95,7 @@ void cw1200_pm_deinit(struct cw1200_pm_state *pm) wake_lock_destroy(&pm->wakelock); cw1200_pm_deinit_common(pm); } + void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, unsigned long tmo) { @@ -205,6 +206,10 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) return -EAGAIN; #endif + /* Do not suspend when datapath is not idle */ + if (priv->tx_queue_stats.num_queued) + return -EBUSY; + /* Make sure there is no configuration requests in progress. */ if (!mutex_trylock(&priv->conf_mutex)) return -EBUSY; diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c index 83b294dc1da..d19f87acf4d 100644 --- a/drivers/staging/cw1200/queue.c +++ b/drivers/staging/cw1200/queue.c @@ -11,37 +11,37 @@ #include "net/mac80211.h" #include "queue.h" -#include "wsm.h" -#include "txrx.h" #include "cw1200.h" +#include "debug.h" /* private */ struct cw1200_queue_item { struct list_head head; struct sk_buff *skb; u32 packetID; + unsigned long timestamp; struct cw1200_txpriv txpriv; u8 generation; }; -static inline void __cw1200_queue_lock(struct cw1200_queue *queue, - struct cw1200_common *cw1200) +static inline void __cw1200_queue_lock(struct cw1200_queue *queue) { + struct cw1200_queue_stats *stats = queue->stats; if (queue->tx_locked_cnt++ == 0) { txrx_printk(KERN_DEBUG "[TX] Queue %d is locked.\n", queue->queue_id); - ieee80211_stop_queue(cw1200->hw, queue->queue_id); + ieee80211_stop_queue(stats->priv->hw, queue->queue_id); } } -static inline void __cw1200_queue_unlock(struct cw1200_queue *queue, - struct cw1200_common *cw1200) +static inline void __cw1200_queue_unlock(struct cw1200_queue *queue) { + struct cw1200_queue_stats *stats = queue->stats; BUG_ON(!queue->tx_locked_cnt); if (--queue->tx_locked_cnt == 0) { txrx_printk(KERN_DEBUG "[TX] Queue %d is unlocked.\n", queue->queue_id); - ieee80211_wake_queue(cw1200->hw, queue->queue_id); + ieee80211_wake_queue(stats->priv->hw, queue->queue_id); } } @@ -65,11 +65,95 @@ static inline u32 cw1200_queue_make_packet_id(u8 queue_generation, u8 queue_id, ((u32)queue_generation << 24); } +static void cw1200_queue_post_gc(struct cw1200_queue_stats *stats, + struct list_head *gc_list) +{ + struct cw1200_queue_item *item; + + while (!list_empty(gc_list)) { + item = list_first_entry( + gc_list, struct cw1200_queue_item, head); + list_del(&item->head); + stats->skb_dtor(stats->priv, item->skb, &item->txpriv); + kfree(item); + } +} + +static void cw1200_queue_register_post_gc(struct list_head *gc_list, + struct cw1200_queue_item *item) +{ + struct cw1200_queue_item *gc_item; + gc_item = kmalloc(sizeof(struct cw1200_queue_item), + GFP_KERNEL | GFP_ATOMIC); + BUG_ON(!gc_item); + memcpy(gc_item, item, sizeof(struct cw1200_queue_item)); + list_move_tail(&gc_item->head, gc_list); +} + +static void __cw1200_queue_gc(struct cw1200_queue *queue, + struct list_head *head, + bool unlock) +{ + struct cw1200_queue_stats *stats = queue->stats; + struct cw1200_queue_item *item = NULL; + bool wakeup_stats = false; + + while (!list_empty(&queue->queue)) { + item = list_first_entry( + &queue->queue, struct cw1200_queue_item, head); + if (jiffies - item->timestamp < queue->ttl) + break; + --queue->num_queued; + --queue->link_map_cache[item->txpriv.link_id]; + spin_lock_bh(&stats->lock); + --stats->num_queued; + if (!--stats->link_map_cache[item->txpriv.link_id]) + wakeup_stats = true; + spin_unlock_bh(&stats->lock); + cw1200_debug_tx_ttl(stats->priv); + cw1200_queue_register_post_gc(head, item); + item->skb = NULL; + list_move_tail(&item->head, &queue->free_pool); + } + + if (wakeup_stats) + wake_up(&stats->wait_link_id_empty); + + if (queue->overfull) { + if (queue->num_queued <= (queue->capacity >> 1)) { + queue->overfull = false; + if (unlock) + __cw1200_queue_unlock(queue); + } else { + unsigned long tmo = item->timestamp + queue->ttl; + mod_timer(&queue->gc, tmo); + cw1200_pm_stay_awake(&stats->priv->pm_state, + tmo - jiffies); + } + } +} + +static void cw1200_queue_gc(unsigned long arg) +{ + LIST_HEAD(list); + struct cw1200_queue *queue = + (struct cw1200_queue *)arg; + + spin_lock_bh(&queue->lock); + __cw1200_queue_gc(queue, &list, true); + spin_unlock_bh(&queue->lock); + cw1200_queue_post_gc(queue->stats, &list); +} + int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, - size_t map_capacity) + size_t map_capacity, + cw1200_queue_skb_dtor_t skb_dtor, + struct cw1200_common *priv) { memset(stats, 0, sizeof(*stats)); stats->map_capacity = map_capacity; + stats->skb_dtor = skb_dtor; + stats->priv = priv; spin_lock_init(&stats->lock); init_waitqueue_head(&stats->wait_link_id_empty); @@ -84,7 +168,8 @@ int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, int cw1200_queue_init(struct cw1200_queue *queue, struct cw1200_queue_stats *stats, u8 queue_id, - size_t capacity) + size_t capacity, + unsigned long ttl) { size_t i; @@ -92,10 +177,14 @@ int cw1200_queue_init(struct cw1200_queue *queue, queue->stats = stats; queue->capacity = capacity; queue->queue_id = queue_id; + queue->ttl = ttl; INIT_LIST_HEAD(&queue->queue); INIT_LIST_HEAD(&queue->pending); INIT_LIST_HEAD(&queue->free_pool); spin_lock_init(&queue->lock); + init_timer(&queue->gc); + queue->gc.data = (unsigned long)queue; + queue->gc.function = cw1200_queue_gc; queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity, GFP_KERNEL); @@ -116,9 +205,10 @@ int cw1200_queue_init(struct cw1200_queue *queue, return 0; } -int cw1200_queue_clear(struct cw1200_queue *queue, struct cw1200_common *priv) +int cw1200_queue_clear(struct cw1200_queue *queue) { int i; + LIST_HEAD(gc_list); struct cw1200_queue_stats *stats = queue->stats; spin_lock_bh(&queue->lock); @@ -128,15 +218,12 @@ int cw1200_queue_clear(struct cw1200_queue *queue, struct cw1200_common *priv) struct cw1200_queue_item *item = list_first_entry( &queue->pending, struct cw1200_queue_item, head); WARN_ON(!item->skb); - if (likely(item->skb)) { - dev_kfree_skb_any(item->skb); - item->skb = NULL; - } + cw1200_queue_register_post_gc(&gc_list, item); + item->skb = NULL; list_move_tail(&item->head, &queue->free_pool); } queue->num_queued = 0; queue->num_pending = 0; - queue->num_sent = 0; spin_lock_bh(&stats->lock); for (i = 0; i < stats->map_capacity; ++i) { @@ -147,10 +234,11 @@ int cw1200_queue_clear(struct cw1200_queue *queue, struct cw1200_common *priv) spin_unlock_bh(&stats->lock); if (unlikely(queue->overfull)) { queue->overfull = false; - __cw1200_queue_unlock(queue, priv); + __cw1200_queue_unlock(queue); } spin_unlock_bh(&queue->lock); wake_up(&stats->wait_link_id_empty); + cw1200_queue_post_gc(stats, &gc_list); return 0; } @@ -160,10 +248,10 @@ void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats) stats->link_map_cache = NULL; } -void cw1200_queue_deinit(struct cw1200_queue *queue, - struct cw1200_common *priv) +void cw1200_queue_deinit(struct cw1200_queue *queue) { - cw1200_queue_clear(queue, priv); + cw1200_queue_clear(queue); + del_timer_sync(&queue->gc); INIT_LIST_HEAD(&queue->free_pool); kfree(queue->pool); kfree(queue->link_map_cache); @@ -196,18 +284,14 @@ size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, return ret; } -int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, - struct sk_buff *skb, struct cw1200_txpriv *txpriv) +int cw1200_queue_put(struct cw1200_queue *queue, + struct sk_buff *skb, + struct cw1200_txpriv *txpriv) { - int ret; - struct wsm_tx *wsm; + int ret = 0; + LIST_HEAD(gc_list); struct cw1200_queue_stats *stats = queue->stats; - wsm = (struct wsm_tx *)skb_push(skb, sizeof(struct wsm_tx)); - ret = cw1200_skb_to_wsm(priv, skb, wsm, txpriv); - if (ret) - return ret; - if (txpriv->link_id >= queue->stats->map_capacity) return -EINVAL; @@ -224,7 +308,7 @@ int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, item->packetID = cw1200_queue_make_packet_id( queue->generation, queue->queue_id, item->generation, item - queue->pool); - wsm->packetID = __cpu_to_le32(item->packetID); + item->timestamp = jiffies; ++queue->num_queued; ++queue->link_map_cache[txpriv->link_id]; @@ -236,12 +320,17 @@ int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv, if (queue->num_queued >= queue->capacity) { queue->overfull = true; - __cw1200_queue_lock(queue, priv); + __cw1200_queue_gc(queue, &gc_list, false); + if (queue->overfull) + __cw1200_queue_lock(queue); + } } else { ret = -ENOENT; } spin_unlock_bh(&queue->lock); + if (unlikely(!list_empty(&gc_list))) + cw1200_queue_post_gc(stats, &gc_list); return ret; } @@ -268,6 +357,7 @@ int cw1200_queue_get(struct cw1200_queue *queue, *tx = (struct wsm_tx *)item->skb->data; *tx_info = IEEE80211_SKB_CB(item->skb); *txpriv = &item->txpriv; + (*tx)->packetID = __cpu_to_le32(item->packetID); list_move_tail(&item->head, &queue->pending); ++queue->num_pending; --queue->link_map_cache[item->txpriv.link_id]; @@ -307,7 +397,6 @@ int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID) WARN_ON(1); ret = -ENOENT; } else { - struct wsm_tx *wsm = (struct wsm_tx *)item->skb->data; --queue->num_pending; ++queue->link_map_cache[item->txpriv.link_id]; @@ -319,7 +408,6 @@ int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID) item->generation = ++item_generation; item->packetID = cw1200_queue_make_packet_id( queue_generation, queue_id, item_generation, item_id); - wsm->packetID = __cpu_to_le32(item->packetID); list_move(&item->head, &queue->queue); } spin_unlock_bh(&queue->lock); @@ -333,7 +421,6 @@ int cw1200_queue_requeue_all(struct cw1200_queue *queue) while (!list_empty(&queue->pending)) { struct cw1200_queue_item *item = list_entry( queue->pending.prev, struct cw1200_queue_item, head); - struct wsm_tx *wsm = (struct wsm_tx *)item->skb->data; --queue->num_pending; ++queue->link_map_cache[item->txpriv.link_id]; @@ -347,7 +434,6 @@ int cw1200_queue_requeue_all(struct cw1200_queue *queue) item->packetID = cw1200_queue_make_packet_id( queue->generation, queue->queue_id, item->generation, item - queue->pool); - wsm->packetID = __cpu_to_le32(item->packetID); list_move(&item->head, &queue->queue); } spin_unlock_bh(&queue->lock); @@ -355,13 +441,15 @@ int cw1200_queue_requeue_all(struct cw1200_queue *queue) return 0; } -int cw1200_queue_remove(struct cw1200_queue *queue, struct cw1200_common *priv, - u32 packetID) +int cw1200_queue_remove(struct cw1200_queue *queue, u32 packetID) { int ret = 0; u8 queue_generation, queue_id, item_generation, item_id; struct cw1200_queue_item *item; - struct sk_buff *skb_to_free = NULL; + struct cw1200_queue_stats *stats = queue->stats; + struct sk_buff *gc_skb = NULL; + struct cw1200_txpriv gc_txpriv; + cw1200_queue_parse_id(packetID, &queue_generation, &queue_id, &item_generation, &item_id); @@ -378,12 +466,13 @@ int cw1200_queue_remove(struct cw1200_queue *queue, struct cw1200_common *priv, WARN_ON(1); ret = -ENOENT; } else { + gc_txpriv = item->txpriv; + gc_skb = item->skb; + item->skb = NULL; --queue->num_pending; --queue->num_queued; ++queue->num_sent; ++item->generation; - skb_to_free = item->skb; - item->skb = NULL; /* Do not use list_move_tail here, but list_move: * try to utilize cache row. */ @@ -392,20 +481,19 @@ int cw1200_queue_remove(struct cw1200_queue *queue, struct cw1200_common *priv, if (unlikely(queue->overfull) && (queue->num_queued <= (queue->capacity >> 1))) { queue->overfull = false; - __cw1200_queue_unlock(queue, priv); + __cw1200_queue_unlock(queue); } } spin_unlock_bh(&queue->lock); - if (skb_to_free) - dev_kfree_skb_any(item->skb); + if (gc_skb) + stats->skb_dtor(stats->priv, gc_skb, &gc_txpriv); return ret; } int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, - struct sk_buff **skb, - const struct cw1200_txpriv **txpriv) + struct sk_buff **skb) { int ret = 0; u8 queue_generation, queue_id, item_generation, item_id; @@ -427,42 +515,25 @@ int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, ret = -ENOENT; } else { *skb = item->skb; - *txpriv = &item->txpriv; - item->skb = NULL; } spin_unlock_bh(&queue->lock); return ret; } -void cw1200_queue_lock(struct cw1200_queue *queue, struct cw1200_common *cw1200) +void cw1200_queue_lock(struct cw1200_queue *queue) { spin_lock_bh(&queue->lock); - __cw1200_queue_lock(queue, cw1200); + __cw1200_queue_lock(queue); spin_unlock_bh(&queue->lock); } -void cw1200_queue_unlock(struct cw1200_queue *queue, - struct cw1200_common *cw1200) +void cw1200_queue_unlock(struct cw1200_queue *queue) { spin_lock_bh(&queue->lock); - __cw1200_queue_unlock(queue, cw1200); + __cw1200_queue_unlock(queue); spin_unlock_bh(&queue->lock); } -/* -int cw1200_queue_get_stats(struct cw1200_queue *queue, - struct ieee80211_tx_queue_stats *stats) -{ - spin_lock_bh(&queue->lock); - stats->len = queue->num_queued; - stats->limit = queue->capacity; - stats->count = queue->num_sent; - spin_unlock_bh(&queue->lock); - - return 0; -} -*/ - bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, u32 link_id_map) { diff --git a/drivers/staging/cw1200/queue.h b/drivers/staging/cw1200/queue.h index 502496b142b..bff33625c8a 100644 --- a/drivers/staging/cw1200/queue.h +++ b/drivers/staging/cw1200/queue.h @@ -22,6 +22,10 @@ /* forward */ struct cw1200_queue_stats; +typedef void (*cw1200_queue_skb_dtor_t)(struct cw1200_common *priv, + struct sk_buff *skb, + const struct cw1200_txpriv *txpriv); + struct cw1200_queue { struct cw1200_queue_stats *stats; size_t capacity; @@ -38,6 +42,8 @@ struct cw1200_queue { spinlock_t lock; u8 queue_id; u8 generation; + struct timer_list gc; + unsigned long ttl; }; struct cw1200_queue_stats { @@ -46,29 +52,36 @@ struct cw1200_queue_stats { int num_queued; size_t map_capacity; wait_queue_head_t wait_link_id_empty; + cw1200_queue_skb_dtor_t skb_dtor; + struct cw1200_common *priv; }; struct cw1200_txpriv { u8 link_id; u8 raw_link_id; u8 tid; + u8 rate_id; + u8 offset; }; int cw1200_queue_stats_init(struct cw1200_queue_stats *stats, - size_t map_capacity); + size_t map_capacity, + cw1200_queue_skb_dtor_t skb_dtor, + struct cw1200_common *priv); int cw1200_queue_init(struct cw1200_queue *queue, struct cw1200_queue_stats *stats, u8 queue_id, - size_t capacity); -int cw1200_queue_clear(struct cw1200_queue *queue, struct cw1200_common *priv); + size_t capacity, + unsigned long ttl); +int cw1200_queue_clear(struct cw1200_queue *queue); void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats); -void cw1200_queue_deinit(struct cw1200_queue *queue, - struct cw1200_common *priv); +void cw1200_queue_deinit(struct cw1200_queue *queue); size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue, u32 link_id_map); -int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *cw1200, - struct sk_buff *skb, struct cw1200_txpriv *txpriv); +int cw1200_queue_put(struct cw1200_queue *queue, + struct sk_buff *skb, + struct cw1200_txpriv *txpriv); int cw1200_queue_get(struct cw1200_queue *queue, u32 link_id_map, struct wsm_tx **tx, @@ -76,15 +89,12 @@ int cw1200_queue_get(struct cw1200_queue *queue, const struct cw1200_txpriv **txpriv); int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID); int cw1200_queue_requeue_all(struct cw1200_queue *queue); -int cw1200_queue_remove(struct cw1200_queue *queue, struct cw1200_common *priv, - u32 packetID); +int cw1200_queue_remove(struct cw1200_queue *queue, + u32 packetID); int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, - struct sk_buff **skb, - const struct cw1200_txpriv **txpriv); -void cw1200_queue_lock(struct cw1200_queue *queue, - struct cw1200_common *cw1200); -void cw1200_queue_unlock(struct cw1200_queue *queue, - struct cw1200_common *cw1200); + struct sk_buff **skb); +void cw1200_queue_lock(struct cw1200_queue *queue); +void cw1200_queue_unlock(struct cw1200_queue *queue); bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, u32 link_id_map); diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index cc5a345f441..434e054672c 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -136,7 +136,7 @@ void cw1200_stop(struct ieee80211_hw *dev) priv->join_status = CW1200_JOIN_STATUS_PASSIVE; for (i = 0; i < 4; i++) - cw1200_queue_clear(&priv->tx_queue[i], priv); + cw1200_queue_clear(&priv->tx_queue[i]); /* HACK! */ if (atomic_xchg(&priv->tx_lock, 1) != 1) @@ -760,7 +760,7 @@ int __cw1200_flush(struct cw1200_common *priv, bool drop) */ if (drop) { for (i = 0; i < 4; ++i) - cw1200_queue_clear(&priv->tx_queue[i], priv); + cw1200_queue_clear(&priv->tx_queue[i]); } else { ret = wait_event_timeout( priv->tx_queue_stats.wait_link_id_empty, @@ -1135,7 +1135,7 @@ void cw1200_join_work(struct work_struct *work) bss = cfg80211_get_bss(priv->hw->wiphy, NULL, bssid, NULL, 0, 0, 0); if (!bss) { cw1200_queue_remove(&priv->tx_queue[queueId], - priv, __le32_to_cpu(wsm->packetID)); + __le32_to_cpu(wsm->packetID)); wsm_unlock_tx(priv); return; } @@ -1210,7 +1210,7 @@ void cw1200_join_work(struct work_struct *work) memset(&priv->join_bssid[0], 0, sizeof(priv->join_bssid)); cw1200_queue_remove(&priv->tx_queue[queueId], - priv, __le32_to_cpu(wsm->packetID)); + __le32_to_cpu(wsm->packetID)); cancel_delayed_work_sync(&priv->join_timeout); cw1200_update_listening(priv, priv->listening); } else { diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 04b7b0d091e..72f5acdccdc 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -24,16 +24,10 @@ #define tx_policy_printk(...) #endif -/* txrx private */ -struct __cw1200_txpriv { - struct cw1200_txpriv super; - u16 ethertype; -}; +#define CW1200_INVALID_RATE_ID (0xFF) static int cw1200_handle_action_rx(struct cw1200_common *priv, struct sk_buff *skb); -static int cw1200_handle_action_tx(struct cw1200_common *priv, - struct sk_buff *skb); static const struct ieee80211_rate * cw1200_get_tx_rate(const struct cw1200_common *priv, const struct ieee80211_tx_rate *rate); @@ -45,14 +39,14 @@ static inline void cw1200_tx_queues_lock(struct cw1200_common *priv) { int i; for (i = 0; i < 4; ++i) - cw1200_queue_lock(&priv->tx_queue[i], priv); + cw1200_queue_lock(&priv->tx_queue[i]); } static inline void cw1200_tx_queues_unlock(struct cw1200_common *priv) { int i; for (i = 0; i < 4; ++i) - cw1200_queue_unlock(&priv->tx_queue[i], priv); + cw1200_queue_unlock(&priv->tx_queue[i]); } /* ******************************************************************** */ @@ -269,7 +263,7 @@ static int tx_policy_get(struct cw1200_common *priv, return idx; } -void tx_policy_put(struct cw1200_common *priv, int idx) +static void tx_policy_put(struct cw1200_common *priv, int idx) { int usage, locked; struct tx_policy_cache *cache = &priv->tx_policy_cache; @@ -352,6 +346,18 @@ void tx_policy_upload_work(struct work_struct *work) /* ******************************************************************** */ /* cw1200 TX implementation */ +struct cw1200_txinfo { + struct sk_buff *skb; + unsigned queue; + struct ieee80211_tx_info *tx_info; + const struct ieee80211_rate *rate; + struct ieee80211_hdr *hdr; + size_t hdrlen; + const u8 *da; + struct cw1200_sta_priv *sta_priv; + struct cw1200_txpriv txpriv; +}; + u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates) { u32 ret = 0; @@ -375,252 +381,292 @@ cw1200_get_tx_rate(const struct cw1200_common *priv, bitrates[rate->idx]; } -/* NOTE: cw1200_skb_to_wsm executes in atomic context. */ -int cw1200_skb_to_wsm(struct cw1200_common *priv, struct sk_buff *skb, - struct wsm_tx *wsm, struct cw1200_txpriv *txpriv) +static int +cw1200_tx_h_calc_link_ids(struct cw1200_common *priv, + struct cw1200_txinfo *t) { - struct __cw1200_txpriv *info = - container_of(txpriv, struct __cw1200_txpriv, super); - bool tx_policy_renew = false; - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - const struct ieee80211_rate *rate = cw1200_get_tx_rate(priv, - &tx_info->control.rates[0]); - u8 priority = 0; - memset(wsm, 0, sizeof(*wsm)); - wsm->hdr.len = __cpu_to_le16(skb->len); - wsm->hdr.id = __cpu_to_le16(0x0004); - if (rate) { - wsm->maxTxRate = rate->hw_value; - if (rate->flags & IEEE80211_TX_RC_MCS) { - if (cw1200_ht_greenfield(&priv->ht_info)) - wsm->htTxParameters |= - __cpu_to_le32(WSM_HT_TX_GREENFIELD); - else - wsm->htTxParameters |= - __cpu_to_le32(WSM_HT_TX_MIXED); + if (likely(t->tx_info->control.sta && t->sta_priv->link_id)) + t->txpriv.raw_link_id = + t->txpriv.link_id = + t->sta_priv->link_id; + else if (priv->mode != NL80211_IFTYPE_AP) + t->txpriv.raw_link_id = + t->txpriv.link_id = 0; + else if (is_multicast_ether_addr(t->da)) { + if (priv->enable_beacon) { + t->txpriv.raw_link_id = 0; + t->txpriv.link_id = CW1200_LINK_ID_AFTER_DTIM; + } else { + t->txpriv.raw_link_id = 0; + t->txpriv.link_id = 0; } + } else { + t->txpriv.link_id = cw1200_find_link_id(priv, t->da); + if (!t->txpriv.link_id) + t->txpriv.link_id = cw1200_alloc_link_id(priv, t->da); + if (!t->txpriv.link_id) { + wiphy_err(priv->hw->wiphy, + "%s: No more link IDs available.\n", + __func__); + return -ENOENT; + } + t->txpriv.raw_link_id = t->txpriv.link_id; } - wsm->flags = tx_policy_get(priv, - tx_info->control.rates, IEEE80211_TX_MAX_RATES, - &tx_policy_renew) << 4; + if (t->txpriv.raw_link_id) + priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp = + jiffies; - if (tx_policy_renew) { - tx_policy_printk(KERN_DEBUG "[TX] TX policy renew.\n"); - /* It's not so optimal to stop TX queues every now and then. - * Maybe it's better to reimplement task scheduling with - * a counter. */ - /* cw1200_tx_queues_lock(priv); */ - /* Definetly better. TODO. */ - wsm_lock_tx_async(priv); - cw1200_tx_queues_lock(priv); - queue_work(priv->workqueue, &priv->tx_policy_upload_work); + if (t->tx_info->control.sta && + (t->tx_info->control.sta->uapsd_queues & BIT(t->queue))) + t->txpriv.link_id = CW1200_LINK_ID_UAPSD; + return 0; +} + +static void +cw1200_tx_h_pm(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + if (unlikely(ieee80211_is_auth(t->hdr->frame_control))) { + u32 mask = ~BIT(t->txpriv.raw_link_id); + spin_lock_bh(&priv->ps_state_lock); + priv->sta_asleep_mask &= mask; + priv->pspoll_mask &= mask; + spin_unlock_bh(&priv->ps_state_lock); } +} - wsm->queueId = wsm_queue_id_to_wsm(skb_get_queue_mapping(skb)); +static void +cw1200_tx_h_calc_tid(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + if (ieee80211_is_data_qos(t->hdr->frame_control)) { + u8 *qos = ieee80211_get_qos_ctl(t->hdr); + t->txpriv.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK; + } else if (ieee80211_is_data(t->hdr->frame_control)) { + t->txpriv.tid = 0; + } +} - /* BT Coex specific handling */ - if (priv->is_BT_Present) { - struct ieee80211_hdr *hdr = - (struct ieee80211_hdr *)(skb->data + sizeof(struct wsm_tx)); +/* IV/ICV injection. */ +/* TODO: Quite unoptimal. It's better co modify mac80211 + * to reserve space for IV */ +static int +cw1200_tx_h_crypt(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + size_t iv_len; + size_t icv_len; + u8 *icv; + u8 *newhdr; - if (cpu_to_be16(info->ethertype) == ETH_P_PAE) - priority = WSM_EPTA_PRIORITY_EAPOL; - else if (ieee80211_is_action(hdr->frame_control)) - priority = WSM_EPTA_PRIORITY_ACTION; - else if (ieee80211_is_mgmt(hdr->frame_control)) - priority = WSM_EPTA_PRIORITY_MGT; - else if ((wsm->queueId == WSM_QUEUE_VOICE)) - priority = WSM_EPTA_PRIORITY_VOICE; - else if ((wsm->queueId == WSM_QUEUE_VIDEO)) - priority = WSM_EPTA_PRIORITY_VIDEO; - else - priority = WSM_EPTA_PRIORITY_DATA; + if (!t->tx_info->control.hw_key || + !(t->hdr->frame_control & + __cpu_to_le32(IEEE80211_FCTL_PROTECTED))) + return 0; - txrx_printk(KERN_DEBUG "[TX] EPTA priority %x.\n", - ((priority) & 0x7)); + iv_len = t->tx_info->control.hw_key->iv_len; + icv_len = t->tx_info->control.hw_key->icv_len; - /* Set EPTA priority */ - wsm->flags |= (((priority) & 0x7) << 1); + if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { + icv_len += 8; /* MIC */ } + if ((skb_headroom(t->skb) + skb_tailroom(t->skb) < + iv_len + icv_len + WSM_TX_EXTRA_HEADROOM) || + (skb_headroom(t->skb) < + iv_len + WSM_TX_EXTRA_HEADROOM)) { + wiphy_err(priv->hw->wiphy, + "Bug: no space allocated for crypto headers.\n" + "headroom: %d, tailroom: %d, " + "req_headroom: %d, req_tailroom: %d\n" + "Please fix it in cw1200_get_skb().\n", + skb_headroom(t->skb), skb_tailroom(t->skb), + iv_len + WSM_TX_EXTRA_HEADROOM, icv_len); + return -ENOMEM; + } else if (skb_tailroom(t->skb) < icv_len) { + size_t offset = icv_len - skb_tailroom(t->skb); + u8 *p; + wiphy_warn(priv->hw->wiphy, + "Slowpath: tailroom is not big enough. " + "Req: %d, got: %d.\n", + icv_len, skb_tailroom(t->skb)); + + p = skb_push(t->skb, offset); + memmove(p, &p[offset], t->skb->len - offset); + skb_trim(t->skb, t->skb->len - offset); + } + + newhdr = skb_push(t->skb, iv_len); + memmove(newhdr, newhdr + iv_len, t->hdrlen); + t->hdrlen += iv_len; + icv = skb_put(t->skb, icv_len); + return 0; } -/* ******************************************************************** */ +static int +cw1200_tx_h_align(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + size_t offset = (size_t)t->skb->data & 3; + u8 *p; + + if (!offset) + return 0; + + if (skb_headroom(t->skb) < offset) { + wiphy_err(priv->hw->wiphy, + "Bug: no space allocated " + "for DMA alignment.\n" + "headroom: %d\n", + skb_headroom(t->skb)); + return -ENOMEM; + } + p = skb_push(t->skb, offset); + memmove(p, &p[offset], t->skb->len - offset); + skb_trim(t->skb, t->skb->len - offset); + cw1200_debug_tx_copy(priv); + return 0; +} -void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +static int +cw1200_tx_h_action(struct cw1200_common *priv, + struct cw1200_txinfo *t) { - struct cw1200_common *priv = dev->priv; - unsigned queue = skb_get_queue_mapping(skb); - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr = - (struct ieee80211_hdr *)skb->data; - int was_buffered = 0; - const u8 *da = ieee80211_get_DA(hdr); - struct cw1200_sta_priv *sta_priv = - (struct cw1200_sta_priv *)&tx_info->control.sta->drv_priv; - int link_id, raw_link_id; - int ret; - struct __cw1200_txpriv txpriv = { - .super.tid = CW1200_MAX_TID, - }; + struct ieee80211_mgmt *mgmt = + (struct ieee80211_mgmt *)t->hdr; + if (ieee80211_is_action(t->hdr->frame_control) && + mgmt->u.action.category == WLAN_CATEGORY_BACK) + return 1; + else + return 0; +} - if (likely(tx_info->control.sta && sta_priv->link_id)) - raw_link_id = link_id = sta_priv->link_id; - else if (priv->mode != NL80211_IFTYPE_AP) - raw_link_id = link_id = 0; - else if (is_multicast_ether_addr(da)) { - if (priv->enable_beacon) { - raw_link_id = 0; - link_id = CW1200_LINK_ID_AFTER_DTIM; - } else { - raw_link_id = link_id = 0; - } - } else { - raw_link_id = cw1200_find_link_id(priv, da); - if (!raw_link_id) - raw_link_id = cw1200_alloc_link_id(priv, da); - if (!raw_link_id) { - wiphy_err(priv->hw->wiphy, - "%s: No more link IDs available.\n", - __func__); - goto err; - } - link_id = raw_link_id; +/* Add WSM header */ +static struct wsm_tx * +cw1200_tx_h_wsm(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + struct wsm_tx *wsm; + + if (skb_headroom(t->skb) < sizeof(struct wsm_tx)) { + wiphy_err(priv->hw->wiphy, + "Bug: no space allocated " + "for WSM header.\n" + "headroom: %d\n", + skb_headroom(t->skb)); + return NULL; } - if (raw_link_id) - priv->link_id_db[raw_link_id - 1].timestamp = jiffies; - if (tx_info->control.sta && - (tx_info->control.sta->uapsd_queues & BIT(queue))) - link_id = CW1200_LINK_ID_UAPSD; + wsm = (struct wsm_tx *)skb_push(t->skb, sizeof(struct wsm_tx)); + t->txpriv.offset += sizeof(struct wsm_tx); + memset(wsm, 0, sizeof(*wsm)); + wsm->hdr.len = __cpu_to_le16(t->skb->len); + wsm->hdr.id = __cpu_to_le16(0x0004); + wsm->queueId = wsm_queue_id_to_wsm(t->queue); + return wsm; +} - txrx_printk(KERN_DEBUG "[TX] TX %d bytes (queue: %d, link_id: %d (%d)).\n", - skb->len, queue, link_id, raw_link_id); +/* BT Coex specific handling */ +static void +cw1200_tx_h_bt(struct cw1200_common *priv, + struct cw1200_txinfo *t, + struct wsm_tx *wsm) +{ + u8 priority = 0; - if (WARN_ON(queue >= 4)) - goto err; + if (!priv->is_BT_Present) + return; - if (unlikely(ieee80211_is_auth(hdr->frame_control))) { - spin_lock_bh(&priv->ps_state_lock); - priv->sta_asleep_mask &= ~BIT(raw_link_id); - priv->pspoll_mask &= ~BIT(raw_link_id); - spin_unlock_bh(&priv->ps_state_lock); - } else if (ieee80211_is_data_qos(hdr->frame_control) || - ieee80211_is_qos_nullfunc(hdr->frame_control)) { - u8 *qos = ieee80211_get_qos_ctl(hdr); - txpriv.super.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK; - } else if (ieee80211_is_data(hdr->frame_control) || - ieee80211_is_nullfunc(hdr->frame_control)) { - txpriv.super.tid = 0; + if (unlikely(ieee80211_is_nullfunc(t->hdr->frame_control))) + priority = WSM_EPTA_PRIORITY_MGT; + else if (ieee80211_is_data(t->hdr->frame_control)) { + /* Skip LLC SNAP header (+6) */ + u8 *payload = &t->skb->data[t->hdrlen]; + u16 *ethertype = (u16 *) &payload[6]; + if (unlikely(*ethertype == __be16_to_cpu(ETH_P_PAE))) + priority = WSM_EPTA_PRIORITY_EAPOL; } - - /* BT Coex support related configuration */ - if (priv->is_BT_Present) { - txpriv.ethertype = 0; - - if (ieee80211_is_data_qos(hdr->frame_control) || - ieee80211_is_data(hdr->frame_control)) { - unsigned int headerlen = - ieee80211_get_hdrlen_from_skb(skb); - - /* Skip LLC SNAP header (+6) */ - if (headerlen > 0) - txpriv.ethertype = - *((u16 *)(skb->data + headerlen + 6)); - } - else if (ieee80211_is_assoc_req(hdr->frame_control) || - ieee80211_is_reassoc_req(hdr->frame_control)) { - struct ieee80211_mgmt *mgt_frame = - (struct ieee80211_mgmt *)skb->data; - - if (mgt_frame->u.assoc_req.listen_interval < - priv->listen_interval) { - txrx_printk(KERN_DEBUG - "Modified Listen Interval to %x from %x\n", + else if (unlikely(ieee80211_is_assoc_req(t->hdr->frame_control) || + ieee80211_is_reassoc_req(t->hdr->frame_control))) { + struct ieee80211_mgmt *mgt_frame = + (struct ieee80211_mgmt *)t->hdr; + + if (mgt_frame->u.assoc_req.listen_interval < + priv->listen_interval) { + txrx_printk(KERN_DEBUG + "Modified Listen Interval to %d from %d\n", priv->listen_interval, mgt_frame->u.assoc_req.listen_interval); - /* Replace listen interval derieved from - the one read from SDD */ - mgt_frame->u.assoc_req.listen_interval = - priv->listen_interval; - } + /* Replace listen interval derieved from + * the one read from SDD */ + mgt_frame->u.assoc_req.listen_interval = + priv->listen_interval; } } - /* IV/ICV injection. */ - /* TODO: Quite unoptimal. It's better co modify mac80211 - * to reserve space for IV */ - if (tx_info->control.hw_key && - (hdr->frame_control & - __cpu_to_le32(IEEE80211_FCTL_PROTECTED))) { - size_t hdrlen = ieee80211_hdrlen(hdr->frame_control); - size_t iv_len = tx_info->control.hw_key->iv_len; - size_t icv_len = tx_info->control.hw_key->icv_len; - u8 *icv; - u8 *newhdr; - - if (tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { - icv_len += 8; /* MIC */ - } + if (likely(!priority)) { + if (ieee80211_is_action(t->hdr->frame_control)) + priority = WSM_EPTA_PRIORITY_ACTION; + else if (ieee80211_is_mgmt(t->hdr->frame_control)) + priority = WSM_EPTA_PRIORITY_MGT; + else if ((wsm->queueId == WSM_QUEUE_VOICE)) + priority = WSM_EPTA_PRIORITY_VOICE; + else if ((wsm->queueId == WSM_QUEUE_VIDEO)) + priority = WSM_EPTA_PRIORITY_VIDEO; + else + priority = WSM_EPTA_PRIORITY_DATA; + } - if ((skb_headroom(skb) + skb_tailroom(skb) < - iv_len + icv_len + WSM_TX_EXTRA_HEADROOM) || - (skb_headroom(skb) < iv_len + WSM_TX_EXTRA_HEADROOM)) { - wiphy_err(priv->hw->wiphy, - "Bug: no space allocated " - "for crypto headers.\n" - "headroom: %d, tailroom: %d, " - "req_headroom: %d, req_tailroom: %d\n" - "Please fix it in cw1200_get_skb().\n", - skb_headroom(skb), skb_tailroom(skb), - iv_len + WSM_TX_EXTRA_HEADROOM, icv_len); - goto err; - } else if (skb_tailroom(skb) < icv_len) { - size_t offset = icv_len - skb_tailroom(skb); - u8 *p; - wiphy_warn(priv->hw->wiphy, - "Slowpath: tailroom is not big enough. " - "Req: %d, got: %d.\n", - icv_len, skb_tailroom(skb)); - - p = skb_push(skb, offset); - memmove(p, &p[offset], skb->len - offset); - skb_trim(skb, skb->len - offset); - } + txrx_printk(KERN_DEBUG "[TX] EPTA priority %d.\n", + priority); - newhdr = skb_push(skb, iv_len); - memmove(newhdr, newhdr + iv_len, hdrlen); - memset(&newhdr[hdrlen], 0, iv_len); - icv = skb_put(skb, icv_len); - memset(icv, 0, icv_len); - } + wsm->flags |= priority << 1; +} - if ((size_t)skb->data & 3) { - size_t offset = (size_t)skb->data & 3; - u8 *p; - if (skb_headroom(skb) < 4) { - wiphy_err(priv->hw->wiphy, - "Bug: no space allocated " - "for DMA alignment.\n" - "headroom: %d\n", - skb_headroom(skb)); - goto err; - } - p = skb_push(skb, offset); - memmove(p, &p[offset], skb->len - offset); - skb_trim(skb, skb->len - offset); - cw1200_debug_tx_copy(priv); +static void +cw1200_tx_h_rate_policy(struct cw1200_common *priv, + struct cw1200_txinfo *t, + struct wsm_tx *wsm) +{ + bool tx_policy_renew = false; + + wsm->maxTxRate = t->rate->hw_value; + if (t->rate->flags & IEEE80211_TX_RC_MCS) { + if (cw1200_ht_greenfield(&priv->ht_info)) + wsm->htTxParameters |= + __cpu_to_le32(WSM_HT_TX_GREENFIELD); + else + wsm->htTxParameters |= + __cpu_to_le32(WSM_HT_TX_MIXED); } - if (ieee80211_is_action(hdr->frame_control)) - if (cw1200_handle_action_tx(priv, skb)) - goto drop; + t->txpriv.rate_id = tx_policy_get(priv, + t->tx_info->control.rates, IEEE80211_TX_MAX_RATES, + &tx_policy_renew); + wsm->flags = t->txpriv.rate_id << 4; - spin_lock_bh(&priv->ps_state_lock); + if (tx_policy_renew) { + tx_policy_printk(KERN_DEBUG "[TX] TX policy renew.\n"); + /* It's not so optimal to stop TX queues every now and then. + * Maybe it's better to reimplement task scheduling with + * a counter. */ + /* cw1200_tx_queues_lock(priv); */ + /* Definetly better. TODO. */ + wsm_lock_tx_async(priv); + cw1200_tx_queues_lock(priv); + queue_work(priv->workqueue, &priv->tx_policy_upload_work); + } +} - if (link_id == CW1200_LINK_ID_AFTER_DTIM && +static bool +cw1200_tx_h_pm_state(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + int was_buffered = 1; + + if (t->txpriv.link_id == CW1200_LINK_ID_AFTER_DTIM && !priv->buffered_multicasts) { priv->buffered_multicasts = true; if (priv->sta_asleep_mask) @@ -628,35 +674,82 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) &priv->multicast_start_work); } - if (raw_link_id && txpriv.super.tid < CW1200_MAX_TID) - was_buffered = priv->link_id_db[raw_link_id - 1] - .buffered[txpriv.super.tid]++; + if (t->txpriv.raw_link_id && t->txpriv.tid < CW1200_MAX_TID) + was_buffered = priv->link_id_db[t->txpriv.raw_link_id - 1] + .buffered[t->txpriv.tid]++; - txpriv.super.link_id = link_id; - txpriv.super.raw_link_id = raw_link_id; - ret = cw1200_queue_put(&priv->tx_queue[queue], priv, skb, - &txpriv.super); + return !was_buffered; +} - spin_unlock_bh(&priv->ps_state_lock); +/* ******************************************************************** */ - if (raw_link_id && !was_buffered && txpriv.super.tid < CW1200_MAX_TID) - ieee80211_sta_set_buffered(tx_info->control.sta, - txpriv.super.tid, true); +void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct cw1200_common *priv = dev->priv; + struct cw1200_txinfo t = { + .skb = skb, + .queue = skb_get_queue_mapping(skb), + .tx_info = IEEE80211_SKB_CB(skb), + .hdr = (struct ieee80211_hdr *)skb->data, + .txpriv.tid = CW1200_MAX_TID, + .txpriv.rate_id = CW1200_INVALID_RATE_ID, + }; + struct wsm_tx *wsm; + bool tid_update = 0; + int ret; - if (!WARN_ON(ret)) - cw1200_bh_wakeup(priv); - else - goto err; + t.rate = cw1200_get_tx_rate(priv, + &t.tx_info->control.rates[0]), + t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control); + t.da = ieee80211_get_DA(t.hdr); + t.sta_priv = + (struct cw1200_sta_priv *)&t.tx_info->control.sta->drv_priv; - return; + if (WARN_ON(t.queue >= 4 || !t.rate)) + goto drop; + + if ((ret = cw1200_tx_h_calc_link_ids(priv, &t))) + goto drop; + + txrx_printk(KERN_DEBUG "[TX] TX %d bytes " + "(queue: %d, link_id: %d (%d)).\n", + skb->len, t.queue, t.txpriv.link_id, + t.txpriv.raw_link_id); + + cw1200_tx_h_pm(priv, &t); + cw1200_tx_h_calc_tid(priv, &t); + if ((ret = cw1200_tx_h_crypt(priv, &t))) + goto drop; + if ((ret = cw1200_tx_h_align(priv, &t))) + goto drop; + if ((ret = cw1200_tx_h_action(priv, &t))) + goto drop; + wsm = cw1200_tx_h_wsm(priv, &t); + if (!wsm) { + ret = -ENOMEM; + goto drop; + } + cw1200_tx_h_bt(priv, &t, wsm); + cw1200_tx_h_rate_policy(priv, &t, wsm); + + spin_lock_bh(&priv->ps_state_lock); + { + tid_update = cw1200_tx_h_pm_state(priv, &t); + BUG_ON(cw1200_queue_put(&priv->tx_queue[t.queue], + t.skb, &t.txpriv)); + } + spin_unlock_bh(&priv->ps_state_lock); + + if (tid_update) + ieee80211_sta_set_buffered(t.tx_info->control.sta, + t.txpriv.tid, true); + + cw1200_bh_wakeup(priv); -err: - /* TODO: Update TX failure counters */ - dev_kfree_skb_any(skb); return; drop: - dev_kfree_skb_any(skb); + cw1200_skb_dtor(priv, skb, &t.txpriv); return; } @@ -674,18 +767,6 @@ static int cw1200_handle_action_rx(struct cw1200_common *priv, return 0; } -static int cw1200_handle_action_tx(struct cw1200_common *priv, - struct sk_buff *skb) -{ - struct ieee80211_mgmt *mgmt = (void *)skb->data; - - /* Filter block ACK negotiation: fully controlled by firmware */ - if (mgmt->u.action.category == WLAN_CATEGORY_BACK) - return 1; - - return 0; -} - static int cw1200_handle_pspoll(struct cw1200_common *priv, struct sk_buff *skb) { @@ -709,11 +790,6 @@ static int cw1200_handle_pspoll(struct cw1200_common *priv, sta_priv = (struct cw1200_sta_priv *)&sta->drv_priv; link_id = sta_priv->link_id; pspoll_mask = BIT(sta_priv->link_id); - - /* HACK! To be removed when accurate TX ststus - * reporting for dropped frames is implemented. */ - if (priv->pspoll_mask & pspoll_mask) - ieee80211_sta_eosp_irqsafe(sta); } rcu_read_unlock(); if (!link_id) @@ -746,7 +822,6 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, u8 queue_id = cw1200_queue_get_queue_id(arg->packetID); struct cw1200_queue *queue = &priv->tx_queue[queue_id]; struct sk_buff *skb; - const struct cw1200_txpriv *txpriv = NULL; txrx_printk(KERN_DEBUG "[TX] TX confirm: %d, %d.\n", arg->status, arg->ackFailures); @@ -780,10 +855,8 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, WARN_ON(cw1200_queue_requeue(queue, arg->packetID)); } else if (!WARN_ON(cw1200_queue_get_skb( - queue, arg->packetID, &skb, &txpriv))) { + queue, arg->packetID, &skb))) { struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb); - struct wsm_tx *wsm_tx = (struct wsm_tx *)skb->data; - int rate_id = (wsm_tx->flags >> 4) & 0x07; int tx_count = arg->ackFailures; u8 ht_flags = 0; int i; @@ -791,9 +864,6 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, if (cw1200_ht_greenfield(&priv->ht_info)) ht_flags |= IEEE80211_TX_RC_GREEN_FIELD; - /* Release used TX rate policy */ - tx_policy_put(priv, rate_id); - if (likely(!arg->status)) { tx->flags |= IEEE80211_TX_STAT_ACK; priv->cqm_tx_failure_count = 0; @@ -833,15 +903,11 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, tx->status.rates[i].idx = -1; } - skb_pull(skb, sizeof(struct wsm_tx)); - cw1200_notify_buffered_tx(priv, skb, arg->link_id, - txpriv->tid); - ieee80211_tx_status(priv->hw, skb); - WARN_ON(cw1200_queue_remove(queue, priv, arg->packetID)); + cw1200_queue_remove(queue, arg->packetID); } } -void cw1200_notify_buffered_tx(struct cw1200_common *priv, +static void cw1200_notify_buffered_tx(struct cw1200_common *priv, struct sk_buff *skb, int link_id, int tid) { struct ieee80211_sta *sta; @@ -870,6 +936,19 @@ void cw1200_notify_buffered_tx(struct cw1200_common *priv, } +void cw1200_skb_dtor(struct cw1200_common *priv, + struct sk_buff *skb, + const struct cw1200_txpriv *txpriv) +{ + skb_pull(skb, txpriv->offset); + if (txpriv->rate_id != CW1200_INVALID_RATE_ID) { + cw1200_notify_buffered_tx(priv, skb, + txpriv->raw_link_id, txpriv->tid); + tx_policy_put(priv, txpriv->rate_id); + } + ieee80211_tx_status(priv->hw, skb); +} + void cw1200_rx_cb(struct cw1200_common *priv, struct wsm_rx *arg, struct sk_buff **skb_p) diff --git a/drivers/staging/cw1200/txrx.h b/drivers/staging/cw1200/txrx.h index 87dbdfa949c..9f4f40ea31c 100644 --- a/drivers/staging/cw1200/txrx.h +++ b/drivers/staging/cw1200/txrx.h @@ -17,6 +17,7 @@ /* extern */ struct ieee80211_hw; /* extern */ struct sk_buff; /* extern */ struct wsm_tx; +/* extern */ struct wsm_rx; /* extern */ struct wsm_tx_confirm; /* extern */ struct cw1200_txpriv; @@ -53,19 +54,16 @@ struct tx_policy_cache { */ void tx_policy_init(struct cw1200_common *priv); void tx_policy_upload_work(struct work_struct *work); -void tx_policy_put(struct cw1200_common *priv, int idx); /* ******************************************************************** */ /* TX implementation */ u32 cw1200_rate_mask_to_wsm(struct cw1200_common *priv, u32 rates); -int cw1200_skb_to_wsm(struct cw1200_common *priv, - struct sk_buff *skb, struct wsm_tx *wsm, - struct cw1200_txpriv *txpriv); void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb); -void cw1200_notify_buffered_tx(struct cw1200_common *priv, - struct sk_buff *skb, int link_id, int tid); +void cw1200_skb_dtor(struct cw1200_common *priv, + struct sk_buff *skb, + const struct cw1200_txpriv *txpriv); /* ******************************************************************** */ /* WSM callbacks */ diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index d0bcffa1654..16996def7b2 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1399,8 +1399,6 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, * probe responses. * The easiest way to get it back is to convert * probe request into WSM start_scan command. */ - const struct cw1200_txpriv *txpriv; - int rate_id = (wsm->flags >> 4) & 0x07; struct cw1200_queue *queue = &priv->tx_queue[cw1200_queue_get_queue_id( wsm->packetID)]; @@ -1409,12 +1407,13 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, wsm_lock_tx_async(priv); BUG_ON(priv->scan.probe_skb); BUG_ON(cw1200_queue_get_skb(queue, - wsm->packetID, - &priv->scan.probe_skb, &txpriv)); - BUG_ON(cw1200_queue_remove(queue, priv, - wsm->packetID)); + wsm->packetID, + &priv->scan.probe_skb)); + skb_get(priv->scan.probe_skb); + IEEE80211_SKB_CB(priv->scan.probe_skb)->flags |= + IEEE80211_TX_STAT_ACK; + BUG_ON(cw1200_queue_remove(queue, wsm->packetID)); /* Release used TX rate policy */ - tx_policy_put(priv, rate_id); queue_delayed_work(priv->workqueue, &priv->scan.probe_work, 0); handled = true; @@ -1424,23 +1423,11 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, { /* See detailed description of "join" below. * We are dropping everything except AUTH in non-joined mode. */ - struct sk_buff *skb; - int rate_id = (wsm->flags >> 4) & 0x07; - const struct cw1200_txpriv *txpriv = NULL; struct cw1200_queue *queue = &priv->tx_queue[cw1200_queue_get_queue_id( wsm->packetID)]; - wsm_printk(KERN_DEBUG "[WSM] Drop frame (0x%.4X):" - " not joined.\n", fctl); - BUG_ON(cw1200_queue_get_skb(queue, wsm->packetID, - &skb, &txpriv)); - skb_pull(skb, sizeof(struct wsm_tx)); - cw1200_notify_buffered_tx(priv, skb, link_id, txpriv->tid); - BUG_ON(cw1200_queue_remove(queue, priv, wsm->packetID)); - /* Release used TX rate policy */ - tx_policy_put(priv, rate_id); - /* Release SKB. TODO: report TX failure. */ - dev_kfree_skb(skb); + wsm_printk(KERN_DEBUG "[WSM] Drop frame (0x%.4X).\n", fctl); + BUG_ON(cw1200_queue_remove(queue, wsm->packetID)); handled = true; } break; @@ -1626,7 +1613,6 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, if (ret) break; - if (cw1200_queue_get(queue, tx_allowed_mask, &wsm, &tx_info, &txpriv)) diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 11e18005200..f0b07dc08a2 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -753,11 +753,11 @@ typedef void (*wsm_tx_confirm_cb) (struct cw1200_common *priv, /* Note that ideology of wsm_tx struct is different against the rest of * WSM API. wsm_hdr is /not/ a caller-adapted struct to be used as an input * argument for WSM call, but a prepared bytestream to be sent to firmware. - * It is filled partly in cw1200_skb_to_wsm, partly in low-level WSM code. + * It is filled partly in cw1200_tx, partly in low-level WSM code. * Please pay attention once again: ideology is different. * * Legend: - * - [in]: cw1200_skb_to_wsm must fill this field. + * - [in]: cw1200_tx must fill this field. * - [wsm]: the field is filled by low-level WSM. */ struct wsm_tx { -- cgit v1.2.3 From 9fc4089689c8cfc1e0f1248453d6210105b100ac Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Mon, 10 Oct 2011 12:17:09 +0200 Subject: cw1200: Fix checkpatch warnings. Change-Id: Ibda6002f9ce429dabcc098bb2a88a4ca7e3cf5a4 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33543 Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/ap.c | 54 +++++++++++++++++++----------------- drivers/staging/cw1200/bh.c | 2 +- drivers/staging/cw1200/cw1200.h | 2 +- drivers/staging/cw1200/cw1200_sdio.c | 2 +- drivers/staging/cw1200/fwio.c | 4 +-- drivers/staging/cw1200/pm.c | 4 +-- drivers/staging/cw1200/sta.c | 27 +++++++++--------- drivers/staging/cw1200/txrx.c | 18 ++++++------ drivers/staging/cw1200/wsm.c | 10 +++---- 9 files changed, 64 insertions(+), 59 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 7b1ac0dfada..cd74143e47e 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -38,7 +38,7 @@ static void __cw1200_sta_notify(struct ieee80211_hw *dev, /* AP API */ int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) + struct ieee80211_sta *sta) { struct cw1200_common *priv = hw->priv; struct cw1200_sta_priv *sta_priv = @@ -67,7 +67,7 @@ int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) + struct ieee80211_sta *sta) { struct cw1200_common *priv = hw->priv; struct cw1200_sta_priv *sta_priv = @@ -101,26 +101,26 @@ static void __cw1200_sta_notify(struct ieee80211_hw *dev, u32 prev = priv->sta_asleep_mask & bit; switch (notify_cmd) { - case STA_NOTIFY_SLEEP: - if (!prev) { - if (priv->buffered_multicasts && + case STA_NOTIFY_SLEEP: + if (!prev) { + if (priv->buffered_multicasts && !priv->sta_asleep_mask) queue_work(priv->workqueue, &priv->multicast_start_work); - priv->sta_asleep_mask |= bit; - } - break; - case STA_NOTIFY_AWAKE: - if (prev) { - priv->sta_asleep_mask &= ~bit; - priv->pspoll_mask &= ~bit; - if (priv->tx_multicast && - !priv->sta_asleep_mask) - queue_work(priv->workqueue, - &priv->multicast_stop_work); - cw1200_bh_wakeup(priv); - } - break; + priv->sta_asleep_mask |= bit; + } + break; + case STA_NOTIFY_AWAKE: + if (prev) { + priv->sta_asleep_mask &= ~bit; + priv->pspoll_mask &= ~bit; + if (priv->tx_multicast && + !priv->sta_asleep_mask) + queue_work(priv->workqueue, + &priv->multicast_stop_work); + cw1200_bh_wakeup(priv); + } + break; } } @@ -172,7 +172,7 @@ static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set) skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, &tim_offset, &tim_length); if (!skb) { - if (!__cw1200_flush(priv, true)); + if (!__cw1200_flush(priv, true)) wsm_unlock_tx(priv); return -ENOENT; } @@ -295,7 +295,7 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, * In case of more IPs arp filtering will be disabled. */ if (info->arp_addr_cnt > 0 && info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) { - for(i=0; iarp_addr_cnt;i++) { + for (i = 0; i < info->arp_addr_cnt; i++) { filter.ipv4Address[i] = info->arp_addr_list[i]; ap_printk(KERN_DEBUG "[STA] addr[%d]: 0x%X\n", i, filter.ipv4Address[i]); @@ -330,7 +330,7 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, if (changed & BSS_CHANGED_BEACON_INT) { ap_printk(KERN_DEBUG "CHANGED_BEACON_INT\n"); /* Restart AP only when connected */ - if(priv->join_status == CW1200_JOIN_STATUS_AP) + if (priv->join_status == CW1200_JOIN_STATUS_AP) WARN_ON(cw1200_update_beaconing(priv)); } @@ -520,7 +520,8 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, info->cqm_rssi_hyst = 4; #endif /* 0 */ - ap_printk(KERN_DEBUG "[CQM] RSSI threshold subscribe: %d +- %d\n", + ap_printk(KERN_DEBUG "[CQM] RSSI threshold " + "subscribe: %d +- %d\n", info->cqm_rssi_thold, info->cqm_rssi_hyst); #if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) ap_printk(KERN_DEBUG "[CQM] Beacon loss subscribe: %d\n", @@ -590,8 +591,8 @@ void cw1200_multicast_start_work(struct work_struct *work) void cw1200_multicast_stop_work(struct work_struct *work) { - struct cw1200_common *priv = - container_of(work, struct cw1200_common, multicast_stop_work); + struct cw1200_common *priv = + container_of(work, struct cw1200_common, multicast_stop_work); if (priv->aid0_bit_set) { wsm_lock_tx(priv); @@ -803,7 +804,8 @@ static int cw1200_start_ap(struct cw1200_common *priv) memset(&priv->link_id_db, 0, sizeof(priv->link_id_db)); - ap_printk(KERN_DEBUG "[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s.\n", + ap_printk(KERN_DEBUG "[AP] ch: %d(%d), bcn: %d(%d), " + "brt: 0x%.8X, ssid: %.*s.\n", start.channelNumber, start.band, start.beaconInterval, start.DTIMPeriod, start.basicRateSet, diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index 80efc7dba67..978e97592ce 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -433,7 +433,7 @@ tx: u8 *data; int ret; - if (priv->device_can_sleep) { + if (priv->device_can_sleep) { ret = cw1200_device_wakeup(priv); if (WARN_ON(ret < 0)) break; diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index ae08d82fe19..239c3a9f3b9 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -233,7 +233,7 @@ struct cw1200_common { }; struct cw1200_sta_priv { - int link_id; + int link_id; }; /* interfaces for the drivers */ diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index fb594ff1567..b04f31e0684 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -286,7 +286,7 @@ static size_t cw1200_sdio_align_size(struct sbus_priv *self, size_t size) static int cw1200_sdio_pm(struct sbus_priv *self, bool suspend) { int ret; - const struct resource *irq = self->pdata->irq; + const struct resource *irq = self->pdata->irq; struct sdio_func *func = self->func; sdio_claim_host(func); diff --git a/drivers/staging/cw1200/fwio.c b/drivers/staging/cw1200/fwio.c index da6d3705e6c..72b77bc8bb0 100644 --- a/drivers/staging/cw1200/fwio.c +++ b/drivers/staging/cw1200/fwio.c @@ -438,7 +438,7 @@ int cw1200_load_firmware(struct cw1200_common *priv) ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 4, &ar2); if (ret) { cw1200_dbg(CW1200_DBG_ERROR, - "%s: (2) HW detection: can't read CUT ID.\n", + "%s: (2) HW detection: can't read CUT ID.\n", __func__); goto out; } @@ -446,7 +446,7 @@ int cw1200_load_firmware(struct cw1200_common *priv) ret = cw1200_ahb_read_32(priv, CW1200_CUT2_ID_ADDR + 8, &ar3); if (ret) { cw1200_dbg(CW1200_DBG_ERROR, - "%s: (3) HW detection: can't read CUT ID.\n", + "%s: (3) HW detection: can't read CUT ID.\n", __func__); goto out; } diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c index 3d7570767ed..7b2ba504cb2 100644 --- a/drivers/staging/cw1200/pm.c +++ b/drivers/staging/cw1200/pm.c @@ -17,7 +17,7 @@ #include "sbus.h" static int cw1200_suspend_late(struct device *dev); -static void cw1200_pm_release(struct device * dev); +static void cw1200_pm_release(struct device *dev); static int cw1200_pm_probe(struct platform_device *pdev); /* private */ @@ -29,7 +29,7 @@ struct cw1200_suspend_state { unsigned long link_id_gc; }; -static struct dev_pm_ops cw1200_pm_ops = { +static const struct dev_pm_ops cw1200_pm_ops = { .suspend_noirq = cw1200_suspend_late, }; diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 434e054672c..5dd112a97f5 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -140,8 +140,8 @@ void cw1200_stop(struct ieee80211_hw *dev) /* HACK! */ if (atomic_xchg(&priv->tx_lock, 1) != 1) - sta_printk(KERN_DEBUG "[STA] TX is force-unlocked due to stop " \ - "request.\n"); + sta_printk(KERN_DEBUG "[STA] TX is force-unlocked " + "due to stop request.\n"); wsm_unlock_tx(priv); @@ -255,7 +255,8 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) /* TODO: IEEE80211_CONF_CHANGE_QOS */ if (changed & IEEE80211_CONF_CHANGE_POWER) { priv->output_power = conf->power_level; - sta_printk(KERN_DEBUG "[STA] TX power: %d\n", priv->output_power); + sta_printk(KERN_DEBUG "[STA] TX power: %d\n", + priv->output_power); WARN_ON(wsm_set_output_power(priv, priv->output_power * 10)); } @@ -332,14 +333,14 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) modeinfo->oppPsCTWindow = conf->p2p_ps.ctwindow; switch (conf->p2p_ps.opp_ps) { - case 0: - modeinfo->oppPsCTWindow &= ~(BIT(7)); - break; - case 1: - modeinfo->oppPsCTWindow |= BIT(7); - break; - default: - break; + case 0: + modeinfo->oppPsCTWindow &= ~(BIT(7)); + break; + case 1: + modeinfo->oppPsCTWindow |= BIT(7); + break; + default: + break; } /* Notice of Absence */ @@ -348,7 +349,7 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) modeinfo->duration = __cpu_to_le32(conf->p2p_ps.duration); modeinfo->interval = __cpu_to_le32(conf->p2p_ps.interval); - if(conf->p2p_ps.count) + if (conf->p2p_ps.count) modeinfo->dtimCount = 1; else modeinfo->dtimCount = 0; @@ -358,7 +359,7 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) #if defined(CONFIG_CW1200_STA_DEBUG) print_hex_dump_bytes("p2p_ps_modeinfo: ", DUMP_PREFIX_NONE, - (u8*) modeinfo, + (u8 *)modeinfo, sizeof(*modeinfo)); #endif WARN_ON(wsm_set_p2p_ps_modeinfo(priv, modeinfo)); diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 72f5acdccdc..109ea2a0286 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -468,9 +468,8 @@ cw1200_tx_h_crypt(struct cw1200_common *priv, iv_len = t->tx_info->control.hw_key->iv_len; icv_len = t->tx_info->control.hw_key->icv_len; - if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { + if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) icv_len += 8; /* MIC */ - } if ((skb_headroom(t->skb) + skb_tailroom(t->skb) < iv_len + icv_len + WSM_TX_EXTRA_HEADROOM) || @@ -587,8 +586,7 @@ cw1200_tx_h_bt(struct cw1200_common *priv, u16 *ethertype = (u16 *) &payload[6]; if (unlikely(*ethertype == __be16_to_cpu(ETH_P_PAE))) priority = WSM_EPTA_PRIORITY_EAPOL; - } - else if (unlikely(ieee80211_is_assoc_req(t->hdr->frame_control) || + } else if (unlikely(ieee80211_is_assoc_req(t->hdr->frame_control) || ieee80211_is_reassoc_req(t->hdr->frame_control))) { struct ieee80211_mgmt *mgt_frame = (struct ieee80211_mgmt *)t->hdr; @@ -708,7 +706,8 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) if (WARN_ON(t.queue >= 4 || !t.rate)) goto drop; - if ((ret = cw1200_tx_h_calc_link_ids(priv, &t))) + ret = cw1200_tx_h_calc_link_ids(priv, &t); + if (ret) goto drop; txrx_printk(KERN_DEBUG "[TX] TX %d bytes " @@ -718,11 +717,14 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) cw1200_tx_h_pm(priv, &t); cw1200_tx_h_calc_tid(priv, &t); - if ((ret = cw1200_tx_h_crypt(priv, &t))) + ret = cw1200_tx_h_crypt(priv, &t); + if (ret) goto drop; - if ((ret = cw1200_tx_h_align(priv, &t))) + ret = cw1200_tx_h_align(priv, &t); + if (ret) goto drop; - if ((ret = cw1200_tx_h_action(priv, &t))) + ret = cw1200_tx_h_action(priv, &t); + if (ret) goto drop; wsm = cw1200_tx_h_wsm(priv, &t); if (!wsm) { diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 16996def7b2..f279b5a310d 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -422,9 +422,8 @@ static int wsm_join_confirm(struct cw1200_common *priv, struct wsm_join *arg, struct wsm_buf *buf) { - if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS)) { + if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS)) return -EINVAL; - } arg->minPowerLevel = WSM_GET32(buf); arg->maxPowerLevel = WSM_GET32(buf); @@ -1172,7 +1171,7 @@ underflow: "Firmware exception.\n"); print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE, data, len); - return -EINVAL; + return -EINVAL; } int wsm_handle_rx(struct cw1200_common *priv, int id, @@ -1297,7 +1296,8 @@ int wsm_handle_rx(struct cw1200_common *priv, int id, ret = wsm_find_complete_indication(priv, &wsm_buf); break; case 0x080C: - ret = wsm_suspend_resume_indication(priv, link_id, &wsm_buf); + ret = wsm_suspend_resume_indication(priv, + link_id, &wsm_buf); break; default: STUB(); @@ -1513,7 +1513,7 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv, struct cw1200_queue **queue_p, - u32* tx_allowed_mask_p, + u32 *tx_allowed_mask_p, bool *more) { int i; -- cgit v1.2.3 From bd401fbb9fc2867205c7acb644878697293b59f4 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Mon, 10 Oct 2011 15:14:07 +0200 Subject: cw1200: Syncing ALK and GLK tracks. Change-Id: I111ab2cb92dc5fad3eb8938ceb76b7db715b54cc Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33545 Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/main.c | 4 +++- drivers/staging/cw1200/pm.c | 18 ++++++++++-------- drivers/staging/cw1200/queue.c | 3 ++- drivers/staging/cw1200/sta.c | 4 +++- drivers/staging/cw1200/txrx.c | 7 ++++++- drivers/staging/cw1200/wsm.c | 4 ++-- 6 files changed, 26 insertions(+), 14 deletions(-) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 94398fb222f..d4c9da00214 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -264,8 +264,8 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) /* Aggregation is fully controlled by firmware. * Do not need any support from the mac80211 stack */ /* IEEE80211_HW_AMPDU_AGGREGATION | */ - IEEE80211_HW_SUPPORTS_P2P_PS | #if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + IEEE80211_HW_SUPPORTS_P2P_PS | IEEE80211_HW_SUPPORTS_CQM_BEACON_MISS | IEEE80211_HW_SUPPORTS_CQM_TX_FAIL | #endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ @@ -283,7 +283,9 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) WIPHY_WOWLAN_DISCONNECT; hw->wiphy->wowlan.n_patterns = 0; +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ hw->channel_change_time = 1000; /* TODO: find actual value */ /* priv->beacon_req_id = cpu_to_le32(0); */ diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c index 7b2ba504cb2..7c1a957de3e 100644 --- a/drivers/staging/cw1200/pm.c +++ b/drivers/staging/cw1200/pm.c @@ -79,7 +79,7 @@ static void cw1200_pm_deinit_common(struct cw1200_pm_state *pm) #ifdef CONFIG_WAKELOCK int cw1200_pm_init(struct cw1200_pm_state *pm, - struct cw1200_common *priv) + struct cw1200_common *priv) { int ret = cw1200_pm_init_common(pm, priv); if (!ret) @@ -110,14 +110,15 @@ void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, #else /* CONFIG_WAKELOCK */ -static void cw1200_pm_stay_awake_tmo(unsigned long) +static void cw1200_pm_stay_awake_tmo(unsigned long arg) { } -int cw1200_pm_init(struct cw1200_pm_state *pm) +int cw1200_pm_init(struct cw1200_pm_state *pm, + struct cw1200_common *priv) { - int ret = cw1200_pm_init_common(pm); - if (!ret) + int ret = cw1200_pm_init_common(pm, priv); + if (!ret) { init_timer(&pm->stay_awake); pm->stay_awake.data = (unsigned long)pm; pm->stay_awake.function = cw1200_pm_stay_awake_tmo; @@ -128,6 +129,7 @@ int cw1200_pm_init(struct cw1200_pm_state *pm) void cw1200_pm_deinit(struct cw1200_pm_state *pm) { del_timer_sync(&pm->stay_awake); + cw1200_pm_deinit_common(pm); } void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, @@ -199,9 +201,9 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) int ret; #ifndef CONFIG_WAKELOCK - spin_lock_bh(&pm->lock); - ret = timer_pending(&pm->stay_awake); - spin_unlock_bh(&pm->lock); + spin_lock_bh(&pm_state->lock); + ret = timer_pending(&pm_state->stay_awake); + spin_unlock_bh(&pm_state->lock); if (ret) return -EAGAIN; #endif diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c index d19f87acf4d..12152d33684 100644 --- a/drivers/staging/cw1200/queue.c +++ b/drivers/staging/cw1200/queue.c @@ -9,7 +9,8 @@ * published by the Free Software Foundation. */ -#include "net/mac80211.h" +#include +#include #include "queue.h" #include "cw1200.h" #include "debug.h" diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 5dd112a97f5..aada6e9e648 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -322,6 +322,7 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) cw1200_set_pm(priv, &priv->powersave_mode); } +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) if (changed & IEEE80211_CONF_CHANGE_P2P_PS) { struct wsm_p2p_ps_modeinfo *modeinfo; modeinfo = &priv->p2p_ps_modeinfo; @@ -361,10 +362,11 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) DUMP_PREFIX_NONE, (u8 *)modeinfo, sizeof(*modeinfo)); -#endif +#endif /* CONFIG_CW1200_STA_DEBUG */ WARN_ON(wsm_set_p2p_ps_modeinfo(priv, modeinfo)); } } +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ if (changed & IEEE80211_CONF_CHANGE_MONITOR) { /* TBD: It looks like it's transparent diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 109ea2a0286..d49f5dce09e 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -417,9 +417,11 @@ cw1200_tx_h_calc_link_ids(struct cw1200_common *priv, priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp = jiffies; +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) if (t->tx_info->control.sta && (t->tx_info->control.sta->uapsd_queues & BIT(t->queue))) t->txpriv.link_id = CW1200_LINK_ID_UAPSD; +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ return 0; } @@ -742,9 +744,11 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) } spin_unlock_bh(&priv->ps_state_lock); +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) if (tid_update) ieee80211_sta_set_buffered(t.tx_info->control.sta, t.txpriv.tid, true); +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ cw1200_bh_wakeup(priv); @@ -912,6 +916,7 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, static void cw1200_notify_buffered_tx(struct cw1200_common *priv, struct sk_buff *skb, int link_id, int tid) { +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) struct ieee80211_sta *sta; struct ieee80211_hdr *hdr; u8 *buffered; @@ -935,7 +940,7 @@ static void cw1200_notify_buffered_tx(struct cw1200_common *priv, rcu_read_unlock(); } } - +#endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ } void cw1200_skb_dtor(struct cw1200_common *priv, diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index f279b5a310d..19f87bed526 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1565,7 +1565,7 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, { struct wsm_tx *wsm = NULL; struct ieee80211_tx_info *tx_info; - struct cw1200_queue *queue; + struct cw1200_queue *queue = NULL; u32 tx_allowed_mask = 0; const struct cw1200_txpriv *txpriv = NULL; /* @@ -1577,7 +1577,7 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, int count = 0; /* More is used only for broadcasts. */ - bool more; + bool more = false; if (priv->wsm_cmd.ptr) { ++count; -- cgit v1.2.3 From fda199d9051d4ef0d4dadba34b10d93d2f8e332f Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Mon, 10 Oct 2011 23:20:13 +0200 Subject: cw1200: Requeue special frames. Some frames require special handling in wsm, for example offchannel, wep, join... Requeue for offchannel and wep frames was missing, leading to queue entry leakage and unexpected drop of frames. Fix implements requeue for special frames. Change-Id: Iba5c41496f898e30b4261db3888bba384504df50 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33596 Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/cw1200.h | 2 +- drivers/staging/cw1200/queue.c | 4 +++- drivers/staging/cw1200/queue.h | 3 ++- drivers/staging/cw1200/sta.c | 47 +++++++++++++++++++++++++++++------------ drivers/staging/cw1200/txrx.c | 3 ++- drivers/staging/cw1200/wsm.c | 25 +++++++++++----------- 6 files changed, 54 insertions(+), 30 deletions(-) diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 239c3a9f3b9..3c3a50ce318 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -178,7 +178,7 @@ struct cw1200_common { /* WSM Join */ enum cw1200_join_status join_status; u8 join_bssid[ETH_ALEN]; - const struct wsm_tx *join_pending_frame; + u32 pending_frame_id; struct work_struct join_work; struct delayed_work join_timeout; struct work_struct unjoin_work; diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c index 12152d33684..b1069255790 100644 --- a/drivers/staging/cw1200/queue.c +++ b/drivers/staging/cw1200/queue.c @@ -494,7 +494,8 @@ int cw1200_queue_remove(struct cw1200_queue *queue, u32 packetID) } int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, - struct sk_buff **skb) + struct sk_buff **skb, + const struct cw1200_txpriv **txpriv) { int ret = 0; u8 queue_generation, queue_id, item_generation, item_id; @@ -516,6 +517,7 @@ int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, ret = -ENOENT; } else { *skb = item->skb; + *txpriv = &item->txpriv; } spin_unlock_bh(&queue->lock); return ret; diff --git a/drivers/staging/cw1200/queue.h b/drivers/staging/cw1200/queue.h index bff33625c8a..2403b2519ba 100644 --- a/drivers/staging/cw1200/queue.h +++ b/drivers/staging/cw1200/queue.h @@ -92,7 +92,8 @@ int cw1200_queue_requeue_all(struct cw1200_queue *queue); int cw1200_queue_remove(struct cw1200_queue *queue, u32 packetID); int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, - struct sk_buff **skb); + struct sk_buff **skb, + const struct cw1200_txpriv **txpriv); void cw1200_queue_lock(struct cw1200_queue *queue); void cw1200_queue_unlock(struct cw1200_queue *queue); diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index aada6e9e648..e7fa4aad625 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -725,14 +725,19 @@ void cw1200_wep_key_work(struct work_struct *work) { struct cw1200_common *priv = container_of(work, struct cw1200_common, wep_key_work); + u8 queueId = cw1200_queue_get_queue_id(priv->pending_frame_id); + struct cw1200_queue *queue = &priv->tx_queue[queueId]; __le32 wep_default_key_id = __cpu_to_le32( priv->wep_default_key_id); + BUG_ON(queueId >= 4); + sta_printk(KERN_DEBUG "[STA] Setting default WEP key: %d\n", priv->wep_default_key_id); wsm_flush_tx(priv); WARN_ON(wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID, &wep_default_key_id, sizeof(wep_default_key_id))); + cw1200_queue_requeue(queue, priv->pending_frame_id); wsm_unlock_tx(priv); } @@ -1096,15 +1101,22 @@ void cw1200_offchannel_work(struct work_struct *work) { struct cw1200_common *priv = container_of(work, struct cw1200_common, offchannel_work); + u8 queueId = cw1200_queue_get_queue_id(priv->pending_frame_id); + struct cw1200_queue *queue = &priv->tx_queue[queueId]; + BUG_ON(queueId >= 4); BUG_ON(!priv->channel); mutex_lock(&priv->conf_mutex); - if (!priv->join_status) { + if (likely(!priv->join_status)) { wsm_flush_tx(priv); cw1200_update_listening(priv, true); cw1200_update_filtering(priv); } + if (unlikely(!priv->join_status)) + cw1200_queue_remove(queue, priv->pending_frame_id); + else + cw1200_queue_requeue(queue, priv->pending_frame_id); mutex_unlock(&priv->conf_mutex); wsm_unlock_tx(priv); } @@ -1113,20 +1125,31 @@ void cw1200_join_work(struct work_struct *work) { struct cw1200_common *priv = container_of(work, struct cw1200_common, join_work); - const struct wsm_tx *wsm = priv->join_pending_frame; - const struct ieee80211_hdr *frame = (struct ieee80211_hdr *)&wsm[1]; - const u8 *bssid = &frame->addr1[0]; /* AP SSID in a 802.11 frame */ + u8 queueId = cw1200_queue_get_queue_id(priv->pending_frame_id); + struct cw1200_queue *queue = &priv->tx_queue[queueId]; + const struct cw1200_txpriv *txpriv = NULL; + struct sk_buff *skb = NULL; + const struct wsm_tx *wsm; + const struct ieee80211_hdr *frame; + const u8 *bssid; struct cfg80211_bss *bss; const u8 *ssidie; const u8 *dtimie; const struct ieee80211_tim_ie *tim = NULL; - u8 queueId; + + BUG_ON(queueId >= 4); + if (cw1200_queue_get_skb(queue, priv->pending_frame_id, + &skb, &txpriv)) { + wsm_unlock_tx(priv); + return; + } + wsm = (struct wsm_tx *)&skb->data[0]; + frame = (struct ieee80211_hdr *)&skb->data[txpriv->offset]; + bssid = &frame->addr1[0]; /* AP SSID in a 802.11 frame */ BUG_ON(!wsm); BUG_ON(!priv->channel); - queueId = wsm_queue_id_to_linux(wsm->queueId); - if (unlikely(priv->join_status)) { wsm_lock_tx(priv); cw1200_unjoin_work(&priv->unjoin_work); @@ -1134,11 +1157,9 @@ void cw1200_join_work(struct work_struct *work) cancel_delayed_work_sync(&priv->join_timeout); - priv->join_pending_frame = NULL; bss = cfg80211_get_bss(priv->hw->wiphy, NULL, bssid, NULL, 0, 0, 0); if (!bss) { - cw1200_queue_remove(&priv->tx_queue[queueId], - __le32_to_cpu(wsm->packetID)); + cw1200_queue_remove(queue, priv->pending_frame_id); wsm_unlock_tx(priv); return; } @@ -1212,16 +1233,14 @@ void cw1200_join_work(struct work_struct *work) if (wsm_join(priv, &join)) { memset(&priv->join_bssid[0], 0, sizeof(priv->join_bssid)); - cw1200_queue_remove(&priv->tx_queue[queueId], - __le32_to_cpu(wsm->packetID)); + cw1200_queue_remove(queue, priv->pending_frame_id); cancel_delayed_work_sync(&priv->join_timeout); cw1200_update_listening(priv, priv->listening); } else { /* Upload keys */ WARN_ON(cw1200_upload_keys(priv)); WARN_ON(wsm_keep_alive_period(priv, 30 /* sec */)); - cw1200_queue_requeue(&priv->tx_queue[queueId], - __le32_to_cpu(wsm->packetID)); + cw1200_queue_requeue(queue, priv->pending_frame_id); priv->join_status = CW1200_JOIN_STATUS_STA; } WARN_ON(wsm_set_block_ack_policy(priv, diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index d49f5dce09e..ef5accb19a0 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -828,6 +828,7 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, u8 queue_id = cw1200_queue_get_queue_id(arg->packetID); struct cw1200_queue *queue = &priv->tx_queue[queue_id]; struct sk_buff *skb; + const struct cw1200_txpriv *txpriv; txrx_printk(KERN_DEBUG "[TX] TX confirm: %d, %d.\n", arg->status, arg->ackFailures); @@ -861,7 +862,7 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, WARN_ON(cw1200_queue_requeue(queue, arg->packetID)); } else if (!WARN_ON(cw1200_queue_get_skb( - queue, arg->packetID, &skb))) { + queue, arg->packetID, &skb, &txpriv))) { struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb); int tx_count = arg->ackFailures; u8 ht_flags = 0; diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 19f87bed526..10303fae3c8 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1313,6 +1313,7 @@ out: static bool wsm_handle_tx_data(struct cw1200_common *priv, const struct wsm_tx *wsm, const struct ieee80211_tx_info *tx_info, + struct cw1200_queue *queue, int link_id) { bool handled = false; @@ -1395,24 +1396,24 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, switch (action) { case doProbe: { + const struct cw1200_txpriv *txpriv; /* An interesting FW "feature". Device filters * probe responses. * The easiest way to get it back is to convert * probe request into WSM start_scan command. */ - struct cw1200_queue *queue = - &priv->tx_queue[cw1200_queue_get_queue_id( - wsm->packetID)]; wsm_printk(KERN_DEBUG \ "[WSM] Convert probe request to scan.\n"); wsm_lock_tx_async(priv); BUG_ON(priv->scan.probe_skb); BUG_ON(cw1200_queue_get_skb(queue, wsm->packetID, - &priv->scan.probe_skb)); + &priv->scan.probe_skb, + &txpriv)); skb_get(priv->scan.probe_skb); IEEE80211_SKB_CB(priv->scan.probe_skb)->flags |= IEEE80211_TX_STAT_ACK; - BUG_ON(cw1200_queue_remove(queue, wsm->packetID)); + BUG_ON(cw1200_queue_remove(queue, + __le32_to_cpu(wsm->packetID))); /* Release used TX rate policy */ queue_delayed_work(priv->workqueue, &priv->scan.probe_work, 0); @@ -1423,11 +1424,9 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, { /* See detailed description of "join" below. * We are dropping everything except AUTH in non-joined mode. */ - struct cw1200_queue *queue = - &priv->tx_queue[cw1200_queue_get_queue_id( - wsm->packetID)]; wsm_printk(KERN_DEBUG "[WSM] Drop frame (0x%.4X).\n", fctl); - BUG_ON(cw1200_queue_remove(queue, wsm->packetID)); + BUG_ON(cw1200_queue_remove(queue, + __le32_to_cpu(wsm->packetID))); handled = true; } break; @@ -1441,8 +1440,7 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, * not require protection */ wsm_printk(KERN_DEBUG "[WSM] Issue join command.\n"); wsm_lock_tx_async(priv); - BUG_ON(priv->join_pending_frame); - priv->join_pending_frame = wsm; + priv->pending_frame_id = __le32_to_cpu(wsm->packetID); if (queue_work(priv->workqueue, &priv->join_work) <= 0) wsm_unlock_tx(priv); handled = true; @@ -1452,6 +1450,7 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, { wsm_printk(KERN_DEBUG "[WSM] Offchannel TX request.\n"); wsm_lock_tx_async(priv); + priv->pending_frame_id = __le32_to_cpu(wsm->packetID); if (queue_work(priv->workqueue, &priv->offchannel_work) <= 0) wsm_unlock_tx(priv); handled = true; @@ -1462,6 +1461,7 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, wsm_printk(KERN_DEBUG "[WSM] Issue set_default_wep_key.\n"); wsm_lock_tx_async(priv); priv->wep_default_key_id = tx_info->control.hw_key->keyidx; + priv->pending_frame_id = __le32_to_cpu(wsm->packetID); if (queue_work(priv->workqueue, &priv->wep_key_work) <= 0) wsm_unlock_tx(priv); handled = true; @@ -1619,7 +1619,8 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, continue; if (wsm_handle_tx_data(priv, wsm, - tx_info, txpriv->raw_link_id)) + tx_info, queue, + txpriv->raw_link_id)) continue; /* Handled by WSM */ wsm->hdr.id &= __cpu_to_le16( -- cgit v1.2.3 From 67c9b8ed27974a62c371320c9f3025ca6a52e80f Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 30 Sep 2011 09:47:55 +0200 Subject: cw1200: Switch channel sync Syncing ongoing scan and switch channel request. ST-Ericsson ID: 359760 Change-Id: Ic46eff45fb01b7cb10adcc6b6fc48d2caa85be90 Signed-off-by: Janusz Dziedzic Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32672 Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33667 Tested-by: Dmitry TARNYAGIN Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/scan.c | 3 ++- drivers/staging/cw1200/sta.c | 28 ++++++++++++++++++++++------ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index 11de0db44de..568782eb0c9 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -364,11 +364,13 @@ void cw1200_probe_work(struct work_struct *work) return; } + mutex_lock(&priv->conf_mutex); if (unlikely(down_trylock(&priv->scan.lock))) { /* Scan is already in progress. Requeue self. */ schedule(); queue_delayed_work(priv->workqueue, &priv->scan.probe_work, HZ / 10); + mutex_unlock(&priv->conf_mutex); return; } @@ -403,7 +405,6 @@ void cw1200_probe_work(struct work_struct *work) } } - mutex_lock(&priv->conf_mutex); /* FW bug: driver has to restart p2p-dev mode after scan */ if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) cw1200_disable_listening(priv); diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index e7fa4aad625..daa98b7b849 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -287,17 +287,33 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) cw1200_cancel_scan(priv); sta_printk(KERN_DEBUG "[STA] Freq %d (wsm ch: %d).\n", ch->center_freq, ch->hw_value); - WARN_ON(wait_event_timeout( - priv->channel_switch_done, - !priv->channel_switch_in_progress, 3 * HZ) <= 0); ret = WARN_ON(__cw1200_flush(priv, false)); if (!ret) { + while(down_trylock(&priv->scan.lock)) { + sta_printk(KERN_DEBUG "[STA] waiting, " + "scan in progress.\n"); + msleep(100); + } + ret = WARN_ON(wsm_switch_channel(priv, &channel)); - if (!ret) - priv->channel = ch; - else + if (!ret) { + ret = wait_event_timeout( + priv->channel_switch_done, + !priv->channel_switch_in_progress, + 3 * HZ); + /* TODO: We should check also switch channel + * complete indication + */ + if (ret) { + priv->channel = ch; + ret = 0; + } else + ret = -ETIMEDOUT; + } else wsm_unlock_tx(priv); + + up(&priv->scan.lock); } } -- cgit v1.2.3 From dc6b29511dd1203df96a79d1e74098fb1c1dfa11 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 12 Oct 2011 12:01:37 +0200 Subject: cw1200: Fixing corrupted direct probe requests. Direct probe used shared SKB buffer after headers were stripped by mac80211 layer. Fix reimplements (and significantly simplifies) direct probe handling, using the same technique as for other "special" frames. Change-Id: Ibec9b72ccb497ae385d315b68001c5e23ef05701 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33775 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/scan.c | 32 ++++++++++++++++++-------------- drivers/staging/cw1200/scan.h | 1 - drivers/staging/cw1200/wsm.c | 12 +----------- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index 568782eb0c9..d97bc8182b0 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -329,11 +329,12 @@ void cw1200_probe_work(struct work_struct *work) { struct cw1200_common *priv = container_of(work, struct cw1200_common, scan.probe_work.work); - struct wsm_tx *wsm = (struct wsm_tx *) - priv->scan.probe_skb->data; + u8 queueId = cw1200_queue_get_queue_id(priv->pending_frame_id); + struct cw1200_queue *queue = &priv->tx_queue[queueId]; + const struct cw1200_txpriv *txpriv; + struct wsm_tx *wsm; struct wsm_template_frame frame = { .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, - .skb = priv->scan.probe_skb, }; struct wsm_ssid ssids[1] = {{ .length = 0, @@ -344,7 +345,6 @@ void cw1200_probe_work(struct work_struct *work) } }; struct wsm_scan scan = { .scanType = WSM_SCAN_TYPE_FOREGROUND, - .maxTransmitRate = wsm->maxTxRate, .numOfProbeRequests = 1, .probeDelay = 0, .numOfChannels = 1, @@ -357,12 +357,8 @@ void cw1200_probe_work(struct work_struct *work) wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe work.\n"); - if (!priv->channel) { - dev_kfree_skb(priv->scan.probe_skb); - priv->scan.probe_skb = NULL; - wsm_unlock_tx(priv); - return; - } + BUG_ON(queueId >= 4); + BUG_ON(!priv->channel); mutex_lock(&priv->conf_mutex); if (unlikely(down_trylock(&priv->scan.lock))) { @@ -374,13 +370,20 @@ void cw1200_probe_work(struct work_struct *work) return; } + if (cw1200_queue_get_skb(queue, priv->pending_frame_id, + &frame.skb, &txpriv)) { + wsm_unlock_tx(priv); + return; + } + wsm = (struct wsm_tx *)frame.skb->data; + scan.maxTransmitRate = wsm->maxTxRate; scan.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; if (priv->join_status == CW1200_JOIN_STATUS_STA) scan.scanType = WSM_SCAN_TYPE_BACKGROUND; ch[0].number = priv->channel->hw_value; - skb_pull(frame.skb, sizeof(struct wsm_tx)); + skb_pull(frame.skb, txpriv->offset); ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)]; ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr); @@ -416,9 +419,10 @@ void cw1200_probe_work(struct work_struct *work) } mutex_unlock(&priv->conf_mutex); - /* TODO: Report TX status to ieee80211 layer */ - dev_kfree_skb(priv->scan.probe_skb); - priv->scan.probe_skb = NULL; + skb_push(frame.skb, txpriv->offset); + if (!ret) + IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK; + BUG_ON(cw1200_queue_remove(queue, priv->pending_frame_id)); if (ret) { priv->scan.direct_probe = 0; diff --git a/drivers/staging/cw1200/scan.h b/drivers/staging/cw1200/scan.h index f2ea372aad7..fd59123e742 100644 --- a/drivers/staging/cw1200/scan.h +++ b/drivers/staging/cw1200/scan.h @@ -38,7 +38,6 @@ struct cw1200_scan { atomic_t in_progress; /* Direct probe requests workaround */ struct delayed_work probe_work; - struct sk_buff *probe_skb; int direct_probe; }; diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 10303fae3c8..bcda31364b1 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1404,17 +1404,7 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, wsm_printk(KERN_DEBUG \ "[WSM] Convert probe request to scan.\n"); wsm_lock_tx_async(priv); - BUG_ON(priv->scan.probe_skb); - BUG_ON(cw1200_queue_get_skb(queue, - wsm->packetID, - &priv->scan.probe_skb, - &txpriv)); - skb_get(priv->scan.probe_skb); - IEEE80211_SKB_CB(priv->scan.probe_skb)->flags |= - IEEE80211_TX_STAT_ACK; - BUG_ON(cw1200_queue_remove(queue, - __le32_to_cpu(wsm->packetID))); - /* Release used TX rate policy */ + priv->pending_frame_id = __le32_to_cpu(wsm->packetID); queue_delayed_work(priv->workqueue, &priv->scan.probe_work, 0); handled = true; -- cgit v1.2.3 From 50efd61a75f1ef58920bd268be3088acee9b3259 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 13 Oct 2011 18:11:22 +0200 Subject: u8500: Adding clock control callback into platform data. cw1200 device has hardware clock control. However on some platforms it is required to explicitly enable clock for the device. The patch adds a clock control callback into the platform data. Change-Id: I5fd619a34a4dbeff8f1b25d55bcca9e912020c23 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34069 Reviewed-by: Bartosz MARKOWSKI Tested-by: Robert MARKLUND Reviewed-by: Philippe LANGLAIS --- arch/arm/mach-ux500/include/mach/cw1200_plat.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-ux500/include/mach/cw1200_plat.h b/arch/arm/mach-ux500/include/mach/cw1200_plat.h index e79794f42a3..4d30dbd3979 100644 --- a/arch/arm/mach-ux500/include/mach/cw1200_plat.h +++ b/arch/arm/mach-ux500/include/mach/cw1200_plat.h @@ -16,6 +16,8 @@ struct cw1200_platform_data { const struct resource *reset; int (*power_ctrl)(const struct cw1200_platform_data *pdata, bool enable); + int (*clk_ctrl)(const struct cw1200_platform_data *pdata, + bool enable); }; /* Declaration only. Should be implemented in arch/xxx/mach-yyy */ -- cgit v1.2.3 From a80224363f173184215042498ad32f6419cbd25d Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 13 Oct 2011 18:02:04 +0200 Subject: cw1200: Enable clock at startup. cw1200 device has hardware clock control. However on some platforms it is required to explicitly enable clock for the device. This is something platform-specific and should be handled in the platform code. The patch adds support for enabling clock by a callback in the platform data. ST-Ericsson ID: 361990 Change-Id: Ifdc7a00b28e3864922b3bcdfab09cbd3092e91ea Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34068 Reviewed-by: Bartosz MARKOWSKI Tested-by: Robert MARKLUND Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/cw1200_plat.h | 2 ++ drivers/staging/cw1200/cw1200_sdio.c | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/staging/cw1200/cw1200_plat.h b/drivers/staging/cw1200/cw1200_plat.h index e79794f42a3..4d30dbd3979 100644 --- a/drivers/staging/cw1200/cw1200_plat.h +++ b/drivers/staging/cw1200/cw1200_plat.h @@ -16,6 +16,8 @@ struct cw1200_platform_data { const struct resource *reset; int (*power_ctrl)(const struct cw1200_platform_data *pdata, bool enable); + int (*clk_ctrl)(const struct cw1200_platform_data *pdata, + bool enable); }; /* Declaration only. Should be implemented in arch/xxx/mach-yyy */ diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index b04f31e0684..cd432655bff 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -388,6 +388,12 @@ static int __init cw1200_sdio_init(void) if (ret) goto err_reg; + if (pdata->clk_ctrl) { + ret = pdata->clk_ctrl(pdata, true); + if (ret) + goto err_clk; + } + if (pdata->power_ctrl) { ret = pdata->power_ctrl(pdata, true); if (ret) @@ -404,6 +410,9 @@ err_on: if (pdata->power_ctrl) pdata->power_ctrl(pdata, false); err_power: + if (pdata->clk_ctrl) + pdata->clk_ctrl(pdata, false); +err_clk: sdio_unregister_driver(&sdio_driver); err_reg: return ret; @@ -418,6 +427,8 @@ static void __exit cw1200_sdio_exit(void) cw1200_sdio_off(pdata); if (pdata->power_ctrl) pdata->power_ctrl(pdata, false); + if (pdata->clk_ctrl) + pdata->clk_ctrl(pdata, false); } -- cgit v1.2.3 From b93081d5df652dcadc409e0a558bee9862eab964 Mon Sep 17 00:00:00 2001 From: Robert Marklund Date: Thu, 13 Oct 2011 12:49:14 +0200 Subject: cw1200: Re add snowball support Re add power ctrl and snowball support in the cw1200 driver Change-Id: I6a6b0279a05a419e62c0259d217361e43c855665 Signed-off-by: Robert Marklund Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34064 Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Philippe LANGLAIS --- arch/arm/mach-ux500/board-mop500-wlan.c | 47 +++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-wlan.c b/arch/arm/mach-ux500/board-mop500-wlan.c index e525d4fe8aa..ec53fca5f8d 100644 --- a/arch/arm/mach-ux500/board-mop500-wlan.c +++ b/arch/arm/mach-ux500/board-mop500-wlan.c @@ -16,6 +16,9 @@ #include static void cw1200_release(struct device *dev); +static int cw1200_power_ctrl(const struct cw1200_platform_data *pdata, + bool enable); + static struct resource cw1200_href_resources[] = { { @@ -92,11 +95,45 @@ static int cw1200_pins_enable(bool enable) return ret; } +static int cw1200_power_ctrl(const struct cw1200_platform_data *pdata, + bool enable) +{ + static const char *vdd_name = "vdd"; + struct regulator *vdd; + int ret = 0; + + vdd = regulator_get(&cw1200_device.dev, vdd_name); + if (IS_ERR(vdd)) { + ret = PTR_ERR(vdd); + dev_warn(&cw1200_device.dev, + "%s: Failed to get regulator '%s': %d\n", + __func__, vdd_name, ret); + } else { + if (enable) + ret = regulator_enable(vdd); + else + ret = regulator_disable(vdd); + + if (ret) { + dev_warn(&cw1200_device.dev, + "%s: Failed to %s regulator '%s': %d\n", + __func__, enable ? "enable" : "disable", + vdd_name, ret); + } + regulator_put(vdd); + } + return ret; +} + + + int __init mop500_wlan_init(void) { int ret; - if (machine_is_u8500() || machine_is_nomadik()) { + if (machine_is_u8500() || + machine_is_nomadik() || + machine_is_snowball()) { cw1200_device.num_resources = ARRAY_SIZE(cw1200_href_resources); cw1200_device.resource = cw1200_href_resources; } else if (machine_is_hrefv60()) { @@ -111,13 +148,19 @@ int __init mop500_wlan_init(void) return -ENOTSUPP; } - cw1200_platform_data.mmc_id = "mmc3"; + if (machine_is_snowball()) + cw1200_platform_data.mmc_id = "mmc2"; + else + cw1200_platform_data.mmc_id = "mmc3"; cw1200_platform_data.reset = &cw1200_device.resource[0]; cw1200_platform_data.irq = &cw1200_device.resource[1]; cw1200_device.dev.release = cw1200_release; + if (machine_is_snowball()) + cw1200_platform_data.power_ctrl = cw1200_power_ctrl; + ret = cw1200_pins_enable(true); if (WARN_ON(ret)) return ret; -- cgit v1.2.3 From 9a140bee4c2705a5e4a4b9c26b276c1423e97d13 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Fri, 14 Oct 2011 08:55:48 +0200 Subject: ux500: enable WLAN clock by request Change-Id: Icd8433b49939613801c40e50138e15a4b49869d9 Signed-off-by: Dmitry Tarnyagin Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34070 Tested-by: Robert MARKLUND Reviewed-by: Philippe LANGLAIS --- arch/arm/mach-ux500/board-mop500-wlan.c | 41 ++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-wlan.c b/arch/arm/mach-ux500/board-mop500-wlan.c index ec53fca5f8d..84c1b8958fa 100644 --- a/arch/arm/mach-ux500/board-mop500-wlan.c +++ b/arch/arm/mach-ux500/board-mop500-wlan.c @@ -12,14 +12,17 @@ #include #include #include -#include "pins.h" +#include #include +#include "pins.h" + static void cw1200_release(struct device *dev); static int cw1200_power_ctrl(const struct cw1200_platform_data *pdata, + bool enable); +static int cw1200_clk_ctrl(const struct cw1200_platform_data *pdata, bool enable); - static struct resource cw1200_href_resources[] = { { .start = 215, @@ -50,7 +53,9 @@ static struct resource cw1200_href60_resources[] = { }, }; -static struct cw1200_platform_data cw1200_platform_data = { 0 }; +static struct cw1200_platform_data cw1200_platform_data = { + .clk_ctrl = cw1200_clk_ctrl, +}; static struct platform_device cw1200_device = { .name = "cw1200_wlan", @@ -125,7 +130,37 @@ static int cw1200_power_ctrl(const struct cw1200_platform_data *pdata, return ret; } +static int cw1200_clk_ctrl(const struct cw1200_platform_data *pdata, + bool enable) +{ + static const char *clock_name = "sys_clk_out"; + struct clk *clk_dev; + int ret = 0; + + clk_dev = clk_get(&cw1200_device.dev, clock_name); + if (IS_ERR(clk_dev)) { + ret = PTR_ERR(clk_dev); + dev_warn(&cw1200_device.dev, + "%s: Failed to get clk '%s': %d\n", + __func__, clock_name, ret); + + } else { + + if (enable) + ret = clk_enable(clk_dev); + else + clk_disable(clk_dev); + + if (ret) { + dev_warn(&cw1200_device.dev, + "%s: Failed to %s clk enable: %d\n", + __func__, clock_name, ret); + } + } + + return ret; +} int __init mop500_wlan_init(void) { -- cgit v1.2.3 From 420f5927a398bb98783decfe989eac9d5cf466f7 Mon Sep 17 00:00:00 2001 From: Amit Shakya Date: Fri, 14 Oct 2011 21:22:35 +0530 Subject: cw1200: Fix for WPA2 security not working The WPA2 security was not working because the local variable which keep track of the 802.11 header was not getting updated correctly in case of security related updations in the skb data field. Change-Id: I55a1aecee615843cf89eecb667597479f88e9756 Signed-off-by: Amit Shakya Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34132 Reviewed-by: Robert MARKLUND Reviewed-by: Dmitry TARNYAGIN Tested-by: Dmitry TARNYAGIN Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/txrx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index ef5accb19a0..a9177271d01 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -500,6 +500,7 @@ cw1200_tx_h_crypt(struct cw1200_common *priv, newhdr = skb_push(t->skb, iv_len); memmove(newhdr, newhdr + iv_len, t->hdrlen); + t->hdr = (struct ieee80211_hdr *) newhdr; t->hdrlen += iv_len; icv = skb_put(t->skb, icv_len); -- cgit v1.2.3 From 1c8ef08150ba6192f9a165bf30d29e0d6c0d0aa2 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 26 Oct 2011 13:41:05 +0200 Subject: cw1200: Fix destroy of undefined eeprom_mutex Signed-off-by: Philippe Langlais --- drivers/staging/cw1200/main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index d4c9da00214..3af5d1c21f2 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -432,7 +432,6 @@ void cw1200_unregister_common(struct ieee80211_hw *dev) #endif /* CONFIG_CW1200_LEDS */ mutex_destroy(&priv->conf_mutex); - mutex_destroy(&priv->eeprom_mutex); wsm_buf_deinit(&priv->wsm_cmd_buf); -- cgit v1.2.3 From ea6c14664084389ffbf4fd9f166bd018044dbd9f Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 21 Oct 2011 10:36:46 +0200 Subject: staging: Add STM CW1200 WLAN driver compilation Signed-off-by: Philippe Langlais --- drivers/staging/Kconfig | 2 ++ drivers/staging/Makefile | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 97d412d9145..2f2faac27e6 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -132,4 +132,6 @@ source "drivers/staging/ramster/Kconfig" source "drivers/staging/ozwpan/Kconfig" +source "drivers/staging/cw1200/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index ffe7d44374e..d2c1cdd29d4 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -57,3 +57,4 @@ obj-$(CONFIG_ANDROID) += android/ obj-$(CONFIG_PHONE) += telephony/ obj-$(CONFIG_RAMSTER) += ramster/ obj-$(CONFIG_USB_WPAN_HCD) += ozwpan/ +obj-$(CONFIG_CW1200) += cw1200/ -- cgit v1.2.3 From ae68329dd26193218c46571d86a91fffea717da4 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 2 Dec 2011 14:30:40 +0100 Subject: wlan: Include module.h after 3.2 update Signed-off-by: Philippe Langlais --- arch/arm/mach-ux500/board-mop500-wlan.c | 1 + drivers/staging/cw1200/debug.c | 1 + drivers/staging/cw1200/main.c | 1 + 3 files changed, 3 insertions(+) diff --git a/arch/arm/mach-ux500/board-mop500-wlan.c b/arch/arm/mach-ux500/board-mop500-wlan.c index 84c1b8958fa..52cd23b2852 100644 --- a/arch/arm/mach-ux500/board-mop500-wlan.c +++ b/arch/arm/mach-ux500/board-mop500-wlan.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 91c690ad4e6..926f7b99faa 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -10,6 +10,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include "cw1200.h" diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 3af5d1c21f2..ffc43592141 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -20,6 +20,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include -- cgit v1.2.3 From f8de7ace1f962ec815f24569746f6aa65ef7a4b2 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Mon, 5 Dec 2011 11:47:18 +0100 Subject: cw1200: Fix API change introduced in 3.2 Signed-off-by: Philippe Langlais --- drivers/staging/cw1200/sta.c | 4 ++-- drivers/staging/cw1200/sta.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index daa98b7b849..7acc38a4944 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -523,8 +523,8 @@ void cw1200_configure_filter(struct ieee80211_hw *dev, up(&priv->scan.lock); } -int cw1200_conf_tx(struct ieee80211_hw *dev, u16 queue, - const struct ieee80211_tx_queue_params *params) +int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params) { struct cw1200_common *priv = dev->priv; int ret = 0; diff --git a/drivers/staging/cw1200/sta.h b/drivers/staging/cw1200/sta.h index 88a08aaceaf..0d0098e7346 100644 --- a/drivers/staging/cw1200/sta.h +++ b/drivers/staging/cw1200/sta.h @@ -26,8 +26,8 @@ void cw1200_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, u64 multicast); -int cw1200_conf_tx(struct ieee80211_hw *dev, u16 queue, - const struct ieee80211_tx_queue_params *params); +int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params); int cw1200_get_stats(struct ieee80211_hw *dev, struct ieee80211_low_level_stats *stats); /* Not more a part of interface? -- cgit v1.2.3 From 9e3cdf2a27ebe893f839b968ff34083ab0d777b1 Mon Sep 17 00:00:00 2001 From: Rejane Durand Date: Tue, 18 Oct 2011 13:37:05 +0200 Subject: [U9500] Correct the GPIO configuration for WLAN There are two parts in this correction. Firstly, as there is much in common between a U9500 platform and a U8500 one, all the tests performed to identify a U9500 platform shall be done before the ones related to U8500. Otherwise, the platform will be considered as a U8500 one and the GPIOs will be badly configured. Secondly, the mapping of WLAN_IRQ has to be changed. On U8500, WLAN_IRQ is mapped onto GPIO04. On U9500, it is GPIO144 that shall be used. ST-Ericsson ID: 368270 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I5880f617c65f7cf4ca9eeabf22a53f7a77c20133 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34320 Reviewed-by: Rejane DURAND Tested-by: Rejane DURAND Reviewed-by: QATOOLS Reviewed-by: Baptiste CHAULOUX Tested-by: Baptiste CHAULOUX Reviewed-by: Christophe GUIBOUT Reviewed-by: Jonas ABERG --- arch/arm/mach-ux500/board-mop500-wlan.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-wlan.c b/arch/arm/mach-ux500/board-mop500-wlan.c index 52cd23b2852..46037ca76ae 100644 --- a/arch/arm/mach-ux500/board-mop500-wlan.c +++ b/arch/arm/mach-ux500/board-mop500-wlan.c @@ -54,6 +54,21 @@ static struct resource cw1200_href60_resources[] = { }, }; +static struct resource cw1200_u9500_resources[] = { + { + .start = 85, + .end = 85, + .flags = IORESOURCE_IO, + .name = "cw1200_reset", + }, + { + .start = NOMADIK_GPIO_TO_IRQ(144), + .end = NOMADIK_GPIO_TO_IRQ(144), + .flags = IORESOURCE_IRQ, + .name = "cw1200_irq", + }, +}; + static struct cw1200_platform_data cw1200_platform_data = { .clk_ctrl = cw1200_clk_ctrl, }; @@ -167,9 +182,10 @@ int __init mop500_wlan_init(void) { int ret; - if (machine_is_u8500() || - machine_is_nomadik() || - machine_is_snowball()) { + if (pins_for_u9500()) { + cw1200_device.num_resources = ARRAY_SIZE(cw1200_u9500_resources); + cw1200_device.resource = cw1200_u9500_resources; + } else if (machine_is_u8500() || machine_is_nomadik() || machine_is_snowball()) { cw1200_device.num_resources = ARRAY_SIZE(cw1200_href_resources); cw1200_device.resource = cw1200_href_resources; } else if (machine_is_hrefv60()) { -- cgit v1.2.3 From 5b3b5e0cc12f6ad8d7a4e3d96f29b4c06dc66a7e Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Tue, 6 Dec 2011 12:24:18 +0100 Subject: WLAN: Update Kconfig for CW1200 Add CW1200_U5500_SUPPORT ST-Ericsson Linux next: NA ST-Ericsson ID: 375493 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I017fb5a600fe8609fc61a0618a3934d54e65f1d6 Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/41173 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Srinidhi KASAGAR --- drivers/staging/cw1200/Kconfig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/staging/cw1200/Kconfig b/drivers/staging/cw1200/Kconfig index 946d366cbfb..8a536acc7d9 100644 --- a/drivers/staging/cw1200/Kconfig +++ b/drivers/staging/cw1200/Kconfig @@ -42,6 +42,13 @@ config CW1200_WAPI_SUPPORT Say Y if your compat-wireless support WAPI. If unsure, say N. +config CW1200_U5500_SUPPORT + bool "Enable U5500 support" + depends on CW1200 + help + Say Y if you want to enable wlan on u5500 platform support. + If unsure, say N. + menu "Driver debug features" depends on CW1200 -- cgit v1.2.3 From 96d472636ea3216270ec94f6a0c5c896dfcf8002 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Tue, 6 Dec 2011 12:46:29 +0100 Subject: u5500: cw1200: Remove requesting SDIO pins The SDIO pins belongs to the SDIO driver and should be handled by the SDIO driver only. The reason that it has worked up until now is that the SDIO driver has just started using pm runtime. Since both WLAN and SDIO framework enable the pins, the number of users is set to two. SDIO tries to release the pins (after 50 ms of inactivity in order to save power), the usage count does not go down to 0 and therefore the pins are not reconfigured to GPIOs, which they must be when PL18X is not driving the pins due to power down). ST-Ericsson ID: 375493 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: NA Change-Id: Ibe3dfd32cb16a9d629a8c405e5723aec787a5ae6 Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/41183 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/board-u5500-wlan.c | 43 ++-------------------------------- 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/arch/arm/mach-ux500/board-u5500-wlan.c b/arch/arm/mach-ux500/board-u5500-wlan.c index 8459ebd6aac..3683f3ad8bb 100644 --- a/arch/arm/mach-ux500/board-u5500-wlan.c +++ b/arch/arm/mach-ux500/board-u5500-wlan.c @@ -12,7 +12,6 @@ #include #include #include -#include #include "pins.h" #include @@ -44,38 +43,8 @@ const struct cw1200_platform_data *cw1200_u5500_get_platform_data(void) } EXPORT_SYMBOL_GPL(cw1200_u5500_get_platform_data); -static int cw1200_pins_enable(bool enable) -{ - struct ux500_pins *pins = NULL; - int ret = 0; - - pins = ux500_pins_get("sdi3"); - - if (!pins) { - printk(KERN_ERR "cw1200: Pins are not found. " - "Check platform data.\n"); - return -ENOENT; - } - - if (enable) - ret = ux500_pins_enable(pins); - else - ret = ux500_pins_disable(pins); - - if (ret) - printk(KERN_ERR "cw1200: Pins can not be %s: %d.\n", - enable ? "enabled" : "disabled", - ret); - - ux500_pins_put(pins); - - return ret; -} - int __init u5500_wlan_init(void) { - int ret; - if (machine_is_u5500()) { cw1200_device.num_resources = ARRAY_SIZE(cw1200_u5500_resources); cw1200_device.resource = cw1200_u5500_resources; @@ -92,18 +61,10 @@ int __init u5500_wlan_init(void) cw1200_device.dev.release = cw1200_release; - ret = cw1200_pins_enable(true); - if (WARN_ON(ret)) - return ret; - - ret = platform_device_register(&cw1200_device); - if (ret) - cw1200_pins_enable(false); - - return ret; + return platform_device_register(&cw1200_device); } static void cw1200_release(struct device *dev) { - cw1200_pins_enable(false); + } -- cgit v1.2.3 From c81d6835ff70b5f4132d1cef32329e4f0ecde220 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 7 Dec 2011 11:36:21 +0100 Subject: u5500: WLAN: Adds prcmu_ctrl callback Adds prcmu_ctrl callback to u5500 WLAN platform data file. In u5500 platform cw1200 reset pin is connected to PRCMU. prcmu_resetout is used to set/unset RESOUT2_N_PIN by writing to PRCMU register (platform callback) ST-Ericsson Linux next: NA ST-Ericsson ID: 375493 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Iedfa62dcec0b0bb9bc67abb6ff8dc00b1b78f375 Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/41359 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/board-u5500-wlan.c | 21 ++++++++++++++++++++- arch/arm/mach-ux500/include/mach/cw1200_plat.h | 2 ++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/board-u5500-wlan.c b/arch/arm/mach-ux500/board-u5500-wlan.c index 3683f3ad8bb..fd64089108a 100644 --- a/arch/arm/mach-ux500/board-u5500-wlan.c +++ b/arch/arm/mach-ux500/board-u5500-wlan.c @@ -10,12 +10,16 @@ #include #include #include +#include #include #include #include "pins.h" #include + static void cw1200_release(struct device *dev); +static int cw1200_prcmu_ctrl(const struct cw1200_platform_data *pdata, + bool enable); static struct resource cw1200_u5500_resources[] = { { @@ -26,7 +30,9 @@ static struct resource cw1200_u5500_resources[] = { }, }; -static struct cw1200_platform_data cw1200_u5500_platform_data = { 0 }; +static struct cw1200_platform_data cw1200_u5500_platform_data = { + .prcmu_ctrl = cw1200_prcmu_ctrl, +}; static struct platform_device cw1200_device = { .name = "cw1200_wlan", @@ -43,6 +49,19 @@ const struct cw1200_platform_data *cw1200_u5500_get_platform_data(void) } EXPORT_SYMBOL_GPL(cw1200_u5500_get_platform_data); +static int cw1200_prcmu_ctrl(const struct cw1200_platform_data *pdata, + bool enable) +{ + int ret; + + if (enable) + ret = prcmu_resetout(2, 1); + else + ret = prcmu_resetout(2, 0); + + return ret; +} + int __init u5500_wlan_init(void) { if (machine_is_u5500()) { diff --git a/arch/arm/mach-ux500/include/mach/cw1200_plat.h b/arch/arm/mach-ux500/include/mach/cw1200_plat.h index 4d30dbd3979..3a73183c9f8 100644 --- a/arch/arm/mach-ux500/include/mach/cw1200_plat.h +++ b/arch/arm/mach-ux500/include/mach/cw1200_plat.h @@ -18,6 +18,8 @@ struct cw1200_platform_data { bool enable); int (*clk_ctrl)(const struct cw1200_platform_data *pdata, bool enable); + int (*prcmu_ctrl)(const struct cw1200_platform_data *pdata, + bool enable); }; /* Declaration only. Should be implemented in arch/xxx/mach-yyy */ -- cgit v1.2.3 From d875b4fc6fe56c9acef2d51dea3e6c6cac0dfe9e Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:14:08 +0100 Subject: cw1200: Disable BSSID filtering in monitor mode. Firmware requires that BSSID filtering must be always disabled in monitor mode. ST-Ericsson ID: 366402 Change-Id: I8677197d2a661dcc9b97d73cd798ebbbe32cfce7 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33937 Reviewed-by: QABUILD Reviewed-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/sta.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 7acc38a4944..3e81513b681 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -433,9 +433,12 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) void cw1200_update_filtering(struct cw1200_common *priv) { int ret; + bool bssid_filtering = !priv->rx_filter.bssid; if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) return; + else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) + bssid_filtering = false; ret = wsm_set_rx_filter(priv, &priv->rx_filter); if (!ret) @@ -443,7 +446,7 @@ void cw1200_update_filtering(struct cw1200_common *priv) if (!ret) ret = wsm_beacon_filter_control(priv, &priv->bf_control); if (!ret) - ret = wsm_set_bssid_filtering(priv, !priv->rx_filter.bssid); + ret = wsm_set_bssid_filtering(priv, bssid_filtering); if (!ret) ret = wsm_set_multicast_filter(priv, &priv->multicast_filter); if (ret) -- cgit v1.2.3 From b4ed1b3e271d89a8fb40e384df873abcf0871d7b Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Wed, 29 Feb 2012 15:14:09 +0100 Subject: cw1200: scan, probe_req rates check no_cck flag Check no_cck flag in hw_scan callback, and use correct rates for scan request. Similar in case of probe request frame. Set correct rate for beacon/probe_res frames. Signed-off-by: Janusz Dziedzic Change-Id: I83216968e9394a17f6294923d209d2f6876c12b7 ST-Ericsson ID: 360139 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33399 Reviewed-by: QABUILD Reviewed-by: Janusz DZIEDZIC Tested-by: Janusz DZIEDZIC Reviewed-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/ap.c | 2 ++ drivers/staging/cw1200/scan.c | 7 +++++-- drivers/staging/cw1200/sta.c | 5 ++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index cd74143e47e..4f558506d12 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -683,6 +683,8 @@ static int cw1200_upload_beacon(struct cw1200_common *priv) .frame_type = WSM_FRAME_TYPE_BEACON, }; + if (priv->vif->p2p) + frame.rate = WSM_TRANSMIT_RATE_6; frame.skb = ieee80211_beacon_get(priv->hw, priv->vif); if (WARN_ON(!frame.skb)) diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index d97bc8182b0..cb63376649e 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -199,8 +199,11 @@ void cw1200_scan_work(struct work_struct *work) break; } scan.band = first->band; - /* TODO: Is it optimal? */ - scan.maxTransmitRate = WSM_TRANSMIT_RATE_1; + + if (priv->scan.req->no_cck) + scan.maxTransmitRate = WSM_TRANSMIT_RATE_6; + else + scan.maxTransmitRate = WSM_TRANSMIT_RATE_1; /* TODO: Is it optimal? */ scan.numOfProbeRequests = (first->flags & IEEE80211_CHAN_PASSIVE_SCAN) ? 0 : 2; diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 3e81513b681..e5ecde6216e 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1239,8 +1239,11 @@ void cw1200_join_work(struct work_struct *work) memcpy(&join.ssid[0], &ssidie[2], join.ssidLength); } - if (priv->vif->p2p) + if (priv->vif->p2p) { join.flags |= WSM_JOIN_FLAGS_P2P_GO; + join.basicRateSet = + cw1200_rate_mask_to_wsm(priv, 0xFF0); + } wsm_flush_tx(priv); -- cgit v1.2.3 From 82669a6f865087397aa8c538f60aeed5218826e5 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 29 Feb 2012 15:14:10 +0100 Subject: cw1200: Update license information Adds license comments for cw1200 driver ST-Ericsson ID: 368236 ST-Ericsson FOSS-OUT ID: NA Change-Id: Ibe9683cb1e093dba6fe480ffeacb2c955fa43c04 Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34306 --- drivers/staging/cw1200/debug.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/staging/cw1200/debug.h b/drivers/staging/cw1200/debug.h index 6f7d8acab00..e6812dfc41e 100644 --- a/drivers/staging/cw1200/debug.h +++ b/drivers/staging/cw1200/debug.h @@ -1,3 +1,14 @@ +/* + * DebugFS code for ST-Ericsson CW1200 mac80211 driver + * + * Copyright (c) 2011, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + #ifndef CW1200_DEBUG_H_INCLUDED #define CW1200_DEBUG_H_INCLUDED -- cgit v1.2.3 From 2f60d34aa23fc9dd795b0a6df18bd3879b6154b5 Mon Sep 17 00:00:00 2001 From: Marek Kwaczynski Date: Wed, 29 Feb 2012 15:14:11 +0100 Subject: cw1200: Remove unnecessary condition in scan function When length of ssid is zero all networks are scanned. It is needed to display all networks in scan results when ssid of network was set before. ST-Ericsson ID: 365688 ST-Ericsson FOSS-OUT ID: NA Change-Id: I8e025d2e0f89cdd353b9cc2ac3eecbb399103588 Signed-off-by: Marek Kwaczynski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34420 Reviewed-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/scan.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index cb63376649e..391fab8c2dc 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -107,15 +107,13 @@ int cw1200_hw_scan(struct ieee80211_hw *hw, priv->scan.output_power = priv->output_power; for (i = 0; i < req->n_ssids; ++i) { - if (req->ssids[i].ssid_len) { - struct wsm_ssid *dst = - &priv->scan.ssids[priv->scan.n_ssids]; - BUG_ON(req->ssids[i].ssid_len > sizeof(dst->ssid)); - memcpy(&dst->ssid[0], req->ssids[i].ssid, - sizeof(dst->ssid)); - dst->length = req->ssids[i].ssid_len; - ++priv->scan.n_ssids; - } + struct wsm_ssid *dst = + &priv->scan.ssids[priv->scan.n_ssids]; + BUG_ON(req->ssids[i].ssid_len > sizeof(dst->ssid)); + memcpy(&dst->ssid[0], req->ssids[i].ssid, + sizeof(dst->ssid)); + dst->length = req->ssids[i].ssid_len; + ++priv->scan.n_ssids; } mutex_unlock(&priv->conf_mutex); -- cgit v1.2.3 From bd4fb12f5688a2991d78208dda9826aeba1769be Mon Sep 17 00:00:00 2001 From: "Ajitpal.Singh" Date: Wed, 29 Feb 2012 15:14:12 +0100 Subject: cw1200: Set better block size for SDIO Changing default SDIO block size used to 512. Helps in less overhead on SDIO bus. ST-Ericsson ID: 357764 ST-Ericsson FOSS-OUT ID: NA Change-Id: I6cd1384ab5cff308f533b85b50d3396396cc4f3a Signed-off-by: Ajitpal.Singh Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32913 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/bh.c | 2 -- drivers/staging/cw1200/bh.h | 4 +--- drivers/staging/cw1200/cw1200_sdio.c | 12 ++++++------ drivers/staging/cw1200/main.c | 4 ++++ drivers/staging/cw1200/sbus.h | 1 + 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index 978e97592ce..ff6b85edfb7 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -346,8 +346,6 @@ rx: * to the NEXT Message length + 2 Bytes for SKB */ read_len = read_len + 2; - BUG_ON(SDIO_BLOCK_SIZE & (SDIO_BLOCK_SIZE - 1)); - #if defined(CONFIG_CW1200_NON_POWER_OF_TWO_BLOCKSIZES) alloc_len = priv->sbus_ops->align_size( priv->sbus_priv, read_len); diff --git a/drivers/staging/cw1200/bh.h b/drivers/staging/cw1200/bh.h index 6d4d27b18c5..ea4598afc43 100644 --- a/drivers/staging/cw1200/bh.h +++ b/drivers/staging/cw1200/bh.h @@ -14,9 +14,7 @@ /* extern */ struct cw1200_common; -/* TODO: 512, actually. Was increased to 1024 - * for compatibility with particular FW. */ -#define SDIO_BLOCK_SIZE (1024) +#define SDIO_BLOCK_SIZE (512) int cw1200_register_bh(struct cw1200_common *priv); void cw1200_unregister_bh(struct cw1200_common *priv); diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index cd432655bff..dffa8879b93 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -274,15 +274,14 @@ static int cw1200_sdio_reset(struct sbus_priv *self) static size_t cw1200_sdio_align_size(struct sbus_priv *self, size_t size) { size_t aligned = sdio_align_size(self->func, size); - /* HACK!!! Problems with DMA size on u8500 platform */ - if ((aligned & 0x1F) && (aligned & ~0x1F)) { - aligned &= ~0x1F; - aligned += 0x20; - } - return aligned; } +int cw1200_sdio_set_block_size(struct sbus_priv *self, size_t size) +{ + return sdio_set_block_size(self->func, size); +} + static int cw1200_sdio_pm(struct sbus_priv *self, bool suspend) { int ret; @@ -312,6 +311,7 @@ static struct sbus_ops cw1200_sdio_sbus_ops = { .reset = cw1200_sdio_reset, .align_size = cw1200_sdio_align_size, .power_mgmt = cw1200_sdio_pm, + .set_block_size = cw1200_sdio_set_block_size, }; /* Probe Function to be called by SDIO stack when device is discovered */ diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index ffc43592141..e57519aa3fb 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -498,6 +498,10 @@ int cw1200_core_probe(const struct sbus_ops *sbus_ops, err = cw1200_load_firmware(priv); if (err) goto err2; + priv->sbus_ops->lock(priv->sbus_priv); + WARN_ON(priv->sbus_ops->set_block_size(priv->sbus_priv, + SDIO_BLOCK_SIZE)); + priv->sbus_ops->unlock(priv->sbus_priv); if (wait_event_interruptible_timeout(priv->wsm_startup_done, priv->wsm_caps.firmwareReady, 3*HZ) <= 0) { diff --git a/drivers/staging/cw1200/sbus.h b/drivers/staging/cw1200/sbus.h index a911ef1a0e6..49bd06d20e5 100644 --- a/drivers/staging/cw1200/sbus.h +++ b/drivers/staging/cw1200/sbus.h @@ -33,6 +33,7 @@ struct sbus_ops { int (*reset)(struct sbus_priv *self); size_t (*align_size)(struct sbus_priv *self, size_t size); int (*power_mgmt)(struct sbus_priv *self, bool suspend); + int (*set_block_size)(struct sbus_priv *self, size_t size); }; #endif /* CW1200_SBUS_H */ -- cgit v1.2.3 From 7dafd98f73a6a55a257305eeacfc8c65b085bdbe Mon Sep 17 00:00:00 2001 From: "Ajitpal.Singh" Date: Wed, 29 Feb 2012 15:14:13 +0100 Subject: cw1200: Remove unnecessary reads of CONTROL reg cw1200 device implements a SDIO retry mechanism which requires the HOST to do a read/write to any device reg after reading the QUEUE reg. Removed reading of CONTROL register after every RX/TX and replaced it with a read of 1 byte of the CONFIG reg when no RX/TX is pending in the cw1200_bh() ST-Ericsson ID: 357764 ST-Ericsson FOSS-OUT ID: NA Signed-off-by: Ajitpal.Singh Change-Id: Ib700807853883800fb2649e3b430c43c14e1e6d8 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32007 Reviewed-by: Bartosz MARKOWSKI Reviewed-by: QABUILD Reviewed-by: QATEST Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/bh.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index ff6b85edfb7..acc16459807 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -262,6 +262,7 @@ static int cw1200_bh(void *arg) int tx_allowed; int pending_tx = 0; long status; + u8 dummy; for (;;) { if (!priv->hw_bufs_used @@ -274,6 +275,12 @@ static int cw1200_bh(void *arg) else status = MAX_SCHEDULE_TIMEOUT; + /* Dummy Read for SDIO retry mechanism*/ + if (((atomic_read(&priv->bh_rx) == 0) && + (atomic_read(&priv->bh_tx) == 0))) + cw1200_reg_read(priv, ST90TDS_CONFIG_REG_ID, + &dummy, sizeof(dummy)); + status = wait_event_interruptible_timeout(priv->bh_wq, ({ rx = atomic_xchg(&priv->bh_rx, 0); tx = atomic_xchg(&priv->bh_tx, 0); @@ -499,14 +506,6 @@ tx: } } - /* HACK!!! Device tends not to send interrupt - * if this extra check is missing */ - if (!(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)) { - if (WARN_ON(cw1200_bh_read_ctrl_reg( - priv, &ctrl_reg))) - break; - } - if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) goto rx; } -- cgit v1.2.3 From f8078997500faad868cdd6374abfdb0f17f34026 Mon Sep 17 00:00:00 2001 From: "Ajitpal.Singh" Date: Wed, 29 Feb 2012 15:14:14 +0100 Subject: cw1200: Retry sdio read/write in case of error SDIO read/write API sometimes returns error due to signal integrity issues on the SDIO bus. In such cases, sometimes retrying the operation instead of exiting can help in recovery. ST-Ericsson ID: 357764, 364168 ST-Ericsson FOSS-OUT ID: NA Signed-off-by: Ajitpal.Singh Change-Id: Iede20c48447467a27ee7f9a49578ef7709cdd83f Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33205 Reviewed-by: QABUILD Reviewed-by: QATEST Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/hwio.c | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/drivers/staging/cw1200/hwio.c b/drivers/staging/cw1200/hwio.c index 094ce8234f0..b544a0a4a80 100644 --- a/drivers/staging/cw1200/hwio.c +++ b/drivers/staging/cw1200/hwio.c @@ -27,6 +27,7 @@ | (((mpf) & 1) << 6) \ | (((rfu) & 1) << 5) \ | (((reg_id_ofs) & 0x1F) << 0)) +#define MAX_RETRY 3 static int __cw1200_reg_read(struct cw1200_common *priv, u16 addr, @@ -113,16 +114,25 @@ int cw1200_reg_write(struct cw1200_common *priv, u16 addr, const void *buf, int cw1200_data_read(struct cw1200_common *priv, void *buf, size_t buf_len) { - int ret; + int ret, retry = 1; BUG_ON(!priv->sbus_ops); priv->sbus_ops->lock(priv->sbus_priv); { int buf_id_rx = priv->buf_id_rx; - ret = __cw1200_reg_read(priv, ST90TDS_IN_OUT_QUEUE_REG_ID, buf, - buf_len, buf_id_rx + 1); - if (!ret) { - buf_id_rx = (buf_id_rx + 1) & 3; - priv->buf_id_rx = buf_id_rx; + while (retry <= MAX_RETRY) { + ret = __cw1200_reg_read(priv, + ST90TDS_IN_OUT_QUEUE_REG_ID, buf, + buf_len, buf_id_rx + 1); + if (!ret) { + buf_id_rx = (buf_id_rx + 1) & 3; + priv->buf_id_rx = buf_id_rx; + break; + } else { + retry++; + mdelay(1); + cw1200_dbg(CW1200_DBG_ERROR, "%s,error :[%d]\n", + __func__, ret); + } } } priv->sbus_ops->unlock(priv->sbus_priv); @@ -132,16 +142,25 @@ int cw1200_data_read(struct cw1200_common *priv, void *buf, size_t buf_len) int cw1200_data_write(struct cw1200_common *priv, const void *buf, size_t buf_len) { - int ret; + int ret, retry = 1; BUG_ON(!priv->sbus_ops); priv->sbus_ops->lock(priv->sbus_priv); { int buf_id_tx = priv->buf_id_tx; - ret = __cw1200_reg_write(priv, ST90TDS_IN_OUT_QUEUE_REG_ID, buf, - buf_len, buf_id_tx); - if (!ret) { - buf_id_tx = (buf_id_tx + 1) & 31; - priv->buf_id_tx = buf_id_tx; + while (retry <= MAX_RETRY) { + ret = __cw1200_reg_write(priv, + ST90TDS_IN_OUT_QUEUE_REG_ID, buf, + buf_len, buf_id_tx); + if (!ret) { + buf_id_tx = (buf_id_tx + 1) & 31; + priv->buf_id_tx = buf_id_tx; + break; + } else { + retry++; + mdelay(1); + cw1200_dbg(CW1200_DBG_ERROR, "%s,error :[%d]\n", + __func__, ret); + } } } priv->sbus_ops->unlock(priv->sbus_priv); -- cgit v1.2.3 From 976bfd8cb9b4c0fdcdabd561009a1469c836dcb5 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Wed, 29 Feb 2012 15:14:16 +0100 Subject: cw1200: Enable probe_req forwarding In case we became P2P_GO we should enable probe_req forwarding to upper layer. Next wpa_supplicant will handle this request. Need to be integrated together with firmware: WSM_A30.01.0338 ST-Ericsson ID: 365887 ST-Ericsson FOSS-OUT ID: NA Change-Id: I623c1b092ecbda814ff22d48269e1dbb50094d98 Signed-off-by: Marek Puzyniak Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34006 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/ap.c | 2 ++ drivers/staging/cw1200/wsm.h | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 4f558506d12..d3c75275fae 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -698,6 +698,8 @@ static int cw1200_upload_beacon(struct cw1200_common *priv) __cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE; + if (priv->vif->p2p) + frame.disable = true; ret = wsm_set_template_frame(priv, &frame); } dev_kfree_skb(frame.skb); diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index f0b07dc08a2..133de01cb96 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -1393,6 +1393,7 @@ static inline int wsm_set_operational_mode(struct cw1200_common *priv, struct wsm_template_frame { u8 frame_type; u8 rate; + bool disable; struct sk_buff *skb; }; @@ -1403,7 +1404,10 @@ static inline int wsm_set_template_frame(struct cw1200_common *priv, u8 *p = skb_push(arg->skb, 4); p[0] = arg->frame_type; p[1] = arg->rate; - ((u16 *) p)[1] = __cpu_to_le16(arg->skb->len - 4); + if (arg->disable) + ((u16 *) p)[1] = 0; + else + ((u16 *) p)[1] = __cpu_to_le16(arg->skb->len - 4); ret = wsm_write_mib(priv, WSM_MIB_ID_TEMPLATE_FRAME, p, arg->skb->len); skb_pull(arg->skb, 4); return ret; -- cgit v1.2.3 From a039f907402bc75e10335444a9dbdc39e2d68bbd Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 29 Feb 2012 15:14:17 +0100 Subject: cw1200: Add 2byte hole in TXreq for unaligned buf Adds a 2 bytes hole in the WSM Transmit request when skb->data is aligned at 2 bytes.This will make the transmit request 4byte aligned. This optmisation is need for DMA. The firmware is informed about the hole by setting BIT7 in the WSM Transmit Request flags. ST-Ericsson ID: 357764 ST-Ericsson FOSS-OUT ID: NA Signed-off-by: Ajitpal.Singh Change-Id: I7bb45b01476a51052e5166feac5d05677bb56eed Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/35047 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/debug.c | 4 ++-- drivers/staging/cw1200/debug.h | 8 ++++---- drivers/staging/cw1200/txrx.c | 23 ++++++++++++++++++----- drivers/staging/cw1200/wsm.c | 15 +++++++-------- drivers/staging/cw1200/wsm.h | 3 +++ 5 files changed, 34 insertions(+), 19 deletions(-) diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 926f7b99faa..50941846e28 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -289,8 +289,8 @@ static int cw1200_status_show(struct seq_file *seq, void *v) d->rx_agg); seq_printf(seq, "TX miss: %d\n", d->tx_cache_miss); - seq_printf(seq, "TX copy: %d\n", - d->tx_copy); + seq_printf(seq, "TX align: %d\n", + d->tx_align); seq_printf(seq, "TX TTL: %d\n", d->tx_ttl); seq_printf(seq, "Scan: %s\n", diff --git a/drivers/staging/cw1200/debug.h b/drivers/staging/cw1200/debug.h index e6812dfc41e..96b11feab1c 100644 --- a/drivers/staging/cw1200/debug.h +++ b/drivers/staging/cw1200/debug.h @@ -25,7 +25,7 @@ struct cw1200_debug_priv { int tx_multi; int tx_multi_frames; int tx_cache_miss; - int tx_copy; + int tx_align; int tx_ttl; }; @@ -64,9 +64,9 @@ static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv) ++priv->debug->tx_cache_miss; } -static inline void cw1200_debug_tx_copy(struct cw1200_common *priv) +static inline void cw1200_debug_tx_align(struct cw1200_common *priv) { - ++priv->debug->tx_copy; + ++priv->debug->tx_align; } static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv) @@ -110,7 +110,7 @@ static inline void cw1200_debug_tx_cache_miss(struct cw1200_common *priv) { } -static inline void cw1200_debug_tx_copy(struct cw1200_common *priv) +static inline void cw1200_debug_tx_align(struct cw1200_common *priv) { } diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index a9177271d01..582b255a460 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -509,7 +509,8 @@ cw1200_tx_h_crypt(struct cw1200_common *priv, static int cw1200_tx_h_align(struct cw1200_common *priv, - struct cw1200_txinfo *t) + struct cw1200_txinfo *t, + u8 *flags) { size_t offset = (size_t)t->skb->data & 3; u8 *p; @@ -517,6 +518,14 @@ cw1200_tx_h_align(struct cw1200_common *priv, if (!offset) return 0; + if (offset & 1) { + wiphy_err(priv->hw->wiphy, + "Bug: attempt to transmit a frame " + "with wrong alignment: %d\n", + offset); + return -EINVAL; + } + if (skb_headroom(t->skb) < offset) { wiphy_err(priv->hw->wiphy, "Bug: no space allocated " @@ -526,9 +535,11 @@ cw1200_tx_h_align(struct cw1200_common *priv, return -ENOMEM; } p = skb_push(t->skb, offset); - memmove(p, &p[offset], t->skb->len - offset); - skb_trim(t->skb, t->skb->len - offset); - cw1200_debug_tx_copy(priv); + t->hdr = (struct ieee80211_hdr *) p; + t->hdrlen += offset; + t->txpriv.offset += offset; + *flags |= WSM_TX_2BYTES_SHIFT; + cw1200_debug_tx_align(priv); return 0; } @@ -697,6 +708,7 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) }; struct wsm_tx *wsm; bool tid_update = 0; + u8 flags = 0; int ret; t.rate = cw1200_get_tx_rate(priv, @@ -723,7 +735,7 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) ret = cw1200_tx_h_crypt(priv, &t); if (ret) goto drop; - ret = cw1200_tx_h_align(priv, &t); + ret = cw1200_tx_h_align(priv, &t, &flags); if (ret) goto drop; ret = cw1200_tx_h_action(priv, &t); @@ -734,6 +746,7 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) ret = -ENOMEM; goto drop; } + wsm->flags |= flags; cw1200_tx_h_bt(priv, &t, wsm); cw1200_tx_h_rate_policy(priv, &t, wsm); diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index bcda31364b1..a8c49494e49 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1313,12 +1313,12 @@ out: static bool wsm_handle_tx_data(struct cw1200_common *priv, const struct wsm_tx *wsm, const struct ieee80211_tx_info *tx_info, - struct cw1200_queue *queue, - int link_id) + const struct cw1200_txpriv *txpriv, + struct cw1200_queue *queue) { bool handled = false; const struct ieee80211_hdr *frame = - (struct ieee80211_hdr *)&wsm[1]; + (struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset]; __le16 fctl = frame->frame_control; enum { doProbe, @@ -1349,7 +1349,7 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, case NL80211_IFTYPE_AP: if (unlikely(!priv->join_status)) action = doDrop; - else if (unlikely(!(BIT(link_id) & + else if (unlikely(!(BIT(txpriv->raw_link_id) & (BIT(0) | priv->link_id_map)))) { wiphy_warn(priv->hw->wiphy, "A frame with expired link id " @@ -1396,7 +1396,6 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, switch (action) { case doProbe: { - const struct cw1200_txpriv *txpriv; /* An interesting FW "feature". Device filters * probe responses. * The easiest way to get it back is to convert @@ -1609,8 +1608,7 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, continue; if (wsm_handle_tx_data(priv, wsm, - tx_info, queue, - txpriv->raw_link_id)) + tx_info, txpriv, queue)) continue; /* Handled by WSM */ wsm->hdr.id &= __cpu_to_le16( @@ -1625,7 +1623,8 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, if (more) { struct ieee80211_hdr *hdr = - (struct ieee80211_hdr *) &wsm[1]; + (struct ieee80211_hdr *) + &((u8 *)wsm)[txpriv->offset]; /* more buffered multicast/broadcast frames * ==> set MoreData flag in IEEE 802.11 header * to inform PS STAs */ diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 133de01cb96..bcccd790332 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -284,6 +284,9 @@ struct cw1200_common; /* Macro to fetch encryption key index. */ #define WSM_RX_STATUS_KEY_IDX(status) (((status >> 20)) & 0x0F) +/* Frame Control field starts at Frame offset + 2 */ +#define WSM_TX_2BYTES_SHIFT (BIT(7)) + /* Join mode */ /* IBSS */ #define WSM_JOIN_MODE_IBSS (0) -- cgit v1.2.3 From 2e697e81b52318fa1ad6ce45b2ed19b335fdedff Mon Sep 17 00:00:00 2001 From: "Ajitpal.Singh" Date: Wed, 29 Feb 2012 15:14:18 +0100 Subject: cw1200: Fix pointer cast in cw1200_tx_h_align Fixes wrong casting of cw1200_txinfo.hdr in cw1200_tx_h_align Signed-off-by: Ajitpal.Singh Change-Id: I5dd2b718be87fa2b12b6951e0b649646d43868a4 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/35048 Reviewed-by: Dmitry TARNYAGIN Reviewed-by: QABUILD Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/txrx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 582b255a460..f2f4ed014de 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -513,7 +513,6 @@ cw1200_tx_h_align(struct cw1200_common *priv, u8 *flags) { size_t offset = (size_t)t->skb->data & 3; - u8 *p; if (!offset) return 0; @@ -534,8 +533,7 @@ cw1200_tx_h_align(struct cw1200_common *priv, skb_headroom(t->skb)); return -ENOMEM; } - p = skb_push(t->skb, offset); - t->hdr = (struct ieee80211_hdr *) p; + skb_push(t->skb, offset); t->hdrlen += offset; t->txpriv.offset += offset; *flags |= WSM_TX_2BYTES_SHIFT; -- cgit v1.2.3 From 17e450dac2fa80cd664ea96e3709b3191ddad63c Mon Sep 17 00:00:00 2001 From: Piotr Nakraszewicz Date: Wed, 29 Feb 2012 15:14:19 +0100 Subject: cw1200: Change kalloc parameter to GFP_ATOMIC In function cw1200_queue_register_post_gc change kalloc parameter from (GFP_KERNEL | GFP_ATOMIC) to GFP_ATOMIC to prevent a BUG: sleeping function called from invalid context ST-Ericsson ID: 368724 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I6d6c9978596a41c165415886175eb0eac17eba5e Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34770 Reviewed-by: QABUILD Reviewed-by: QATOOLS Reviewed-by: Szymon BIGOS Reviewed-by: Dmitry TARNYAGIN Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c index b1069255790..9a9fd2871e4 100644 --- a/drivers/staging/cw1200/queue.c +++ b/drivers/staging/cw1200/queue.c @@ -85,7 +85,7 @@ static void cw1200_queue_register_post_gc(struct list_head *gc_list, { struct cw1200_queue_item *gc_item; gc_item = kmalloc(sizeof(struct cw1200_queue_item), - GFP_KERNEL | GFP_ATOMIC); + GFP_ATOMIC); BUG_ON(!gc_item); memcpy(gc_item, item, sizeof(struct cw1200_queue_item)); list_move_tail(&gc_item->head, gc_list); -- cgit v1.2.3 From 99d16921d04f0a53996e6f1afbd78762064e801f Mon Sep 17 00:00:00 2001 From: Lukasz Kucharczyk Date: Wed, 29 Feb 2012 15:14:20 +0100 Subject: cw1200: reset firmware after deauthentication. Firmware needs to be reseted after sending deuathentication frame in AP mode in order to assure proper operation. Without reset, some frames are dropped by firmware afterwards. ST-Ericsson ID: 360490 ST-Ericsson FOSS-OUT ID: NA Change-Id: I202ffb99f9c047444ec18a3df30907672e566fe0 Signed-off-by: Lukasz Kucharczyk Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/35064 Reviewed-by: Dmitry TARNYAGIN Reviewed-by: QATOOLS Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/ap.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index d3c75275fae..d1dbcb759d1 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -79,13 +79,13 @@ int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, entry = &priv->link_id_db[sta_priv->link_id - 1]; spin_lock_bh(&priv->ps_state_lock); - entry->status = CW1200_LINK_SOFT; + entry->status = CW1200_LINK_RESERVE; entry->timestamp = jiffies; - if (!delayed_work_pending(&priv->link_id_gc_work)) - queue_delayed_work(priv->workqueue, - &priv->link_id_gc_work, - CW1200_LINK_ID_GC_TIMEOUT); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(priv); spin_unlock_bh(&priv->ps_state_lock); + flush_workqueue(priv->workqueue); return 0; } -- cgit v1.2.3 From 8702004a664d4fd01d3626b59717a2a0198cdeee Mon Sep 17 00:00:00 2001 From: Lukasz KUCHARCZYK Date: Wed, 29 Feb 2012 15:14:22 +0100 Subject: cw1200: fix WMM AC parameters forwarding to WSM. Pass the WMM AC / WMM ACI parameters to WSM in a correct order. ST-Ericsson ID: 355241, 355096, 355166, 355221, 355070 ST-Ericsson FOSS-OUT ID: NA Signed-off-by: Lukasz Kucharczyk Signed-off-by: Piotr Nakraszewicz Change-Id: I23497fad48fda42bd731490aaac505f666a166a2 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/35063 Reviewed-by: Piotr NAKRASZEWICZ Reviewed-by: Bartosz MARKOWSKI Reviewed-by: Lukasz KUCHARCZYK Tested-by: Lukasz KUCHARCZYK --- drivers/staging/cw1200/cw1200.h | 1 + drivers/staging/cw1200/sta.c | 27 ++++++++++++++++++++------- drivers/staging/cw1200/wsm.c | 30 ++++++++++++++---------------- drivers/staging/cw1200/wsm.h | 30 +++++++++++++++++++++--------- 4 files changed, 56 insertions(+), 32 deletions(-) diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 3c3a50ce318..1852c734ce5 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -111,6 +111,7 @@ struct cw1200_common { struct ieee80211_channel *channel; u8 bssid[ETH_ALEN]; struct wsm_edca_params edca; + struct wsm_tx_queue_params tx_queue_params; struct wsm_association_mode association_mode; struct wsm_set_bss_params bss_params; struct cw1200_ht_info ht_info; diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index e5ecde6216e..6c216917d44 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -49,11 +49,11 @@ int cw1200_start(struct ieee80211_hw *dev) mutex_lock(&priv->conf_mutex); - /* default ECDA */ - WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, false); - WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, false); - WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, false); - WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, false); + /* default EDCA */ + WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, 0xc8, false); + WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, 0xc8, false); + WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, 0xc8, false); + WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, 0xc8, false); ret = wsm_set_edca_params(priv, &priv->edca); if (WARN_ON(ret)) goto out; @@ -539,12 +539,24 @@ int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, if (queue < dev->queues) { old_uapsdFlags = priv->uapsd_info.uapsdFlags; + WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0); + ret = wsm_set_tx_queue_params(priv, + &priv->tx_queue_params.params[queue], queue); + if (ret) { + ret = -EINVAL; + goto out; + } + WSM_EDCA_SET(&priv->edca, queue, params->aifs, - params->cw_min, params->cw_max, params->txop, + params->cw_min, params->cw_max, params->txop, 0xc8, params->uapsd); ret = wsm_set_edca_params(priv, &priv->edca); + if (ret) { + ret = -EINVAL; + goto out; + } - if (!ret && (priv->mode == NL80211_IFTYPE_STATION)) { + if (priv->mode == NL80211_IFTYPE_STATION) { ret = cw1200_set_uapsd_param(priv, &priv->edca); if (!ret && priv->setbssparams_done && (priv->join_status == CW1200_JOIN_STATUS_STA) && @@ -554,6 +566,7 @@ int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, } else ret = -EINVAL; +out: mutex_unlock(&priv->conf_mutex); return ret; } diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index a8c49494e49..add382d78ee 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -536,15 +536,15 @@ nomem: /* ******************************************************************** */ int wsm_set_tx_queue_params(struct cw1200_common *priv, - const struct wsm_set_tx_queue_params *arg) + const struct wsm_set_tx_queue_params *arg, u8 id) { int ret; struct wsm_buf *buf = &priv->wsm_cmd_buf; + u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1}; wsm_cmd_lock(priv); - /* TODO: verify me. */ - WSM_PUT8(buf, arg->queueId); + WSM_PUT8(buf, queue_id_to_wmm_aci[id]); WSM_PUT8(buf, 0); WSM_PUT8(buf, arg->ackPolicy); WSM_PUT8(buf, 0); @@ -572,34 +572,32 @@ int wsm_set_edca_params(struct cw1200_common *priv, wsm_cmd_lock(priv); - /* TODO: verify me. */ - /* Implemented according to specification. Note that there is a - * mismatch in BK and BE mapping. */ + /* Implemented according to specification. */ + WSM_PUT16(buf, arg->params[3].cwMin); + WSM_PUT16(buf, arg->params[2].cwMin); WSM_PUT16(buf, arg->params[1].cwMin); WSM_PUT16(buf, arg->params[0].cwMin); - WSM_PUT16(buf, arg->params[2].cwMin); - WSM_PUT16(buf, arg->params[3].cwMin); + WSM_PUT16(buf, arg->params[3].cwMax); + WSM_PUT16(buf, arg->params[2].cwMax); WSM_PUT16(buf, arg->params[1].cwMax); WSM_PUT16(buf, arg->params[0].cwMax); - WSM_PUT16(buf, arg->params[2].cwMax); - WSM_PUT16(buf, arg->params[3].cwMax); + WSM_PUT8(buf, arg->params[3].aifns); + WSM_PUT8(buf, arg->params[2].aifns); WSM_PUT8(buf, arg->params[1].aifns); WSM_PUT8(buf, arg->params[0].aifns); - WSM_PUT8(buf, arg->params[2].aifns); - WSM_PUT8(buf, arg->params[3].aifns); + WSM_PUT16(buf, arg->params[3].txOpLimit); + WSM_PUT16(buf, arg->params[2].txOpLimit); WSM_PUT16(buf, arg->params[1].txOpLimit); WSM_PUT16(buf, arg->params[0].txOpLimit); - WSM_PUT16(buf, arg->params[2].txOpLimit); - WSM_PUT16(buf, arg->params[3].txOpLimit); + WSM_PUT32(buf, arg->params[3].maxReceiveLifetime); + WSM_PUT32(buf, arg->params[2].maxReceiveLifetime); WSM_PUT32(buf, arg->params[1].maxReceiveLifetime); WSM_PUT32(buf, arg->params[0].maxReceiveLifetime); - WSM_PUT32(buf, arg->params[2].maxReceiveLifetime); - WSM_PUT32(buf, arg->params[3].maxReceiveLifetime); ret = wsm_cmd_send(priv, buf, NULL, 0x0013, WSM_CMD_TIMEOUT); wsm_cmd_unlock(priv); diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index bcccd790332..635c9c7fca1 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -1035,12 +1035,6 @@ int wsm_remove_key(struct cw1200_common *priv, /* 3.34 */ struct wsm_set_tx_queue_params { - /* 0 best effort/legacy */ - /* 1 background */ - /* 2 video */ - /* 3 voice */ - u8 queueId; - /* WSM_ACK_POLICY_... */ u8 ackPolicy; @@ -1053,8 +1047,23 @@ struct wsm_set_tx_queue_params { u32 maxTransmitLifetime; }; +struct wsm_tx_queue_params { + /* NOTE: index is a linux queue id. */ + struct wsm_set_tx_queue_params params[4]; +}; + + +#define WSM_TX_QUEUE_SET(queue_params, queue, ack_policy, allowed_time,\ + max_life_time) \ +do { \ + struct wsm_set_tx_queue_params *p = &(queue_params)->params[queue]; \ + p->ackPolicy = (ack_policy); \ + p->allowedMediumTime = (allowed_time); \ + p->maxTransmitLifetime = (max_life_time); \ +} while (0) + int wsm_set_tx_queue_params(struct cw1200_common *priv, - const struct wsm_set_tx_queue_params *arg); + const struct wsm_set_tx_queue_params *arg, u8 id); /* 3.36 */ struct wsm_edca_queue_params { @@ -1084,13 +1093,16 @@ struct wsm_edca_params { struct wsm_edca_queue_params params[4]; }; -#define WSM_EDCA_SET(edca, queue, aifs, cw_min, cw_max, txop, uapsd) \ +#define TXOP_UNIT 32 +#define WSM_EDCA_SET(edca, queue, aifs, cw_min, cw_max, txop, life_time,\ + uapsd) \ do { \ struct wsm_edca_queue_params *p = &(edca)->params[queue]; \ p->cwMin = (cw_min); \ p->cwMax = (cw_max); \ p->aifns = (aifs); \ - p->txOpLimit = (txop); \ + p->txOpLimit = ((txop) * TXOP_UNIT); \ + p->maxReceiveLifetime = (life_time); \ p->uapsdEnable = (uapsd); \ } while (0) -- cgit v1.2.3 From 00ec8f311ab77b5caf470e034cda26aea50f4b6d Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 29 Feb 2012 15:14:23 +0100 Subject: cw1200: Force background scan Set 'forced background scan' bit in scan flags. If station cannot enter PS mode, it will be forced to perform background scan and the same send probe requests. ST-Ericsson ID: 361264 Change-Id: I186a09958934b8c1286748a373a0ec067fc00bc5 Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/35197 Reviewed-by: QABUILD --- drivers/staging/cw1200/scan.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index 391fab8c2dc..e939e62bc95 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -213,8 +213,10 @@ void cw1200_scan_work(struct work_struct *work) /* It is not stated in WSM specification, however * FW team says that driver may not use FG scan * when joined. */ - if (priv->join_status == CW1200_JOIN_STATUS_STA) + if (priv->join_status == CW1200_JOIN_STATUS_STA) { scan.scanType = WSM_SCAN_TYPE_BACKGROUND; + scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND; + } scan.ch = kzalloc( sizeof(struct wsm_scan_ch[it - priv->scan.curr]), GFP_KERNEL); @@ -380,8 +382,10 @@ void cw1200_probe_work(struct work_struct *work) scan.maxTransmitRate = wsm->maxTxRate; scan.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; - if (priv->join_status == CW1200_JOIN_STATUS_STA) + if (priv->join_status == CW1200_JOIN_STATUS_STA) { scan.scanType = WSM_SCAN_TYPE_BACKGROUND; + scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND; + } ch[0].number = priv->channel->hw_value; skb_pull(frame.skb, txpriv->offset); -- cgit v1.2.3 From 552a5a4e166be53e5e786e0a1bfe0c0d81c4a01e Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 29 Feb 2012 15:14:24 +0100 Subject: cw1200: Disable AP-keep-alive before reset Disabling keep-alive right before reset request to firmware and reenable it after successfull association. ST-Ericsson ID: 361264 Change-Id: Ib6f47d89de69ef3bdb01761aa8bcb9fe58d8b3df Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36718 Reviewed-by: QABUILD --- drivers/staging/cw1200/ap.c | 1 + drivers/staging/cw1200/sta.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index d1dbcb759d1..f09d9031d0f 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -452,6 +452,7 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, priv->association_mode.basicRateSet); WARN_ON(wsm_set_association_mode(priv, &priv->association_mode)); + WARN_ON(wsm_keep_alive_period(priv, 30 /* sec */)); WARN_ON(wsm_set_bss_params(priv, &priv->bss_params)); priv->setbssparams_done = true; WARN_ON(wsm_set_beacon_wakeup_period(priv, diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 6c216917d44..822f6a4d54e 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1274,7 +1274,6 @@ void cw1200_join_work(struct work_struct *work) } else { /* Upload keys */ WARN_ON(cw1200_upload_keys(priv)); - WARN_ON(wsm_keep_alive_period(priv, 30 /* sec */)); cw1200_queue_requeue(queue, priv->pending_frame_id); priv->join_status = CW1200_JOIN_STATUS_STA; } @@ -1333,6 +1332,7 @@ void cw1200_unjoin_work(struct work_struct *work) /* Unjoin is a reset. */ wsm_flush_tx(priv); + WARN_ON(wsm_keep_alive_period(priv, 0)); WARN_ON(wsm_reset(priv, &reset)); priv->join_dtim_period = 0; WARN_ON(cw1200_setup_mac(priv)); -- cgit v1.2.3 From 30302840de6c28d9d8a8187915da005bc3f3008c Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 29 Feb 2012 15:14:25 +0100 Subject: cw1200: Set PM mode only if we are associated Set_PM_Mode request should be called only if we are associated. AID is necessary for PS mode. ST-Ericsson ID: 361264 Change-Id: I880a1009b20c304709dbe9601c90f4bc308f10ab Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36719 Reviewed-by: QABUILD --- drivers/staging/cw1200/sta.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 822f6a4d54e..58774b926bf 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -334,7 +334,7 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) priv->powersave_mode.fastPsmIdlePeriod = conf->dynamic_ps_timeout << 1; - if (priv->join_status == CW1200_JOIN_STATUS_STA) + if (priv->join_status == CW1200_JOIN_STATUS_STA && priv->bss_params.aid) cw1200_set_pm(priv, &priv->powersave_mode); } @@ -1344,7 +1344,9 @@ void cw1200_unjoin_work(struct work_struct *work) priv->ba_tid_mask, priv->ba_tid_mask)); cw1200_update_filtering(priv); priv->setbssparams_done = false; - + memset(&priv->association_mode, 0, + sizeof(priv->association_mode)); + memset(&priv->bss_params, 0, sizeof(priv->bss_params)); sta_printk(KERN_DEBUG "[STA] Unjoin.\n"); } mutex_unlock(&priv->conf_mutex); -- cgit v1.2.3 From d23d0b764a2081af18cd9be4b35626fdd310582c Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 29 Feb 2012 15:14:26 +0100 Subject: cw1200: Synchronize PS and scan requestes New PS settings shall be applied after scan finish, to prevent overlapping request to firmware ST-Ericsson ID: 361264 Change-Id: I6d48d570b02256cd77c6ef58fe197355b17890e1 Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36720 Reviewed-by: QABUILD --- drivers/staging/cw1200/sta.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 58774b926bf..570d4c6bd6f 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -334,8 +334,12 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) priv->powersave_mode.fastPsmIdlePeriod = conf->dynamic_ps_timeout << 1; - if (priv->join_status == CW1200_JOIN_STATUS_STA && priv->bss_params.aid) + if (priv->join_status == CW1200_JOIN_STATUS_STA && priv->bss_params.aid) { + while (down_trylock(&priv->scan.lock)) + msleep(100); cw1200_set_pm(priv, &priv->powersave_mode); + up(&priv->scan.lock); + } } #if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) -- cgit v1.2.3 From f7d39df3167cb443e5e3ed319266abac4d2ed845 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 29 Feb 2012 15:14:27 +0100 Subject: cw1200: Do not send BSS params if we are not associated Connection parameters must be set only if we are associated. This is firmware requirement. Otherwise WLAN device connot operate correctly. ST-Ericsson ID: 361264 Change-Id: I13b34a641b6cfd58d2e599475669700973e6bfba Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36721 Reviewed-by: QABUILD --- drivers/staging/cw1200/ap.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index f09d9031d0f..f328cff389e 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -566,8 +566,13 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, priv->cqm_beacon_loss_count ? priv->cqm_beacon_loss_count : priv->cqm_link_loss_count; - WARN_ON(wsm_set_bss_params(priv, &priv->bss_params)); - priv->setbssparams_done = true; + /* Make sure we are associated before sending + * set_bss_params to firmware */ + if (priv->bss_params.aid) { + WARN_ON(wsm_set_bss_params(priv, + &priv->bss_params)); + priv->setbssparams_done = true; + } } #endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ } -- cgit v1.2.3 From da3d13a635111dc6e9d0f69ff55fc06ce329d0b7 Mon Sep 17 00:00:00 2001 From: Piotr Nakraszewicz Date: Wed, 29 Feb 2012 15:14:28 +0100 Subject: cw1200: Enable IEEE802.11 PS for P2P Enable IEEE802.11 PS for P2P operations. ST-Ericsson ID: 361465 ST-Ericsson FOSS-OUT ID: NA Change-Id: Id41b61d7a95359021ca4534da1bfa747688e925c Signed-off-by: Piotr Nakraszewicz Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36668 Reviewed-by: QATOOLS Reviewed-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/sta.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 570d4c6bd6f..dd227bec2a3 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -348,6 +348,16 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) modeinfo = &priv->p2p_ps_modeinfo; sta_printk(KERN_DEBUG "[STA] IEEE80211_CONF_CHANGE_P2P_PS\n"); + if (conf->p2p_ps.legacy_ps >= 0) { + if (conf->p2p_ps.legacy_ps > 0) + priv->powersave_mode.pmMode = WSM_PSM_PS; + else + priv->powersave_mode.pmMode = WSM_PSM_ACTIVE; + + if (priv->join_status == CW1200_JOIN_STATUS_STA) + cw1200_set_pm(priv, &priv->powersave_mode); + } + if (conf->p2p_ps.ctwindow >= 128) modeinfo->oppPsCTWindow = 127; else if (conf->p2p_ps.ctwindow >= 0) -- cgit v1.2.3 From 74ce5fb12b483f13663096a5d317c73cb99ea6da Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:14:29 +0100 Subject: cw1200: Enable Block ACK only for 11n BSS. Driver tried to setup Block ACK not taking in account 11n capabilities of the BSS. It even tried to setup Block ACK for legacy 11g BSSes. Patch checks HT status of the current connection before enabling Block ACK. ST-Ericsson ID: 369354 Change-Id: I11515c4d70dbb572b1e2da6749ba123c3ca71751 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36608 Reviewed-by: QABUILD Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/ap.c | 8 ++++++++ drivers/staging/cw1200/main.c | 14 ++++---------- drivers/staging/cw1200/sta.c | 7 ++++--- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index f328cff389e..25612eba1ca 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -457,6 +457,14 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, priv->setbssparams_done = true; WARN_ON(wsm_set_beacon_wakeup_period(priv, dtim_interval, listen_interval)); + if (sta && cw1200_is_ht(&priv->ht_info)) { + ap_printk(KERN_DEBUG + "[STA] Enabling Block ACK\n"); + WARN_ON(wsm_set_block_ack_policy(priv, + priv->ba_tid_mask, + priv->ba_tid_mask)); + } + cw1200_set_pm(priv, &priv->powersave_mode); if (priv->is_BT_Present) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index e57519aa3fb..c8eb01b94e1 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -158,11 +158,8 @@ static struct ieee80211_supported_band cw1200_band_2ghz = { .bitrates = cw1200_g_rates, .n_bitrates = cw1200_g_rates_size, .ht_cap = { - .cap = IEEE80211_HT_CAP_SM_PS | - IEEE80211_HT_CAP_GRN_FLD | - /* HT Rx STBC: Rx support of one spatial stream */ - 0x0100 | - IEEE80211_HT_CAP_DELAY_BA | + .cap = IEEE80211_HT_CAP_GRN_FLD | + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | IEEE80211_HT_CAP_MAX_AMSDU, .ht_supported = 1, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, @@ -182,11 +179,8 @@ static struct ieee80211_supported_band cw1200_band_5ghz = { .bitrates = cw1200_a_rates, .n_bitrates = cw1200_a_rates_size, .ht_cap = { - .cap = IEEE80211_HT_CAP_SM_PS | - IEEE80211_HT_CAP_GRN_FLD | - /* HT Rx STBC: Rx support of one spatial stream */ - 0x0100 | - IEEE80211_HT_CAP_DELAY_BA | + .cap = IEEE80211_HT_CAP_GRN_FLD | + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | IEEE80211_HT_CAP_MAX_AMSDU, .ht_supported = 1, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index dd227bec2a3..bac6fa6a6a2 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1279,6 +1279,9 @@ void cw1200_join_work(struct work_struct *work) &priv->join_timeout, 3 * HZ); cw1200_update_listening(priv, false); + /* BlockACK policy will be updated when assoc is done */ + WARN_ON(wsm_set_block_ack_policy(priv, + 0, 0)); if (wsm_join(priv, &join)) { memset(&priv->join_bssid[0], 0, sizeof(priv->join_bssid)); @@ -1291,8 +1294,6 @@ void cw1200_join_work(struct work_struct *work) cw1200_queue_requeue(queue, priv->pending_frame_id); priv->join_status = CW1200_JOIN_STATUS_STA; } - WARN_ON(wsm_set_block_ack_policy(priv, - priv->ba_tid_mask, priv->ba_tid_mask)); cw1200_update_filtering(priv); } mutex_unlock(&priv->conf_mutex); @@ -1355,7 +1356,7 @@ void cw1200_unjoin_work(struct work_struct *work) cancel_delayed_work_sync(&priv->connection_loss_work); cw1200_update_listening(priv, priv->listening); WARN_ON(wsm_set_block_ack_policy(priv, - priv->ba_tid_mask, priv->ba_tid_mask)); + 0, 0)); cw1200_update_filtering(priv); priv->setbssparams_done = false; memset(&priv->association_mode, 0, -- cgit v1.2.3 From 66854f8dfa58d91afeb9b0baac364682b0d72415 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:14:30 +0100 Subject: cw1200: Add option to disable 11d beacon hints. Some setups use country information from an external provider. For these setups 11d hinting in the driver should be disabled to avoid interference with misconfigured APs in the air. ST-Ericsson ID: 372882 Change-Id: I734880c8f14aa2a57453623d09099f813d3d207f Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36609 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/Kconfig | 7 +++++++ drivers/staging/cw1200/main.c | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/drivers/staging/cw1200/Kconfig b/drivers/staging/cw1200/Kconfig index 8a536acc7d9..dbb97adb829 100644 --- a/drivers/staging/cw1200/Kconfig +++ b/drivers/staging/cw1200/Kconfig @@ -49,6 +49,13 @@ config CW1200_U5500_SUPPORT Say Y if you want to enable wlan on u5500 platform support. If unsure, say N. +config CW1200_DISABLE_BEACON_HINTS + bool "Disable 11d beacon hints" + depends on CW1200 + help + Say Y if you want to disable 11d beacon hints. + If unsure, say N. + menu "Driver debug features" depends on CW1200 diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index c8eb01b94e1..89d59e153b0 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -282,6 +282,10 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; #endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ +#if defined(CONFIG_CW1200_DISABLE_BEACON_HINTS) + hw->wiphy->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS; +#endif + hw->channel_change_time = 1000; /* TODO: find actual value */ /* priv->beacon_req_id = cpu_to_le32(0); */ hw->queues = 4; -- cgit v1.2.3 From c30f5f1edd818eb8f312420cd27f484932b53b77 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:14:31 +0100 Subject: cw1200: Unitialized spinlocks. Driver missed initialization of some spinlocks. ST-Ericsson ID: 372882 Change-Id: I144a4d4bc28b3a1fa14ade9c08786f8409baf441 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36612 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 89d59e153b0..4ecf491deec 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -328,6 +328,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) INIT_WORK(&priv->offchannel_work, cw1200_offchannel_work); INIT_WORK(&priv->wep_key_work, cw1200_wep_key_work); INIT_WORK(&priv->tx_policy_upload_work, tx_policy_upload_work); + spin_lock_init(&priv->event_queue_lock); INIT_LIST_HEAD(&priv->event_queue); INIT_WORK(&priv->event_handler, cw1200_event_handler); INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work); @@ -373,6 +374,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) init_waitqueue_head(&priv->wsm_cmd_wq); init_waitqueue_head(&priv->wsm_startup_done); wsm_buf_init(&priv->wsm_cmd_buf); + spin_lock_init(&priv->wsm_cmd.lock); tx_policy_init(priv); return hw; -- cgit v1.2.3 From bfa8adb2ecbfe1e7a5b24800df8f90e3b8c60e68 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:14:32 +0100 Subject: cw1200: Stay awake when DEAUTH frame is received. Driver can receive DEAUTH frame from the AP asyncronously at any moment in time. Then userspace has to do some sequence of operations to scan / reconnect / whatever. Default 1 sec. grace interval can be not sufficient enough for this kind of frames and system can miss ability to reconnect. Patch increases grace interval for DEAUTH frames to 5 sec. ST-Ericsson ID: 368312 Change-Id: I0fef153bb426c5eb16414e6c237d71092c2263c7 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36613 Reviewed-by: QABUILD Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/txrx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index f2f4ed014de..cb3537c0dd7 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -1095,6 +1095,8 @@ void cw1200_rx_cb(struct cw1200_common *priv, * wakelock. */ if (ieee80211_is_auth(frame->frame_control)) grace_period = 5 * HZ; + else if (ieee80211_is_deauth(frame->frame_control)) + grace_period = 5 * HZ; else grace_period = 1 * HZ; cw1200_pm_stay_awake(&priv->pm_state, grace_period); -- cgit v1.2.3 From 8ee10f1168369293cdc92ce9577ca0dc0a8f527d Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:14:33 +0100 Subject: cw1200: Do not set ListenInterval for BeaconWakeUpPeriod MIB. Firmware tends to miss DTIM beacons if this field is set. ST-Ericsson ID: 371816 Change-Id: I2151396691550582c3804adaf14311086e74c76d Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36614 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/ap.c | 14 ++++---------- drivers/staging/cw1200/main.c | 1 + drivers/staging/cw1200/sta.c | 18 ------------------ 3 files changed, 5 insertions(+), 28 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 25612eba1ca..5b5e5094953 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -269,7 +269,6 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, u32 changed) { struct cw1200_common *priv = dev->priv; - struct ieee80211_conf *conf = &dev->conf; mutex_lock(&priv->conf_mutex); if (changed & BSS_CHANGED_BSSID) { @@ -356,8 +355,7 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, BSS_CHANGED_ERP_SLOT)) { ap_printk(KERN_DEBUG "BSS_CHANGED_ASSOC.\n"); if (info->assoc) { /* TODO: ibss_joined */ - int dtim_interval = conf->ps_dtim_period; - int listen_interval = conf->listen_interval; + int dtim_interval = info->dtim_period; struct ieee80211_sta *sta = NULL; /* Associated: kill join timeout */ @@ -435,13 +433,9 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, if (dtim_interval < 1) dtim_interval = 1; - if (dtim_interval < priv->join_dtim_period) - dtim_interval = priv->join_dtim_period; - if (listen_interval < dtim_interval) - listen_interval = 0; - ap_printk(KERN_DEBUG "[STA] DTIM %d, listen %d\n", - dtim_interval, listen_interval); + ap_printk(KERN_DEBUG "[STA] DTIM %d\n", + dtim_interval); ap_printk(KERN_DEBUG "[STA] Preamble: %d, " \ "Greenfield: %d, Aid: %d, " \ "Rates: 0x%.8X, Basic: 0x%.8X\n", @@ -456,7 +450,7 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, WARN_ON(wsm_set_bss_params(priv, &priv->bss_params)); priv->setbssparams_done = true; WARN_ON(wsm_set_beacon_wakeup_period(priv, - dtim_interval, listen_interval)); + dtim_interval, 0)); if (sta && cw1200_is_ht(&priv->ht_info)) { ap_printk(KERN_DEBUG "[STA] Enabling Block ACK\n"); diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 4ecf491deec..1436c6e60f6 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -256,6 +256,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_CQM_RSSI | + IEEE80211_HW_NEED_DTIM_PERIOD | /* Aggregation is fully controlled by firmware. * Do not need any support from the mac80211 stack */ /* IEEE80211_HW_AMPDU_AGGREGATION | */ diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index bac6fa6a6a2..cda81e4c4b1 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -260,24 +260,6 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) WARN_ON(wsm_set_output_power(priv, priv->output_power * 10)); } - if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) { - /* TODO: Not sure. Needs to be verified. */ - /* TODO: DTIM skipping */ - int dtim_interval = conf->ps_dtim_period; - int listen_interval = conf->listen_interval; - if (dtim_interval < 1) - dtim_interval = 1; - if (listen_interval < dtim_interval) - listen_interval = 0; - /* TODO: max_sleep_period is not supported - * and silently skipped. */ - sta_printk(KERN_DEBUG "[STA] DTIM %d, listen %d\n", - dtim_interval, listen_interval); - WARN_ON(wsm_set_beacon_wakeup_period(priv, - dtim_interval, listen_interval)); - } - - if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) && (priv->channel != conf->channel)) { struct ieee80211_channel *ch = conf->channel; -- cgit v1.2.3 From dd9f2b57524eea31aa39632896736c25dd8cb05b Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 29 Feb 2012 15:14:34 +0100 Subject: cw1200: Fix potential SMP synchonization issues Adds mutex/semaphores unlocks in cw1200_probe_work Change-Id: I1380a233692c171cd315f0b3ce31b142b563e33b Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/37492 Reviewed-by: QABUILD Reviewed-by: Dmitry TARNYAGIN --- drivers/staging/cw1200/scan.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index e939e62bc95..0d3859fd011 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -375,6 +375,8 @@ void cw1200_probe_work(struct work_struct *work) if (cw1200_queue_get_skb(queue, priv->pending_frame_id, &frame.skb, &txpriv)) { + up(&priv->scan.lock); + mutex_unlock(&priv->conf_mutex); wsm_unlock_tx(priv); return; } -- cgit v1.2.3 From 723143f7b0f360d0161120909a502790afa09b41 Mon Sep 17 00:00:00 2001 From: "Ajitpal.Singh" Date: Wed, 29 Feb 2012 15:14:35 +0100 Subject: cw1200: Add workaround for WFD test case 6.1.10 Adds workaround for WFD test case 6.1.10. The test engine misbehaves and sends ACTION (Device Discoverability) frames with the same sequence number which is then dropped by the firmware. To get around this we temporarily MAP to SA of the ACTION frame and sent a reset request to the FW. ST-Ericsson ID: 372706 ST-Ericsson FOSS-OUT ID: NA Signed-off-by: Ajitpal.Singh Change-Id: Id1d80920a3d3c57d14a81e771ff5b0dc73a4184d Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/37167 Reviewed-by: Bartosz MARKOWSKI Tested-by: Ajit Pal SINGH --- drivers/staging/cw1200/cw1200.h | 5 +++++ drivers/staging/cw1200/main.c | 3 +++ drivers/staging/cw1200/txrx.c | 50 +++++++++++++++++++++++++++++++++++++---- drivers/staging/cw1200/txrx.h | 6 +++++ 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 1852c734ce5..0ab5d04a054 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -231,6 +231,11 @@ struct cw1200_common { /* statistics */ struct ieee80211_low_level_stats stats; +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + /* Workaround for WFD testcase 6.1.10*/ + struct work_struct linkid_reset_work; + u8 action_frame_sa[ETH_ALEN]; +#endif }; struct cw1200_sta_priv { diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 1436c6e60f6..02373201114 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -342,6 +342,9 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work); INIT_WORK(&priv->link_id_work, cw1200_link_id_work); INIT_DELAYED_WORK(&priv->link_id_gc_work, cw1200_link_id_gc_work); +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + INIT_WORK(&priv->linkid_reset_work, cw1200_link_id_reset); +#endif init_timer(&priv->mcast_timeout); priv->mcast_timeout.data = (unsigned long)priv; priv->mcast_timeout.function = cw1200_mcast_timeout; diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index cb3537c0dd7..b59ebf6e4cc 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -719,9 +719,15 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) if (WARN_ON(t.queue >= 4 || !t.rate)) goto drop; - ret = cw1200_tx_h_calc_link_ids(priv, &t); - if (ret) - goto drop; + /* Temporary change*/ + if (unlikely(ieee80211_is_probe_resp(t.hdr->frame_control))) { + t.txpriv.raw_link_id = + t.txpriv.link_id = 0; + } else { + ret = cw1200_tx_h_calc_link_ids(priv, &t); + if (ret) + goto drop; + } txrx_printk(KERN_DEBUG "[TX] TX %d bytes " "(queue: %d, link_id: %d (%d)).\n", @@ -976,6 +982,9 @@ void cw1200_rx_cb(struct cw1200_common *priv, struct sk_buff *skb = *skb_p; struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; +#endif struct cw1200_link_entry *entry = NULL; unsigned long grace_period; bool early_data = false; @@ -993,7 +1002,15 @@ void cw1200_rx_cb(struct cw1200_common *priv, early_data = true; entry->timestamp = jiffies; } - +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + else if ((priv->vif->p2p == WSM_START_MODE_P2P_GO) + && ieee80211_is_action(frame->frame_control) + && (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { + txrx_printk(KERN_DEBUG "[RX] Going to MAP&RESET link ID\n"); + memcpy(&priv->action_frame_sa[0], ieee80211_get_SA(frame), ETH_ALEN); + schedule_work(&priv->linkid_reset_work); + } +#endif if (unlikely(arg->status)) { if (arg->status == WSM_STATUS_MICFAILURE) { txrx_printk(KERN_DEBUG "[RX] MIC failure.\n"); @@ -1161,3 +1178,28 @@ int cw1200_upload_keys(struct cw1200_common *priv) } return ret; } +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) +/* Workaround for WFD test case 6.1.10 */ +void cw1200_link_id_reset(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, linkid_reset_work); + int temp_linkid; + + /* In GO mode we can receive ACTION frames without a linkID */ + temp_linkid = cw1200_alloc_link_id(priv, &priv->action_frame_sa[0]); + WARN_ON(!temp_linkid); + if (temp_linkid) { + /* Make sure we execute the WQ */ + flush_workqueue(priv->workqueue); + /* Release the link ID */ + spin_lock(&priv->ps_state_lock); + priv->link_id_db[temp_linkid - 1].status = + CW1200_LINK_RESERVE; + spin_unlock(&priv->ps_state_lock); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(priv); + } +} +#endif diff --git a/drivers/staging/cw1200/txrx.h b/drivers/staging/cw1200/txrx.h index 9f4f40ea31c..f3b2023ff2b 100644 --- a/drivers/staging/cw1200/txrx.h +++ b/drivers/staging/cw1200/txrx.h @@ -86,4 +86,10 @@ void cw1200_free_key(struct cw1200_common *priv, int idx); void cw1200_free_keys(struct cw1200_common *priv); int cw1200_upload_keys(struct cw1200_common *priv); +/* ******************************************************************** */ +/* Workaround for WFD test case 6.1.10 */ +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) +void cw1200_link_id_reset(struct work_struct *work); +#endif + #endif /* CW1200_TXRX_H */ -- cgit v1.2.3 From 4c4e06be96fda139dad42b7b743091a58dda92f0 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:14:36 +0100 Subject: cw1200: block consecutive calls to wsm_set_pm Reported by Dheeshan SASI: One observation in OMAC logging is that there are repeated calls of set-PM-Mode-Req. This is incorrect, it is a call that should be made only once. After this, the FW manages power control. Each time the call is made the FW states have to be reset. Even on channel changes (as in code snippet below) FW will manage the power save states. Host MAC should not control this. Leave always in fast power save, the only practical reason to change this is for rogue/bad APs. Channel change will internally change the joined channel. If there appears to be a link to Power Save, then this set-PM-Mode-Req issue should be corrected. The call can be made once per association, after set-bss-params. This means all the information for power save is available (AID especially). Also after Reset if necessary for OMAC. Similarly operational mode also should only be called once. There is no need to keep calling this, however this has less impact and no large state machine is involved. ST-Ericsson ID: 355071 Change-Id: I740e9b21f5e1fdf0b696ddc709b26a640872922c Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/37688 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/cw1200.h | 1 + drivers/staging/cw1200/scan.c | 4 ++-- drivers/staging/cw1200/sta.c | 16 +++++++++------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 0ab5d04a054..6dbc59eeab5 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -116,6 +116,7 @@ struct cw1200_common { struct wsm_set_bss_params bss_params; struct cw1200_ht_info ht_info; struct wsm_set_pm powersave_mode; + struct wsm_set_pm firmware_ps_mode; int cqm_rssi_thold; unsigned cqm_rssi_hyst; unsigned cqm_tx_failure_thold; diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index 0d3859fd011..0d6a1c2f5b7 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -152,7 +152,7 @@ void cw1200_scan_work(struct work_struct *work) !(priv->powersave_mode.pmMode & WSM_PSM_PS)) { struct wsm_set_pm pm = priv->powersave_mode; pm.pmMode = WSM_PSM_PS; - wsm_set_pm(priv, &pm); + cw1200_set_pm(priv, &pm); } else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) { /* FW bug: driver has to restart p2p-dev mode * after scan */ @@ -166,7 +166,7 @@ void cw1200_scan_work(struct work_struct *work) priv->output_power * 10)); if (priv->join_status == CW1200_JOIN_STATUS_STA && !(priv->powersave_mode.pmMode & WSM_PSM_PS)) - wsm_set_pm(priv, &priv->powersave_mode); + cw1200_set_pm(priv, &priv->powersave_mode); if (priv->scan.req) wiphy_dbg(priv->hw->wiphy, diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index cda81e4c4b1..6c79880900b 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -389,11 +389,6 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) } if (changed & IEEE80211_CONF_CHANGE_IDLE) { - struct wsm_operational_mode mode = { - .power_mode = wsm_power_mode_quiescent, - .disableMoreFlagUsage = true, - }; - wsm_lock_tx(priv); /* Disable p2p-dev mode forced by TX request */ if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) && @@ -402,7 +397,6 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) cw1200_disable_listening(priv); priv->join_status = CW1200_JOIN_STATUS_PASSIVE; } - WARN_ON(wsm_set_operational_mode(priv, &mode)); wsm_unlock_tx(priv); } @@ -597,7 +591,13 @@ int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg) if (priv->uapsd_info.uapsdFlags != 0) pm.pmMode &= ~WSM_PSM_FAST_PS_FLAG; - return wsm_set_pm(priv, &pm); + if (memcmp(&pm, &priv->firmware_ps_mode, + sizeof(struct wsm_set_pm))) { + priv->firmware_ps_mode = pm; + return wsm_set_pm(priv, &pm); + } else { + return 0; + } } int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, @@ -1344,6 +1344,8 @@ void cw1200_unjoin_work(struct work_struct *work) memset(&priv->association_mode, 0, sizeof(priv->association_mode)); memset(&priv->bss_params, 0, sizeof(priv->bss_params)); + memset(&priv->firmware_ps_mode, 0, + sizeof(priv->firmware_ps_mode)); sta_printk(KERN_DEBUG "[STA] Unjoin.\n"); } mutex_unlock(&priv->conf_mutex); -- cgit v1.2.3 From fc8126b0320d96e97f64122a1114d4da10507f2e Mon Sep 17 00:00:00 2001 From: Marek Kwaczynski Date: Wed, 29 Feb 2012 15:14:37 +0100 Subject: cw1200: Fix for MIC failure - strip ICV Firmware strips ICV in case of MIC failure it was a root cause that the frame was dropped. ST-Ericsson ID: 355071 ST-Ericsson FOSS-OUT ID: NA Change-Id: Iae90e606c23d901bc9693693b6a2c0aaf428231a Signed-off-by: Marek Kwaczynski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32924 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/txrx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index b59ebf6e4cc..5b5d484e0ce 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -1086,6 +1086,10 @@ void cw1200_rx_cb(struct cw1200_common *priv, goto drop; } + /* Firmware strips ICV in case of MIC failure. */ + if (arg->status == WSM_STATUS_MICFAILURE) + icv_len = 0; + if (skb->len < hdrlen + iv_len + icv_len) { wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. " "Size is lesser than crypto headers.\n"); -- cgit v1.2.3 From 9da830b2f4b7cb886d6b0d816bd7b857271e4c8e Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:14:38 +0100 Subject: cw1200: TX burst forming. The patch implements simple TX burst forming. ST-Ericsson ID: 355241, 355096, 355166, 355221 Change-Id: Ied9fb8c619783881b3fc22e16829ee13bb651cdc Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36790 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/bh.c | 25 +++++++++++++++++++++---- drivers/staging/cw1200/debug.c | 4 ++++ drivers/staging/cw1200/debug.h | 20 ++++++++++++++++++++ drivers/staging/cw1200/wsm.c | 14 ++++++++++++-- drivers/staging/cw1200/wsm.h | 5 +++-- 5 files changed, 60 insertions(+), 8 deletions(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index acc16459807..1f7409fbff4 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -22,6 +22,7 @@ #include "hwio.h" #include "wsm.h" #include "sbus.h" +#include "debug.h" #if defined(CONFIG_CW1200_BH_DEBUG) #define bh_printk(...) printk(__VA_ARGS__) @@ -261,6 +262,8 @@ static int cw1200_bh(void *arg) u16 ctrl_reg = 0; int tx_allowed; int pending_tx = 0; + int tx_burst; + int rx_burst = 0; long status; u8 dummy; @@ -339,8 +342,10 @@ static int cw1200_bh(void *arg) break; rx: read_len = (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2; - if (!read_len) + if (!read_len) { + rx_burst = 0; goto tx; + } if (WARN_ON((read_len < sizeof(struct wsm_hdr)) || (read_len > EFFECTIVE_BUF_SIZE))) { @@ -425,13 +430,19 @@ rx: } read_len = 0; + + if (rx_burst) { + cw1200_debug_rx_burst(priv); + --rx_burst; + goto rx; + } } tx: /* HACK! One buffer is reserved for control path */ BUG_ON(priv->hw_bufs_used > priv->wsm_caps.numInpChBufs); - tx_allowed = - priv->hw_bufs_used < priv->wsm_caps.numInpChBufs; + tx_burst = priv->wsm_caps.numInpChBufs - priv->hw_bufs_used; + tx_allowed = tx_burst > 0; if (tx && tx_allowed) { size_t tx_len; @@ -452,7 +463,7 @@ tx: } wsm_alloc_tx_buffer(priv); - ret = wsm_get_tx(priv, &data, &tx_len); + ret = wsm_get_tx(priv, &data, &tx_len, &tx_burst); if (ret <= 0) { wsm_release_tx_buffer(priv, 1); if (WARN_ON(ret < 0)) @@ -503,6 +514,12 @@ tx: wsm_txed(priv, data); priv->wsm_tx_seq = (priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX; + + if (tx_burst > 1) { + cw1200_debug_tx_burst(priv); + ++rx_burst; + goto tx; + } } } diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 50941846e28..597f94de87f 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -291,6 +291,10 @@ static int cw1200_status_show(struct seq_file *seq, void *v) d->tx_cache_miss); seq_printf(seq, "TX align: %d\n", d->tx_align); + seq_printf(seq, "TX burst: %d\n", + d->tx_burst); + seq_printf(seq, "RX burst: %d\n", + d->rx_burst); seq_printf(seq, "TX TTL: %d\n", d->tx_ttl); seq_printf(seq, "Scan: %s\n", diff --git a/drivers/staging/cw1200/debug.h b/drivers/staging/cw1200/debug.h index 96b11feab1c..02943e699b8 100644 --- a/drivers/staging/cw1200/debug.h +++ b/drivers/staging/cw1200/debug.h @@ -27,6 +27,8 @@ struct cw1200_debug_priv { int tx_cache_miss; int tx_align; int tx_ttl; + int tx_burst; + int rx_burst; }; int cw1200_debug_init(struct cw1200_common *priv); @@ -74,6 +76,16 @@ static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv) ++priv->debug->tx_ttl; } +static inline void cw1200_debug_tx_burst(struct cw1200_common *priv) +{ + ++priv->debug->tx_burst; +} + +static inline void cw1200_debug_rx_burst(struct cw1200_common *priv) +{ + ++priv->debug->rx_burst; +} + #else /* CONFIG_CW1200_DEBUGFS */ static inline int cw1200_debug_init(struct cw1200_common *priv) @@ -118,6 +130,14 @@ static inline void cw1200_debug_tx_ttl(struct cw1200_common *priv) { } +static inline void cw1200_debug_tx_burst(struct cw1200_common *priv) +{ +} + +static inline void cw1200_debug_rx_burst(struct cw1200_common *priv) +{ +} + #endif /* CONFIG_CW1200_DEBUGFS */ #endif /* CW1200_DEBUG_H_INCLUDED */ diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index add382d78ee..a93ecc534ca 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1548,13 +1548,15 @@ found: } int wsm_get_tx(struct cw1200_common *priv, u8 **data, - size_t *tx_len) + size_t *tx_len, int *burst) { struct wsm_tx *wsm = NULL; struct ieee80211_tx_info *tx_info; struct cw1200_queue *queue = NULL; + int queue_num; u32 tx_allowed_mask = 0; const struct cw1200_txpriv *txpriv = NULL; + static const int trottle[] = { 0, 2, 4, 6 }; /* * Count was intended as an input for wsm->more flag. * During implementation it was found that wsm->more @@ -1572,6 +1574,7 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, BUG_ON(!priv->wsm_cmd.ptr); *data = priv->wsm_cmd.ptr; *tx_len = priv->wsm_cmd.len; + *burst = 1; spin_unlock(&priv->wsm_cmd.lock); } else { for (;;) { @@ -1584,6 +1587,7 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, ret = wsm_get_tx_queue_and_mask(priv, &queue, &tx_allowed_mask, &more); + queue_num = queue - priv->tx_queue; if (priv->buffered_multicasts && (ret || !more) && @@ -1617,7 +1621,13 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, *data = (u8 *)wsm; *tx_len = __le16_to_cpu(wsm->hdr.len); - + if (priv->sta_asleep_mask) + *burst = 1; + else + *burst = min(max( + *burst - trottle[queue_num], 1), + (int)cw1200_queue_get_num_queued( + queue, -1)); if (more) { struct ieee80211_hdr *hdr = diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 635c9c7fca1..61c1d5b8a02 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -1716,8 +1716,9 @@ struct wsm_cmd { /* ******************************************************************** */ /* WSM TX buffer access */ -int wsm_get_tx(struct cw1200_common *priv, u8 ** data, size_t * tx_len); -void wsm_txed(struct cw1200_common *priv, u8 * data); +int wsm_get_tx(struct cw1200_common *priv, u8 **data, + size_t *tx_len, int *burst); +void wsm_txed(struct cw1200_common *priv, u8 *data); /* ******************************************************************** */ /* Queue mapping: WSM <---> linux */ -- cgit v1.2.3 From b55d18dd740545779e0f6d305f498133281854e4 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:14:39 +0100 Subject: cw1200: Dynamic TX queue prioritization. Hard TX queue prioritization was used in the driver: VO - VI - BE - BK. It worked perfectly fine with real data, but some artificial certification testcases could not be passed. Patch recalcs TX priorities on every TX using EDCA values, correctly handling "strange" certification cases. ST-Ericsson ID: 355241, 355096, 355166, 355221 Change-Id: Ib8fc5f24872a4cdebbeafa3a4f724ff1e69a78ec Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36791 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/bh.c | 1 - drivers/staging/cw1200/cw1200.h | 1 + drivers/staging/cw1200/wsm.c | 110 +++++++++++++++++++++++++++------------- 3 files changed, 77 insertions(+), 35 deletions(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index 1f7409fbff4..051cb97df00 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -439,7 +439,6 @@ rx: } tx: - /* HACK! One buffer is reserved for control path */ BUG_ON(priv->hw_bufs_used > priv->wsm_caps.numInpChBufs); tx_burst = priv->wsm_caps.numInpChBufs - priv->hw_bufs_used; tx_allowed = tx_burst > 0; diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 6dbc59eeab5..e3bc9c849f7 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -78,6 +78,7 @@ struct cw1200_link_entry { struct cw1200_common { struct cw1200_queue tx_queue[4]; struct cw1200_queue_stats tx_queue_stats; + int tx_burst_idx; struct cw1200_debug_priv *debug; struct ieee80211_hw *hw; diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index a93ecc534ca..20d4c4b619d 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "cw1200.h" #include "wsm.h" @@ -455,6 +456,7 @@ int wsm_join(struct cw1200_common *priv, struct wsm_join *arg) WSM_PUT32(buf, arg->beaconInterval); WSM_PUT32(buf, arg->basicRateSet); + priv->tx_burst_idx = -1; ret = wsm_cmd_send(priv, buf, arg, 0x000B, WSM_CMD_JOIN_TIMEOUT); wsm_cmd_unlock(priv); return ret; @@ -684,6 +686,7 @@ int wsm_start(struct cw1200_common *priv, const struct wsm_start *arg) WSM_PUT(buf, arg->ssid, sizeof(arg->ssid)); WSM_PUT32(buf, arg->basicRateSet); + priv->tx_burst_idx = -1; ret = wsm_cmd_send(priv, buf, NULL, 0x0017, WSM_CMD_START_TIMEOUT); wsm_cmd_unlock(priv); @@ -1498,52 +1501,85 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, return handled; } +static int cw1200_get_prio_queue(struct cw1200_common *priv, + u32 link_id_map, int *total) +{ + static const int urgent = BIT(CW1200_LINK_ID_AFTER_DTIM) | + BIT(CW1200_LINK_ID_UAPSD); + struct wsm_edca_queue_params *edca; + unsigned score, best = -1; + int winner = -1; + int queued; + int i; + + /* search for a winner using edca params */ + for (i = 0; i < 4; ++i) { + queued = cw1200_queue_get_num_queued(&priv->tx_queue[i], + link_id_map); + if (!queued) + continue; + *total += queued; + edca = &priv->edca.params[i]; + score = ((edca->aifns + edca->cwMin) << 16) + + (edca->cwMax - edca->cwMin) * + (random32() & 0xFFFF); + if (score < best) { + best = score; + winner = i; + } + } + + /* override winner if bursting */ + if (winner >= 0 && priv->tx_burst_idx >= 0 && + winner != priv->tx_burst_idx && + !cw1200_queue_get_num_queued( + &priv->tx_queue[winner], + link_id_map & urgent) && + cw1200_queue_get_num_queued( + &priv->tx_queue[priv->tx_burst_idx], + link_id_map)) + winner = priv->tx_burst_idx; + + return winner; +} + static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv, struct cw1200_queue **queue_p, u32 *tx_allowed_mask_p, bool *more) { - int i; - struct cw1200_queue *queue = NULL; + int idx; u32 tx_allowed_mask; - int mcasts = 0; + int total = 0; /* Search for a queue with multicast frames buffered */ if (priv->tx_multicast) { tx_allowed_mask = BIT(CW1200_LINK_ID_AFTER_DTIM); - for (i = 0; i < 4; ++i) { - mcasts += cw1200_queue_get_num_queued( - &priv->tx_queue[i], tx_allowed_mask); - if (!queue && mcasts) - queue = &priv->tx_queue[i]; - if (mcasts > 1) - break; - } - if (mcasts) + idx = cw1200_get_prio_queue(priv, + tx_allowed_mask, &total); + if (idx >= 0) { + *more = total > 1; goto found; + } } /* Search for unicast traffic */ - for (i = 0; i < 4; ++i) { - queue = &priv->tx_queue[i]; - tx_allowed_mask = ~priv->sta_asleep_mask; - tx_allowed_mask |= BIT(CW1200_LINK_ID_UAPSD); - if (priv->sta_asleep_mask) { - tx_allowed_mask |= priv->pspoll_mask; - tx_allowed_mask &= ~BIT(CW1200_LINK_ID_AFTER_DTIM); - } else { - tx_allowed_mask |= BIT(CW1200_LINK_ID_AFTER_DTIM); - } - if (cw1200_queue_get_num_queued( - queue, tx_allowed_mask)) - goto found; + tx_allowed_mask = ~priv->sta_asleep_mask; + tx_allowed_mask |= BIT(CW1200_LINK_ID_UAPSD); + if (priv->sta_asleep_mask) { + tx_allowed_mask |= priv->pspoll_mask; + tx_allowed_mask &= ~BIT(CW1200_LINK_ID_AFTER_DTIM); + } else { + tx_allowed_mask |= BIT(CW1200_LINK_ID_AFTER_DTIM); } - return -ENOENT; + idx = cw1200_get_prio_queue(priv, + tx_allowed_mask, &total); + if (idx < 0) + return -ENOENT; found: - *queue_p = queue; + *queue_p = &priv->tx_queue[idx]; *tx_allowed_mask_p = tx_allowed_mask; - *more = mcasts > 1; return 0; } @@ -1556,7 +1592,6 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, int queue_num; u32 tx_allowed_mask = 0; const struct cw1200_txpriv *txpriv = NULL; - static const int trottle[] = { 0, 2, 4, 6 }; /* * Count was intended as an input for wsm->more flag. * During implementation it was found that wsm->more @@ -1621,13 +1656,20 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, *data = (u8 *)wsm; *tx_len = __le16_to_cpu(wsm->hdr.len); - if (priv->sta_asleep_mask) + + /* allow bursting if txop is set */ + if (priv->edca.params[queue_num].txOpLimit) + *burst = min(*burst, + (int)cw1200_queue_get_num_queued( + queue, tx_allowed_mask) + 1); + else *burst = 1; + + /* store index of bursting queue */ + if (*burst > 1) + priv->tx_burst_idx = queue_num; else - *burst = min(max( - *burst - trottle[queue_num], 1), - (int)cw1200_queue_get_num_queued( - queue, -1)); + priv->tx_burst_idx = -1; if (more) { struct ieee80211_hdr *hdr = -- cgit v1.2.3 From d5b4e118d8a74faa340db6d2d0585234778399d9 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:14:40 +0100 Subject: cw1200: Drop frames from locked queue. The driver locks queues when no TX policies are available. Higher layer should not send any frames and wait until queue is unlocked again. It does not work well with injected frames from certification test engine, leading to BUG_ON in the code. The patch changes BUG_ON to WARN_ON_ONCE and implements a fallback (drops the frame) in this case. ST-Ericsson ID: 355166 Change-Id: Iab8c66d2511afd42bd119c3f776581f0b05dcf51 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/37447 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/txrx.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 5b5d484e0ce..a754f6a53cd 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -235,7 +235,10 @@ static int tx_policy_get(struct cw1200_common *priv, tx_policy_build(priv, &wanted, rates, count); spin_lock_bh(&cache->lock); - BUG_ON(list_empty(&cache->free)); + if (WARN_ON_ONCE(list_empty(&cache->free))) { + spin_unlock_bh(&cache->lock); + return CW1200_INVALID_RATE_ID; + } idx = tx_policy_find(cache, &wanted); if (idx >= 0) { tx_policy_printk(KERN_DEBUG "[TX policy] Used TX policy: %d\n", @@ -635,7 +638,7 @@ cw1200_tx_h_bt(struct cw1200_common *priv, wsm->flags |= priority << 1; } -static void +static int cw1200_tx_h_rate_policy(struct cw1200_common *priv, struct cw1200_txinfo *t, struct wsm_tx *wsm) @@ -655,7 +658,9 @@ cw1200_tx_h_rate_policy(struct cw1200_common *priv, t->txpriv.rate_id = tx_policy_get(priv, t->tx_info->control.rates, IEEE80211_TX_MAX_RATES, &tx_policy_renew); - wsm->flags = t->txpriv.rate_id << 4; + if (t->txpriv.rate_id == CW1200_INVALID_RATE_ID) + return -EFAULT; + wsm->flags |= t->txpriv.rate_id << 4; if (tx_policy_renew) { tx_policy_printk(KERN_DEBUG "[TX] TX policy renew.\n"); @@ -668,6 +673,7 @@ cw1200_tx_h_rate_policy(struct cw1200_common *priv, cw1200_tx_queues_lock(priv); queue_work(priv->workqueue, &priv->tx_policy_upload_work); } + return 0; } static bool @@ -752,7 +758,9 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) } wsm->flags |= flags; cw1200_tx_h_bt(priv, &t, wsm); - cw1200_tx_h_rate_policy(priv, &t, wsm); + ret = cw1200_tx_h_rate_policy(priv, &t, wsm); + if (ret) + goto drop; spin_lock_bh(&priv->ps_state_lock); { -- cgit v1.2.3 From 12f4378b06d2532e46445c501e9fb99e4f9e5b82 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:14:41 +0100 Subject: cw1200: Fix for unbalanced tx_lock/unlock in TX policy uploading Return code for queue_work was not checked, leading to leaking lock. ST-Ericsson ID: 355166 Change-Id: I60da9c112be5765b5dd0e9a6d8d4fc4dded26534 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/38058 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/txrx.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index a754f6a53cd..d868e7bc9af 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -671,7 +671,11 @@ cw1200_tx_h_rate_policy(struct cw1200_common *priv, /* Definetly better. TODO. */ wsm_lock_tx_async(priv); cw1200_tx_queues_lock(priv); - queue_work(priv->workqueue, &priv->tx_policy_upload_work); + if (queue_work(priv->workqueue, + &priv->tx_policy_upload_work) <= 0) { + cw1200_tx_queues_unlock(priv); + wsm_unlock_tx(priv); + } } return 0; } -- cgit v1.2.3 From 02bbad9ebac15c873138ca0401a80eb0afad660c Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:14:42 +0100 Subject: cw1200: fix for wrong order of lock operations in cw1200_config Invalid lock order was used in cw1200_config, leading to a deadlock with scan. Proper order of lock operations if you need both scan and config locks: - down(&priv->scan.lock); - mutex_lock(&priv->conf_mutex); - do_something(); - mutex_unlock(&priv->conf_mutex); - up(&priv->scan.lock); ST-Ericsson ID: 375035 Change-Id: I1988fc061b014bd5d1ffe3ab739cb0fcb3590224 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/38121 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/sta.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 6c79880900b..f3c42f35d7a 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -251,6 +251,7 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) struct cw1200_common *priv = dev->priv; struct ieee80211_conf *conf = &dev->conf; + down(&priv->scan.lock); mutex_lock(&priv->conf_mutex); /* TODO: IEEE80211_CONF_CHANGE_QOS */ if (changed & IEEE80211_CONF_CHANGE_POWER) { @@ -272,12 +273,6 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) ret = WARN_ON(__cw1200_flush(priv, false)); if (!ret) { - while(down_trylock(&priv->scan.lock)) { - sta_printk(KERN_DEBUG "[STA] waiting, " - "scan in progress.\n"); - msleep(100); - } - ret = WARN_ON(wsm_switch_channel(priv, &channel)); if (!ret) { ret = wait_event_timeout( @@ -294,8 +289,6 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) ret = -ETIMEDOUT; } else wsm_unlock_tx(priv); - - up(&priv->scan.lock); } } @@ -316,12 +309,9 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) priv->powersave_mode.fastPsmIdlePeriod = conf->dynamic_ps_timeout << 1; - if (priv->join_status == CW1200_JOIN_STATUS_STA && priv->bss_params.aid) { - while (down_trylock(&priv->scan.lock)) - msleep(100); + if (priv->join_status == CW1200_JOIN_STATUS_STA && + priv->bss_params.aid) cw1200_set_pm(priv, &priv->powersave_mode); - up(&priv->scan.lock); - } } #if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) @@ -417,6 +407,7 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) /* WARN_ON(tx_policy_force_upload(priv)); */ } mutex_unlock(&priv->conf_mutex); + up(&priv->scan.lock); return ret; } @@ -1438,6 +1429,10 @@ int cw1200_set_uapsd_param(struct cw1200_common *priv, static int cw1200_cancel_scan(struct cw1200_common *priv) { - /* STUB(); */ + while (down_trylock(&priv->scan.lock)) { + priv->scan.req = NULL; + schedule(); + } + up(&priv->scan.lock); return 0; } -- cgit v1.2.3 From f8c369d9d9db4d88dd862ea6094a517edbcf0fb8 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:14:43 +0100 Subject: cw1200: remove blocking call to cw1200_cancel_scan() A call to cw1200_cancel_scan() was done with scan lock held. ST-Ericsson ID: 375035 Change-Id: I231efd6906ff8371c9caaed95cfc139fa2f3fa93 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/38183 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/sta.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index f3c42f35d7a..9c6bb5a72e0 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -267,7 +267,6 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) struct wsm_switch_channel channel = { .newChannelNumber = ch->hw_value, }; - cw1200_cancel_scan(priv); sta_printk(KERN_DEBUG "[STA] Freq %d (wsm ch: %d).\n", ch->center_freq, ch->hw_value); -- cgit v1.2.3 From 09f593a418774e2d06346cb10fcff67b5c323935 Mon Sep 17 00:00:00 2001 From: "Ajitpal.Singh" Date: Wed, 29 Feb 2012 15:14:44 +0100 Subject: cw1200: Stay awake if join request sent to FW If a join request is pending towards the firmware host should not try to suspend. This change takes a 3 second wakelock if join request is outstanding. ST-Ericsson ID: 367334 Signed-off-by: Ajitpal.Singh Change-Id: Ie36691211015c376ef3f02007872d9eccd038fb2 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/38125 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/sta.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 9c6bb5a72e0..7b9d81b1575 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1249,6 +1249,8 @@ void cw1200_join_work(struct work_struct *work) /* Queue unjoin if not associated in 3 sec. */ queue_delayed_work(priv->workqueue, &priv->join_timeout, 3 * HZ); + /*Stay Awake for Join Timeout*/ + cw1200_pm_stay_awake(&priv->pm_state, 3 * HZ); cw1200_update_listening(priv, false); /* BlockACK policy will be updated when assoc is done */ -- cgit v1.2.3 From 241956b2ab50491e2f537fa05928497f460bb68f Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:14:45 +0100 Subject: cw1200: temporary disable beacon filtering on join Driver and firmware implements beacon filtering to avoid unnecessary wakeups. At the same time information from beacons is needed for mac80211 stack to correctly set powersave parameters. Patch temporary disables beacon filtering to recieve and pass to the stack at least one beacon and reenables it after that. ST-Ericsson ID: 370390 Change-Id: I121e829a66f93e0fba1d9f845e7db78430ae98de Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/37801 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/cw1200.h | 4 +++- drivers/staging/cw1200/main.c | 1 + drivers/staging/cw1200/sta.c | 32 ++++++++++++++++++++++++++++++-- drivers/staging/cw1200/sta.h | 1 + drivers/staging/cw1200/txrx.c | 10 +++++++++- 5 files changed, 44 insertions(+), 4 deletions(-) diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index e3bc9c849f7..63ed796c6e6 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -137,8 +137,10 @@ struct cw1200_common { struct wsm_rx_filter rx_filter; struct wsm_beacon_filter_table bf_table; struct wsm_beacon_filter_control bf_control; - u8 ba_tid_mask; struct wsm_multicast_filter multicast_filter; + bool disable_beacon_filter; + struct work_struct update_filtering_work; + u8 ba_tid_mask; struct cw1200_pm_state pm_state; struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo; struct wsm_uapsd_info uapsd_info; diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 02373201114..f194d5e61b8 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -345,6 +345,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) #if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) INIT_WORK(&priv->linkid_reset_work, cw1200_link_id_reset); #endif + INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work); init_timer(&priv->mcast_timeout); priv->mcast_timeout.data = (unsigned long)priv; priv->mcast_timeout.function = cw1200_mcast_timeout; diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 7b9d81b1575..c2ef94bcc58 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -414,6 +414,10 @@ void cw1200_update_filtering(struct cw1200_common *priv) { int ret; bool bssid_filtering = !priv->rx_filter.bssid; + static struct wsm_beacon_filter_control bf_disabled = { + .enabled = 0, + .bcn_count = 1, + }; if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) return; @@ -423,8 +427,14 @@ void cw1200_update_filtering(struct cw1200_common *priv) ret = wsm_set_rx_filter(priv, &priv->rx_filter); if (!ret) ret = wsm_set_beacon_filter_table(priv, &priv->bf_table); - if (!ret) - ret = wsm_beacon_filter_control(priv, &priv->bf_control); + if (!ret) { + if (priv->disable_beacon_filter) + ret = wsm_beacon_filter_control(priv, + &bf_disabled); + else + ret = wsm_beacon_filter_control(priv, + &priv->bf_control); + } if (!ret) ret = wsm_set_bssid_filtering(priv, bssid_filtering); if (!ret) @@ -436,6 +446,15 @@ void cw1200_update_filtering(struct cw1200_common *priv) return; } +void cw1200_update_filtering_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, + update_filtering_work); + + cw1200_update_filtering(priv); +} + u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list) { @@ -1267,6 +1286,13 @@ void cw1200_join_work(struct work_struct *work) WARN_ON(cw1200_upload_keys(priv)); cw1200_queue_requeue(queue, priv->pending_frame_id); priv->join_status = CW1200_JOIN_STATUS_STA; + + /* Due to beacon filtering it is possible that the + * AP's beacon is not known for the mac80211 stack. + * Disable filtering temporary to make sure the stack + * receives at least one */ + priv->disable_beacon_filter = true; + } cw1200_update_filtering(priv); } @@ -1316,6 +1342,7 @@ void cw1200_unjoin_work(struct work_struct *work) BUG_ON(1); } if (priv->join_status) { + cancel_work_sync(&priv->update_filtering_work); memset(&priv->join_bssid[0], 0, sizeof(priv->join_bssid)); priv->join_status = CW1200_JOIN_STATUS_PASSIVE; @@ -1331,6 +1358,7 @@ void cw1200_unjoin_work(struct work_struct *work) cw1200_update_listening(priv, priv->listening); WARN_ON(wsm_set_block_ack_policy(priv, 0, 0)); + priv->disable_beacon_filter = false; cw1200_update_filtering(priv); priv->setbssparams_done = false; memset(&priv->association_mode, 0, diff --git a/drivers/staging/cw1200/sta.h b/drivers/staging/cw1200/sta.h index 0d0098e7346..e1257828004 100644 --- a/drivers/staging/cw1200/sta.h +++ b/drivers/staging/cw1200/sta.h @@ -75,6 +75,7 @@ void cw1200_offchannel_work(struct work_struct *work); void cw1200_wep_key_work(struct work_struct *work); void cw1200_update_listening(struct cw1200_common *priv, bool enabled); void cw1200_update_filtering(struct cw1200_common *priv); +void cw1200_update_filtering_work(struct work_struct *work); int __cw1200_flush(struct cw1200_common *priv, bool drop); int cw1200_enable_listening(struct cw1200_common *priv); int cw1200_disable_listening(struct cw1200_common *priv); diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index d868e7bc9af..db333afc444 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -1119,9 +1119,17 @@ void cw1200_rx_cb(struct cw1200_common *priv, cw1200_debug_rxed_agg(priv); if (ieee80211_is_action(frame->frame_control) && - (arg->flags & WSM_RX_STATUS_ADDRESS1)) + (arg->flags & WSM_RX_STATUS_ADDRESS1)) { if (cw1200_handle_action_rx(priv, skb)) return; + } else if (unlikely(priv->disable_beacon_filter) && + !arg->status && + ieee80211_is_beacon(frame->frame_control) && + !memcmp(ieee80211_get_SA(frame), priv->join_bssid, + ETH_ALEN)) { + priv->disable_beacon_filter = false; + queue_work(priv->workqueue, &priv->update_filtering_work); + } /* Stay awake for 1sec. after frame is received to give * userspace chance to react and acquire appropriate -- cgit v1.2.3 From bf884cb20fd7ce85b7a86365ad2d417d00b7af9c Mon Sep 17 00:00:00 2001 From: Stefan Nilsson XK Date: Wed, 29 Feb 2012 15:14:46 +0100 Subject: cw1200: Adds suspend/resume SDIO callbacks Adds SDIO suspend/resume notifications only. ST-Ericsson ID: 366640 Signed-off-by: Stefan Nilsson Change-Id: I3c251b8084731acd7b036757fccfdb26d9cac34f Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/38400 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/cw1200_sdio.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index dffa8879b93..e50aae5577f 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -369,11 +369,29 @@ static void cw1200_sdio_disconnect(struct sdio_func *func) } } +static int cw1200_suspend(struct device *dev) +{ + return 0; +} + +static int cw1200_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops cw1200_pm_ops = { + .suspend = cw1200_suspend, + .resume = cw1200_resume, +}; + static struct sdio_driver sdio_driver = { .name = "cw1200_wlan", .id_table = cw1200_sdio_ids, .probe = cw1200_sdio_probe, .remove = cw1200_sdio_disconnect, + .drv = { + .pm = &cw1200_pm_ops, + } }; /* Init Module function -> Called by insmod */ -- cgit v1.2.3 From 88b90e1122df39fc93de8d61bb89deb40cad3906 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:14:48 +0100 Subject: cw1200: fix initial rate settings for inverted ratesets. Fault description: 1. Minstrel sets rate chain as 48-54-6. 2. It is not supported by device and driver converts it to 54-48-6 and uploads the rate to firmware, but (wrongly) takes the initial transmission rate as 48. 3. Devices confirms TX at second attempt (6), but driver sends 48 to the rate control. Fix gets initial rate after rate control rules are getting reordered. ST-Ericsson ID: 355221 Change-Id: I335f4e8333520aa0d7f3b8259fffc58ca7e3137e Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/38264 Reviewed-by: QABUILD Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/txrx.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index db333afc444..f9065f474c6 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -645,6 +645,16 @@ cw1200_tx_h_rate_policy(struct cw1200_common *priv, { bool tx_policy_renew = false; + t->txpriv.rate_id = tx_policy_get(priv, + t->tx_info->control.rates, IEEE80211_TX_MAX_RATES, + &tx_policy_renew); + if (t->txpriv.rate_id == CW1200_INVALID_RATE_ID) + return -EFAULT; + + wsm->flags |= t->txpriv.rate_id << 4; + + t->rate = cw1200_get_tx_rate(priv, + &t->tx_info->control.rates[0]), wsm->maxTxRate = t->rate->hw_value; if (t->rate->flags & IEEE80211_TX_RC_MCS) { if (cw1200_ht_greenfield(&priv->ht_info)) @@ -655,13 +665,6 @@ cw1200_tx_h_rate_policy(struct cw1200_common *priv, __cpu_to_le32(WSM_HT_TX_MIXED); } - t->txpriv.rate_id = tx_policy_get(priv, - t->tx_info->control.rates, IEEE80211_TX_MAX_RATES, - &tx_policy_renew); - if (t->txpriv.rate_id == CW1200_INVALID_RATE_ID) - return -EFAULT; - wsm->flags |= t->txpriv.rate_id << 4; - if (tx_policy_renew) { tx_policy_printk(KERN_DEBUG "[TX] TX policy renew.\n"); /* It's not so optimal to stop TX queues every now and then. @@ -719,14 +722,12 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) u8 flags = 0; int ret; - t.rate = cw1200_get_tx_rate(priv, - &t.tx_info->control.rates[0]), t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control); t.da = ieee80211_get_DA(t.hdr); t.sta_priv = (struct cw1200_sta_priv *)&t.tx_info->control.sta->drv_priv; - if (WARN_ON(t.queue >= 4 || !t.rate)) + if (WARN_ON(t.queue >= 4)) goto drop; /* Temporary change*/ -- cgit v1.2.3 From c1024b0d885d5e0a1eb10c2b761d39a7def13e76 Mon Sep 17 00:00:00 2001 From: Lukasz Kucharczyk Date: Wed, 29 Feb 2012 15:14:49 +0100 Subject: cw1200: Send null frame after bss lost event. Some APs stops to send beacons. Null frame is being send afeter receiving bss lost event. This event is forwarded to upper layers only after delivery failure of this null frame (what means that AP is really gone). ST-Ericsson ID: 370409 ST-Ericsson FOSS-OUT ID: NA Change-Id: I4458fc447e7a939d2dc2db2786ea37503f856e29 Signed-off-by: Lukasz Kucharczyk Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/37585 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/cw1200.h | 10 ++++++++++ drivers/staging/cw1200/main.c | 1 + drivers/staging/cw1200/scan.c | 3 +++ drivers/staging/cw1200/sta.c | 35 +++++++++++++++++++++++++++++++++++ drivers/staging/cw1200/txrx.c | 15 +++++++++++++++ drivers/staging/cw1200/wsm.c | 11 ++++++++++- 6 files changed, 74 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 63ed796c6e6..a1a93ecfaf7 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -67,6 +67,13 @@ enum cw1200_link_status { CW1200_LINK_HARD, }; +enum cw1200_bss_loss_status { + CW1200_BSS_LOSS_NONE, + CW1200_BSS_LOSS_CHECKING, + CW1200_BSS_LOSS_CONFIRMING, + CW1200_BSS_LOSS_CONFIRMED, +}; + struct cw1200_link_entry { unsigned long timestamp; enum cw1200_link_status status; @@ -223,6 +230,9 @@ struct cw1200_common { struct delayed_work connection_loss_work; struct work_struct tx_failure_work; int delayed_link_loss; + spinlock_t bss_loss_lock; + int bss_loss_status; + int bss_loss_confirm_id; /* TX rate policy cache */ struct tx_policy_cache tx_policy_cache; diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index f194d5e61b8..0585f3dea9a 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -335,6 +335,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) INIT_DELAYED_WORK(&priv->bss_loss_work, cw1200_bss_loss_work); INIT_DELAYED_WORK(&priv->connection_loss_work, cw1200_connection_loss_work); + spin_lock_init(&priv->bss_loss_lock); INIT_WORK(&priv->tx_failure_work, cw1200_tx_failure_work); spin_lock_init(&priv->ps_state_lock); INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work); diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index 0d6a1c2f5b7..db4150fcd77 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -265,6 +265,9 @@ static void cw1200_scan_restart_delayed(struct cw1200_common *priv) wiphy_dbg(priv->hw->wiphy, "[CQM] Requeue BSS loss in %d " "beacons.\n", tmo); + spin_lock(&priv->bss_loss_lock); + priv->bss_loss_status = CW1200_BSS_LOSS_NONE; + spin_unlock(&priv->bss_loss_lock); cancel_delayed_work_sync(&priv->bss_loss_work); queue_delayed_work(priv->workqueue, &priv->bss_loss_work, diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index c2ef94bcc58..9ea92f183c1 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -891,6 +891,14 @@ void cw1200_event_handler(struct work_struct *work) break; case WSM_EVENT_BSS_LOST: { + spin_lock(&priv->bss_loss_lock); + if (priv->bss_loss_status > CW1200_BSS_LOSS_NONE) { + spin_unlock(&priv->bss_loss_lock); + break; + } + priv->bss_loss_status = CW1200_BSS_LOSS_CHECKING; + spin_unlock(&priv->bss_loss_lock); + sta_printk(KERN_DEBUG "[CQM] BSS lost.\n"); cancel_delayed_work_sync(&priv->bss_loss_work); cancel_delayed_work_sync(&priv->connection_loss_work); @@ -913,6 +921,9 @@ void cw1200_event_handler(struct work_struct *work) { sta_printk(KERN_DEBUG "[CQM] BSS regained.\n"); priv->delayed_link_loss = 0; + spin_lock(&priv->bss_loss_lock); + priv->bss_loss_status = CW1200_BSS_LOSS_NONE; + spin_unlock(&priv->bss_loss_lock); cancel_delayed_work_sync(&priv->bss_loss_work); cancel_delayed_work_sync(&priv->connection_loss_work); break; @@ -947,10 +958,30 @@ void cw1200_bss_loss_work(struct work_struct *work) struct cw1200_common *priv = container_of(work, struct cw1200_common, bss_loss_work.work); int timeout; /* in beacons */ + struct sk_buff *skb; timeout = priv->cqm_link_loss_count - priv->cqm_beacon_loss_count; + spin_lock(&priv->bss_loss_lock); + if (priv->bss_loss_status == CW1200_BSS_LOSS_CHECKING) { + spin_unlock(&priv->bss_loss_lock); + skb = ieee80211_nullfunc_get(priv->hw, priv->vif); + if (!(WARN_ON(!skb))) { + cw1200_tx(priv->hw, skb); + /* Start watchdog -- if nullfunc TX doesn't fail + * in 1 sec, forward event to upper layers */ + queue_delayed_work(priv->workqueue, + &priv->bss_loss_work, 1 * HZ); + } + return; + } else if (priv->bss_loss_status == CW1200_BSS_LOSS_CONFIRMING) { + priv->bss_loss_status = CW1200_BSS_LOSS_NONE; + spin_unlock(&priv->bss_loss_lock); + return; + } + spin_unlock(&priv->bss_loss_lock); + if (priv->cqm_beacon_loss_count) { sta_printk(KERN_DEBUG "[CQM] Beacon loss.\n"); if (timeout <= 0) @@ -966,6 +997,10 @@ void cw1200_bss_loss_work(struct work_struct *work) queue_delayed_work(priv->workqueue, &priv->connection_loss_work, timeout * HZ / 10); + + spin_lock(&priv->bss_loss_lock); + priv->bss_loss_status = CW1200_BSS_LOSS_NONE; + spin_unlock(&priv->bss_loss_lock); } void cw1200_connection_loss_work(struct work_struct *work) diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index f9065f474c6..8b921804247 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -914,6 +914,20 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, cw1200_debug_txed_agg(priv); } } else { + spin_lock(&priv->bss_loss_lock); + if (priv->bss_loss_status == + CW1200_BSS_LOSS_CONFIRMING && + priv->bss_loss_confirm_id == + arg->packetID) { + priv->bss_loss_status = + CW1200_BSS_LOSS_CONFIRMED; + spin_unlock(&priv->bss_loss_lock); + cancel_delayed_work(&priv->bss_loss_work); + queue_delayed_work(priv->workqueue, + &priv->bss_loss_work, 0); + } else + spin_unlock(&priv->bss_loss_lock); + /* TODO: Update TX failure counters */ if (unlikely(priv->cqm_tx_failure_thold && (++priv->cqm_tx_failure_count > @@ -941,6 +955,7 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, tx->status.rates[i].idx = -1; } + cw1200_queue_remove(queue, arg->packetID); } } diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 20d4c4b619d..b1a2a9ecc6a 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1332,7 +1332,16 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv, switch (priv->mode) { case NL80211_IFTYPE_STATION: - if (unlikely( + if (unlikely((priv->join_status == CW1200_JOIN_STATUS_STA) && + ieee80211_is_nullfunc(fctl))) { + spin_lock(&priv->bss_loss_lock); + if (priv->bss_loss_status == CW1200_BSS_LOSS_CHECKING) { + priv->bss_loss_status = + CW1200_BSS_LOSS_CONFIRMING; + priv->bss_loss_confirm_id = wsm->packetID; + } + spin_unlock(&priv->bss_loss_lock); + } else if (unlikely( (priv->join_status <= CW1200_JOIN_STATUS_MONITOR) || memcmp(frame->addr1, priv->join_bssid, sizeof(priv->join_bssid)))) { -- cgit v1.2.3 From 561cefca24c66af926b3aa5be69ab99c10e9dfac Mon Sep 17 00:00:00 2001 From: Marek Kwaczynski Date: Wed, 29 Feb 2012 15:14:50 +0100 Subject: cw1200: Added handling Protected Mgmt Policy Protected Mgmnt Policy is needed to disable encryption for authentication frames by the firmware. ST-Ericsson ID: 364367 ST-Ericsson FOSS-OUT ID: NA Change-Id: Icd34af6ff9cc06866d25a7870125fb2f87e11bf4 Signed-off-by: Marek Kwaczynski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/38322 Reviewed-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/sta.c | 7 +++++++ drivers/staging/cw1200/wsm.h | 26 +++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 9ea92f183c1..33856e6fc43 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1208,6 +1208,7 @@ void cw1200_join_work(struct work_struct *work) const u8 *ssidie; const u8 *dtimie; const struct ieee80211_tim_ie *tim = NULL; + struct wsm_protected_mgmt_policy mgmt_policy; BUG_ON(queueId >= 4); if (cw1200_queue_get_skb(queue, priv->pending_frame_id, @@ -1310,6 +1311,12 @@ void cw1200_join_work(struct work_struct *work) /* BlockACK policy will be updated when assoc is done */ WARN_ON(wsm_set_block_ack_policy(priv, 0, 0)); + + mgmt_policy.protectedMgmtEnable = 0; + mgmt_policy.unprotectedMgmtFramesAllowed = 1; + mgmt_policy.encryptionForAuthFrame = 1; + wsm_set_protected_mgmt_policy(priv, &mgmt_policy); + if (wsm_join(priv, &join)) { memset(&priv->join_bssid[0], 0, sizeof(priv->join_bssid)); diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 61c1d5b8a02..0915b32bf96 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -495,7 +495,8 @@ struct cw1200_common; /* 4.37 GroupTxSequenceCounter */ #define WSM_MIB_ID_GRP_SEQ_COUNTER 0x101F -/* 1020 4.38 ProtectedMgmtPolicy */ +/* 4.38 ProtectedMgmtPolicy */ +#define WSM_MIB_ID_PROTECTED_MGMT_POLICY 0x1020 /* 4.39 SetHtProtection */ #define WSM_MID_ID_SET_HT_PROTECTION 0x1021 @@ -1428,6 +1429,29 @@ static inline int wsm_set_template_frame(struct cw1200_common *priv, return ret; } + +struct wsm_protected_mgmt_policy { + bool protectedMgmtEnable; + bool unprotectedMgmtFramesAllowed; + bool encryptionForAuthFrame; +}; + +static inline int wsm_set_protected_mgmt_policy(struct cw1200_common *priv, + struct wsm_protected_mgmt_policy *arg) +{ + __le32 val = 0; + int ret; + if (arg->protectedMgmtEnable) + val |= __cpu_to_le32(BIT(0)); + if (arg->unprotectedMgmtFramesAllowed) + val |= __cpu_to_le32(BIT(1)); + if (arg->encryptionForAuthFrame) + val |= __cpu_to_le32(BIT(2)); + ret = wsm_write_mib(priv, WSM_MIB_ID_PROTECTED_MGMT_POLICY, + &val, sizeof(val)); + return ret; +} + static inline int wsm_set_block_ack_policy(struct cw1200_common *priv, u8 blockAckTxTidPolicy, u8 blockAckRxTidPolicy) -- cgit v1.2.3 From f3a31229c12f9588a709ce214696cf8dbefdc88c Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 29 Feb 2012 15:14:51 +0100 Subject: cw1200: Adds 4byte alignment for 16bit read/writes This is moreless workaround for mmci 4byte alignment restrictions. ST-Ericsson ID: 373107 ST-Ericsson FOSS-OUT ID: NA Change-Id: Ie4ba4bb9f01109599164fc3ef35aee1a8f0dbbc4 Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39076 Reviewed-by: QATOOLS --- drivers/staging/cw1200/bh.c | 2 +- drivers/staging/cw1200/hwio.h | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index 051cb97df00..c4a880a06a1 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -265,7 +265,7 @@ static int cw1200_bh(void *arg) int tx_burst; int rx_burst = 0; long status; - u8 dummy; + u32 dummy; for (;;) { if (!priv->hw_bufs_used diff --git a/drivers/staging/cw1200/hwio.h b/drivers/staging/cw1200/hwio.h index 427b75e0523..25c8f6b4c28 100644 --- a/drivers/staging/cw1200/hwio.h +++ b/drivers/staging/cw1200/hwio.h @@ -177,13 +177,18 @@ int cw1200_reg_write(struct cw1200_common *priv, u16 addr, static inline int cw1200_reg_read_16(struct cw1200_common *priv, u16 addr, u16 *val) { - return cw1200_reg_read(priv, addr, val, sizeof(val)); + u32 bigVal; + int ret; + ret = cw1200_reg_read(priv, addr, &bigVal, sizeof(bigVal)); + *val = (u16)bigVal; + return ret; } static inline int cw1200_reg_write_16(struct cw1200_common *priv, u16 addr, u16 val) { - return cw1200_reg_write(priv, addr, &val, sizeof(val)); + u32 bigVal = (u32)val; + return cw1200_reg_write(priv, addr, &bigVal, sizeof(bigVal)); } static inline int cw1200_reg_read_32(struct cw1200_common *priv, -- cgit v1.2.3 From cf1aa19048fba67a8dcdf9b2f1164cbb10c5c8b4 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 29 Feb 2012 15:14:52 +0100 Subject: cw1200: Remove use of mmc_host_enable|disable for sdio An sdio func driver should not use these functions since it will mess up the internal power state for the sdio host. Moreover it will preventing the sdio host from doing suspend properly. ST-Ericsson ID: 356920 ST-Ericsson FOSS-OUT ID: NA Change-Id: I9fed799b97c84d74cf78a8b5248771bbe229ef9e Signed-off-by: Ulf Hansson Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39202 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/cw1200_sdio.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index e50aae5577f..670ba935462 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -284,18 +284,10 @@ int cw1200_sdio_set_block_size(struct sbus_priv *self, size_t size) static int cw1200_sdio_pm(struct sbus_priv *self, bool suspend) { - int ret; + int ret = 0; const struct resource *irq = self->pdata->irq; - struct sdio_func *func = self->func; - - sdio_claim_host(func); - if (suspend) - ret = mmc_host_disable(func->card->host); - else - ret = mmc_host_enable(func->card->host); - sdio_release_host(func); - if (!ret && irq) + if (irq) ret = irq_set_irq_wake(irq->start, suspend); return ret; -- cgit v1.2.3 From ce0fce4d8b7df2332fa3ed8323f9c6378c2376ea Mon Sep 17 00:00:00 2001 From: Lukasz Kucharczyk Date: Wed, 29 Feb 2012 15:14:53 +0100 Subject: cw1200: enable non-ERP protection. Non-ERP protection is enabled and relevant bit is set in ERP information element upon detection of non-ERP station nearby. ST-Ericsson ID: 374909 Depends-on: I16ca7675d47361246145c7924af4c6fe3601f9ab Depends-on: Ib788fdcb89b14e8ae0f20f8bf1da9b625c4912b8 Change-Id: Ie1932612e52d6ba26882f30bb45211202cab1393 Signed-off-by: Lukasz Kucharczyk Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39121 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/ap.c | 41 +++++++++++++++++++++++++++++++++++------ drivers/staging/cw1200/ap.h | 1 + drivers/staging/cw1200/cw1200.h | 2 ++ drivers/staging/cw1200/main.c | 1 + drivers/staging/cw1200/sta.c | 6 +++++- 5 files changed, 44 insertions(+), 7 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 5b5e5094953..f58ccb13205 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -213,6 +213,38 @@ int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, return 0; } +void cw1200_set_cts_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, set_cts_work.work); + + struct ieee80211_mgmt *mgmt; + u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0}; + struct wsm_update_ie update_ie = { + .what = WSM_UPDATE_IE_BEACON, + .count = 1, + .ies = erp_ie, + .length = 3, + }; + __le32 use_cts_prot = priv->use_cts_prot ? + __cpu_to_le32(1) : 0; + + mutex_lock(&priv->conf_mutex); + if (priv->use_cts_prot) + erp_ie[ERP_INFO_BYTE_OFFSET] |= WLAN_ERP_USE_PROTECTION; + else + erp_ie[ERP_INFO_BYTE_OFFSET] &= ~(WLAN_ERP_USE_PROTECTION); + mutex_unlock(&priv->conf_mutex); + + ap_printk(KERN_DEBUG "[STA] CTS protection %d\n", use_cts_prot); + + WARN_ON(wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION, + &use_cts_prot, sizeof(use_cts_prot))); + WARN_ON(wsm_update_ie(priv, &update_ie)); + + return; +} + static int cw1200_set_btcoexinfo(struct cw1200_common *priv) { struct wsm_override_internal_txrate arg; @@ -495,13 +527,10 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, } } if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT)) { - __le32 use_cts_prot = info->use_cts_prot ? - __cpu_to_le32(1) : 0; - ap_printk(KERN_DEBUG "[STA] CTS protection %d\n", - info->use_cts_prot); - WARN_ON(wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION, - &use_cts_prot, sizeof(use_cts_prot))); + priv->use_cts_prot = info->use_cts_prot; + + queue_delayed_work(priv->workqueue, &priv->set_cts_work, 0*HZ); } if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) { __le32 slot_time = info->use_short_slot ? diff --git a/drivers/staging/cw1200/ap.h b/drivers/staging/cw1200/ap.h index a9e85bd5516..2175a82d2a2 100644 --- a/drivers/staging/cw1200/ap.h +++ b/drivers/staging/cw1200/ap.h @@ -34,6 +34,7 @@ int cw1200_ampdu_action(struct ieee80211_hw *hw, void cw1200_suspend_resume(struct cw1200_common *priv, struct wsm_suspend_resume *arg); void cw1200_set_tim_work(struct work_struct *work); +void cw1200_set_cts_work(struct work_struct *work); void cw1200_multicast_start_work(struct work_struct *work); void cw1200_multicast_stop_work(struct work_struct *work); void cw1200_mcast_timeout(unsigned long arg); diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index a1a93ecfaf7..0bdd3a65406 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -155,6 +155,7 @@ struct cw1200_common { bool is_BT_Present; u8 conf_listen_interval; u32 listen_interval; + u32 use_cts_prot; /* BH */ atomic_t bh_rx; @@ -217,6 +218,7 @@ struct cw1200_common { bool buffered_multicasts; bool tx_multicast; struct work_struct set_tim_work; + struct delayed_work set_cts_work; struct work_struct multicast_start_work; struct work_struct multicast_stop_work; struct timer_list mcast_timeout; diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 0585f3dea9a..4081a3d4fe2 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -338,6 +338,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) spin_lock_init(&priv->bss_loss_lock); INIT_WORK(&priv->tx_failure_work, cw1200_tx_failure_work); spin_lock_init(&priv->ps_state_lock); + INIT_DELAYED_WORK(&priv->set_cts_work, cw1200_set_cts_work); INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work); INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work); INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work); diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 33856e6fc43..5f431a0e2de 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -74,7 +74,7 @@ int cw1200_start(struct ieee80211_hw *dev) priv->cqm_beacon_loss_count = 20; /* Temporary configuration - beacon filter table */ - priv->bf_table.numOfIEs = __cpu_to_le32(1); + priv->bf_table.numOfIEs = __cpu_to_le32(2); priv->bf_table.entry[0].ieId = WLAN_EID_VENDOR_SPECIFIC; priv->bf_table.entry[0].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED | WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | @@ -82,6 +82,10 @@ int cw1200_start(struct ieee80211_hw *dev) priv->bf_table.entry[0].oui[0] = 0x50; priv->bf_table.entry[0].oui[1] = 0x6F; priv->bf_table.entry[0].oui[2] = 0x9A; + priv->bf_table.entry[1].ieId = WLAN_EID_ERP_INFO; + priv->bf_table.entry[1].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED; priv->bf_control.enabled = 1; ret = cw1200_setup_mac(priv); -- cgit v1.2.3 From 3b60c6178c1b44e01aebe2139b1187719a3b7cbd Mon Sep 17 00:00:00 2001 From: "Ajitpal.Singh" Date: Wed, 29 Feb 2012 15:14:55 +0100 Subject: cw1200: Refactor workaround for WFD 6.1.10 Refactoring code for WFD 6.1.10 workaround. Now if an ACTION frame is received from a mapped MAC address then reset the link and re-map it. If ACTION frame is received from an unmapped MAC address then map the MAC addreess and reset it ST-Ericsson ID: 372706 ST-Ericsson FOSS-OUT ID: NA Signed-off-by: Ajitpal.Singh Change-Id: I8ea4fd6368a6bf5a23273ff25ddca3d0c589ec9a Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39432 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/cw1200.h | 1 + drivers/staging/cw1200/txrx.c | 73 ++++++++++++++++++++++++++++------------- 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 0bdd3a65406..8ee88b64e38 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -251,6 +251,7 @@ struct cw1200_common { /* Workaround for WFD testcase 6.1.10*/ struct work_struct linkid_reset_work; u8 action_frame_sa[ETH_ALEN]; + u8 action_linkid; #endif }; diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 8b921804247..1931e3ef88c 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -730,15 +730,9 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) if (WARN_ON(t.queue >= 4)) goto drop; - /* Temporary change*/ - if (unlikely(ieee80211_is_probe_resp(t.hdr->frame_control))) { - t.txpriv.raw_link_id = - t.txpriv.link_id = 0; - } else { - ret = cw1200_tx_h_calc_link_ids(priv, &t); - if (ret) - goto drop; - } + ret = cw1200_tx_h_calc_link_ids(priv, &t); + if (ret) + goto drop; txrx_printk(KERN_DEBUG "[TX] TX %d bytes " "(queue: %d, link_id: %d (%d)).\n", @@ -1035,7 +1029,26 @@ void cw1200_rx_cb(struct cw1200_common *priv, && ieee80211_is_action(frame->frame_control) && (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { txrx_printk(KERN_DEBUG "[RX] Going to MAP&RESET link ID\n"); - memcpy(&priv->action_frame_sa[0], ieee80211_get_SA(frame), ETH_ALEN); + + if (work_pending(&priv->linkid_reset_work)) + WARN_ON(1); + + memcpy(&priv->action_frame_sa[0], + ieee80211_get_SA(frame), ETH_ALEN); + priv->action_linkid = 0; + schedule_work(&priv->linkid_reset_work); + } + + if (arg->link_id && (priv->vif->p2p == WSM_START_MODE_P2P_GO) + && ieee80211_is_action(frame->frame_control) + && (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { + /* Link ID already exists for the ACTION frame. + * Reset and Remap */ + if (work_pending(&priv->linkid_reset_work)) + WARN_ON(1); + memcpy(&priv->action_frame_sa[0], + ieee80211_get_SA(frame), ETH_ALEN); + priv->action_linkid = arg->link_id; schedule_work(&priv->linkid_reset_work); } #endif @@ -1225,21 +1238,37 @@ void cw1200_link_id_reset(struct work_struct *work) struct cw1200_common *priv = container_of(work, struct cw1200_common, linkid_reset_work); int temp_linkid; - - /* In GO mode we can receive ACTION frames without a linkID */ - temp_linkid = cw1200_alloc_link_id(priv, &priv->action_frame_sa[0]); - WARN_ON(!temp_linkid); - if (temp_linkid) { - /* Make sure we execute the WQ */ - flush_workqueue(priv->workqueue); - /* Release the link ID */ + struct cw1200_link_entry *entry; + + if (!priv->action_linkid) { + /* In GO mode we can receive ACTION frames without a linkID */ + temp_linkid = cw1200_alloc_link_id(priv, + &priv->action_frame_sa[0]); + WARN_ON(!temp_linkid); + if (temp_linkid) { + /* Make sure we execute the WQ */ + flush_workqueue(priv->workqueue); + /* Release the link ID */ + spin_lock(&priv->ps_state_lock); + priv->link_id_db[temp_linkid - 1].status = + CW1200_LINK_RESERVE; + spin_unlock(&priv->ps_state_lock); + wsm_lock_tx_async(priv); + if (queue_work(priv->workqueue, + &priv->link_id_work) <= 0) + wsm_unlock_tx(priv); + } + } else { spin_lock(&priv->ps_state_lock); - priv->link_id_db[temp_linkid - 1].status = - CW1200_LINK_RESERVE; - spin_unlock(&priv->ps_state_lock); + entry = &priv->link_id_db[priv->action_linkid - 1]; wsm_lock_tx_async(priv); if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) - wsm_unlock_tx(priv); + wsm_unlock_tx(priv); + spin_unlock(&priv->ps_state_lock); + flush_workqueue(priv->workqueue); + temp_linkid = cw1200_alloc_link_id(priv, + &priv->action_frame_sa[0]); + WARN_ON(!temp_linkid); } } #endif -- cgit v1.2.3 From 4b342aae42b72bec87d77ae09c01f38bf6b985a3 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:14:56 +0100 Subject: cw1200: Enable beacon skipping. Beacon skipping is implemented to reduce power consumption in suspend state. It is enabled in STA mode when there is no active milticast subscriptions. Up to 2 beacons can be skipped in a row. ST-Ericsson ID: 373856 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Idd47937a21d7e2e188e0081d937770181f1f3f0e Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39312 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/cw1200.h | 1 + drivers/staging/cw1200/pm.c | 20 ++++++++++++++++++++ drivers/staging/cw1200/sta.c | 10 ++++++++++ 3 files changed, 31 insertions(+) diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 8ee88b64e38..d3ecb2a950f 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -145,6 +145,7 @@ struct cw1200_common { struct wsm_beacon_filter_table bf_table; struct wsm_beacon_filter_control bf_control; struct wsm_multicast_filter multicast_filter; + bool has_multicast_subscription; bool disable_beacon_filter; struct work_struct update_filtering_work; u8 ba_tid_mask; diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c index 7c1a957de3e..9eece65a709 100644 --- a/drivers/staging/cw1200/pm.c +++ b/drivers/staging/cw1200/pm.c @@ -16,6 +16,8 @@ #include "bh.h" #include "sbus.h" +#define CW1200_BEACON_SKIPPING_MULTIPLIER 3 + static int cw1200_suspend_late(struct device *dev); static void cw1200_pm_release(struct device *dev); static int cw1200_pm_probe(struct platform_device *pdev); @@ -27,6 +29,7 @@ struct cw1200_suspend_state { unsigned long join_tmo; unsigned long direct_probe; unsigned long link_id_gc; + bool beacon_skipping; }; static const struct dev_pm_ops cw1200_pm_ops = { @@ -252,6 +255,17 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) state->link_id_gc = cw1200_suspend_work(&priv->link_id_gc_work); + /* Enable beacon skipping */ + if (priv->join_status == CW1200_JOIN_STATUS_STA + && priv->join_dtim_period + && !priv->has_multicast_subscription) { + state->beacon_skipping = true; + wsm_set_beacon_wakeup_period(priv, + priv->join_dtim_period, + CW1200_BEACON_SKIPPING_MULTIPLIER * + priv->join_dtim_period); + } + /* Stop serving thread */ if (cw1200_bh_suspend(priv)) goto revert4; @@ -318,6 +332,12 @@ int cw1200_wow_resume(struct ieee80211_hw *hw) /* Resume BH thread */ WARN_ON(cw1200_bh_resume(priv)); + if (state->beacon_skipping) { + wsm_set_beacon_wakeup_period(priv, + priv->join_dtim_period, 0); + state->beacon_skipping = false; + } + /* Resume delayed work */ cw1200_resume_work(priv, &priv->bss_loss_work, state->bss_loss_tmo); diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 5f431a0e2de..7a0830e5410 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -462,11 +462,18 @@ void cw1200_update_filtering_work(struct work_struct *work) u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list) { + static u8 broadcast_ipv6[ETH_ALEN] = { + 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 + }; + static u8 broadcast_ipv4[ETH_ALEN] = { + 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01 + }; struct cw1200_common *priv = hw->priv; struct netdev_hw_addr *ha; int count = 0; /* Disable multicast filtering */ + priv->has_multicast_subscription = false; memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter)); if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES) @@ -477,6 +484,9 @@ u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, sta_printk(KERN_DEBUG "[STA] multicast: %pM\n", ha->addr); memcpy(&priv->multicast_filter.macAddress[count], ha->addr, ETH_ALEN); + if (memcmp(ha->addr, broadcast_ipv4, ETH_ALEN) && + memcmp(ha->addr, broadcast_ipv6, ETH_ALEN)) + priv->has_multicast_subscription = true; count++; } -- cgit v1.2.3 From b6c59a7ecd7bdbd5006ab3b6f183a42b6de1bd99 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 29 Feb 2012 15:14:57 +0100 Subject: cw1200: Change WARN_ON to WARN_ON_ONCE For sanity check of cw1200 capabilites. ST-Ericsson ID: 375600 Change-Id: I1742bf6b2b1aa816541c11ec53c53c52dafd53c3 Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39846 --- drivers/staging/cw1200/bh.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index c4a880a06a1..4b678f40c40 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -368,6 +368,12 @@ rx: alloc_len += SDIO_BLOCK_SIZE; #endif /* CONFIG_CW1200_NON_POWER_OF_TWO_BLOCKSIZES */ + /* Check if not exceeding CW1200 capabilities */ + if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) { + printk(KERN_DEBUG "Read aligned len: %d\n", + alloc_len); + } + skb_rx = cw1200_get_skb(priv, alloc_len); if (WARN_ON(!skb_rx)) break; @@ -494,6 +500,14 @@ tx: } #endif /* CONFIG_CW1200_NON_POWER_OF_TWO_BLOCKSIZES */ + /* Check if not exceeding CW1200 + capabilities */ + if (WARN_ON_ONCE( + tx_len > EFFECTIVE_BUF_SIZE)) { + printk(KERN_DEBUG "Write aligned len:" + " %d\n", tx_len); + } + wsm->id &= __cpu_to_le32( ~WSM_TX_SEQ(WSM_TX_SEQ_MAX)); wsm->id |= cpu_to_le32( -- cgit v1.2.3 From 7ab8125854816a8babdb29402c945ecd50253ec3 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 29 Feb 2012 15:14:59 +0100 Subject: cw1200: Make use of KEEP_POWER Sets the SDIO pm flag KEEP_POWER to notify the SDIO framework that this device will keep its power during this suspend cycle. ST-Ericsson ID: 356920 ST-Ericsson FOSS-OUT ID: NA Change-Id: Idd1b0758316f409f800e350b8301aaabe0facbe0 Signed-off-by: Ulf Hansson Signed-off-by: Stefan Nilsson XK Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39639 Reviewed-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/cw1200_sdio.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index 670ba935462..6f3eabd3967 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -363,7 +363,16 @@ static void cw1200_sdio_disconnect(struct sdio_func *func) static int cw1200_suspend(struct device *dev) { - return 0; + int ret; + struct sdio_func *func = dev_to_sdio_func(dev); + + /* Notify SDIO that CW1200 will remain powered during suspend */ + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) + cw1200_dbg(CW1200_DBG_ERROR, + "Error setting SDIO pm flags: %i\n", ret); + + return ret; } static int cw1200_resume(struct device *dev) -- cgit v1.2.3 From bba7f358c54359c97d43802f09e3effdd4e24393 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:15:00 +0100 Subject: cw1200: Enable block ACK in RX direction. BlockAckRxTidPolicy should be setup before connection and not changed. BlockAckRxTidPolicy will affect how the FW reacts when it receives ADDBA from the peer device (i.e. It will effect receive aggregation). Receive cannot be delayed as once the FW rejects ADDBA from the peer it will not re-try. ST-Ericsson ID: 399657 Change-Id: I6cf0306a14a35d1b9d058389ae386a5230897ed5 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39933 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/sta.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 7a0830e5410..594be1cd5c8 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1324,7 +1324,7 @@ void cw1200_join_work(struct work_struct *work) cw1200_update_listening(priv, false); /* BlockACK policy will be updated when assoc is done */ WARN_ON(wsm_set_block_ack_policy(priv, - 0, 0)); + 0, priv->ba_tid_mask)); mgmt_policy.protectedMgmtEnable = 0; mgmt_policy.unprotectedMgmtFramesAllowed = 1; @@ -1413,7 +1413,7 @@ void cw1200_unjoin_work(struct work_struct *work) cancel_delayed_work_sync(&priv->connection_loss_work); cw1200_update_listening(priv, priv->listening); WARN_ON(wsm_set_block_ack_policy(priv, - 0, 0)); + 0, priv->ba_tid_mask)); priv->disable_beacon_filter = false; cw1200_update_filtering(priv); priv->setbssparams_done = false; -- cgit v1.2.3 From f5077ac0fc9abff31bd536b1353ac57a17f05b45 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:15:01 +0100 Subject: cw1200: Block consecurive calls to the wsm_configuration Consecutive calls to wsm_configuration are not supported by firmware and can cause problems. ST-Ericsson ID: 399837 Change-Id: I9dacc2a6aa962368c50d062d45ca0b2f7c262c9b Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39935 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/sta.c | 55 +++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 594be1cd5c8..43d7843e417 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1103,10 +1103,6 @@ static int cw1200_parse_SDD_file(struct cw1200_common *priv) int cw1200_setup_mac(struct cw1200_common *priv) { - /* TBD: Do you know how to assing MAC address without - * annoying uploading RX data? */ - u8 prev_mac[ETH_ALEN]; - /* NOTE: There is a bug in FW: it reports signal * as RSSI if RSSI subscription is enabled. * It's not enough to set WSM_RCPI_RSSI_USE_RSSI. */ @@ -1119,40 +1115,37 @@ int cw1200_setup_mac(struct cw1200_common *priv) }; int ret = 0; - if (wsm_get_station_id(priv, &prev_mac[0]) - || memcmp(prev_mac, priv->mac_addr, ETH_ALEN)) { + if (!priv->sdd) { const char *sdd_path = NULL; struct wsm_configuration cfg = { .dot11StationId = &priv->mac_addr[0], }; - if (!priv->sdd) { - switch (priv->hw_revision) { - case CW1200_HW_REV_CUT10: - sdd_path = SDD_FILE_10; - break; - case CW1200_HW_REV_CUT11: - sdd_path = SDD_FILE_11; - break; - case CW1200_HW_REV_CUT20: - sdd_path = SDD_FILE_20; - break; - case CW1200_HW_REV_CUT22: - sdd_path = SDD_FILE_22; - break; - default: - BUG_ON(1); - } + switch (priv->hw_revision) { + case CW1200_HW_REV_CUT10: + sdd_path = SDD_FILE_10; + break; + case CW1200_HW_REV_CUT11: + sdd_path = SDD_FILE_11; + break; + case CW1200_HW_REV_CUT20: + sdd_path = SDD_FILE_20; + break; + case CW1200_HW_REV_CUT22: + sdd_path = SDD_FILE_22; + break; + default: + BUG_ON(1); + } - ret = request_firmware(&priv->sdd, - sdd_path, priv->pdev); + ret = request_firmware(&priv->sdd, + sdd_path, priv->pdev); - if (unlikely(ret)) { - cw1200_dbg(CW1200_DBG_ERROR, - "%s: can't load sdd file %s.\n", - __func__, sdd_path); - return ret; - } + if (unlikely(ret)) { + cw1200_dbg(CW1200_DBG_ERROR, + "%s: can't load sdd file %s.\n", + __func__, sdd_path); + return ret; } cfg.dpdData = priv->sdd->data; -- cgit v1.2.3 From 8ce53620a98111d7364adc85df5d3b3bfb24af9e Mon Sep 17 00:00:00 2001 From: Pawel Kulakowski Date: Wed, 29 Feb 2012 15:15:02 +0100 Subject: cw1200: Fixed unregistering order in cw1200_unregister_common In cw1200_unregister_common function cw1200_debug_release should be called before ieee80211_unregister_hw. ST-Ericsson ID: 399014 Change-Id: I1145b9425a8ba5a2153c1fea076b90f1ddcf651a Signed-off-by: Pawel Kulakowski Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39852 --- drivers/staging/cw1200/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 4081a3d4fe2..4ca98d2f777 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -428,10 +428,10 @@ void cw1200_unregister_common(struct ieee80211_hw *dev) struct cw1200_common *priv = dev->priv; int i; - ieee80211_unregister_hw(dev); - cw1200_debug_release(priv); + ieee80211_unregister_hw(dev); + priv->sbus_ops->irq_unsubscribe(priv->sbus_priv); cw1200_unregister_bh(priv); -- cgit v1.2.3 From 7cb93f57126b23f8c4bba967ed47d59b63c06652 Mon Sep 17 00:00:00 2001 From: Tomasz Hliwiak Date: Wed, 29 Feb 2012 15:15:03 +0100 Subject: cw1200: Always update template on scan request. Send probe request template to FW before each scan. It corrects wrong destination address in probe requests after p2p_connect in sta mode. ST-Ericsson ID: 364811 ST-Ericsson FOSS-OUT ID: NA Signed-off-by: Tomasz Hliwiak Change-Id: I90cbba678a916390f035c5664e25ea13428541cf Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39816 Tested-by: Tomasz HLIWIAK Reviewed-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/main.c | 3 --- drivers/staging/cw1200/scan.c | 21 ++++----------------- drivers/staging/cw1200/scan.h | 2 -- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 4ca98d2f777..7a2e12d23a7 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -443,9 +443,6 @@ void cw1200_unregister_common(struct ieee80211_hw *dev) wsm_buf_deinit(&priv->wsm_cmd_buf); - kfree(priv->scan.ie); - priv->scan.ie = NULL; - priv->scan.ie_len = 0; destroy_workqueue(priv->workqueue); priv->workqueue = NULL; diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index db4150fcd77..32faf2b1c3f 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -64,23 +64,10 @@ int cw1200_hw_scan(struct ieee80211_hw *hw, if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS) return -EINVAL; - if (req->ie_len != priv->scan.ie_len || - memcmp(req->ie, priv->scan.ie, req->ie_len)) { - frame.skb = ieee80211_probereq_get(hw, priv->vif, NULL, 0, - req->ie, req->ie_len); - if (!frame.skb) - return -ENOMEM; - kfree(priv->scan.ie); - priv->scan.ie = NULL; - priv->scan.ie_len = 0; - if (req->ie_len) { - priv->scan.ie = kmalloc(req->ie_len, GFP_KERNEL); - if (priv->scan.ie) { - memcpy(priv->scan.ie, req->ie, req->ie_len); - priv->scan.ie_len = req->ie_len; - } - } - } + frame.skb = ieee80211_probereq_get(hw, priv->vif, NULL, 0, + req->ie, req->ie_len); + if (!frame.skb) + return -ENOMEM; /* will be unlocked in cw1200_scan_work() */ down(&priv->scan.lock); diff --git a/drivers/staging/cw1200/scan.h b/drivers/staging/cw1200/scan.h index fd59123e742..abffd1d796f 100644 --- a/drivers/staging/cw1200/scan.h +++ b/drivers/staging/cw1200/scan.h @@ -30,8 +30,6 @@ struct cw1200_scan { struct ieee80211_channel **curr; struct ieee80211_channel **end; struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS]; - u8 *ie; - size_t ie_len; int output_power; int n_ssids; int status; -- cgit v1.2.3 From 1be9f1fb645f302f42eab10b6645e05f60751be6 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:15:04 +0100 Subject: cw1200: Add RCU protection for ieee80211_sta * Add RCU protection for ieee80211_sta * Do not call ieee80211_sta_set_buffered when STA is not set. ST-Ericsson ID: 399337, 368121, 374076 Change-Id: I4bbe397a5418cff60d91458192c3d112a6424662 Signed-off-by: Dmitry Tarnyagin Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/40346 --- drivers/staging/cw1200/txrx.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 1931e3ef88c..0d535f797a8 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -717,6 +717,7 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) .txpriv.tid = CW1200_MAX_TID, .txpriv.rate_id = CW1200_INVALID_RATE_ID, }; + struct ieee80211_sta *sta; struct wsm_tx *wsm; bool tid_update = 0; u8 flags = 0; @@ -761,6 +762,9 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) if (ret) goto drop; + rcu_read_lock(); + sta = rcu_dereference(t.tx_info->control.sta); + spin_lock_bh(&priv->ps_state_lock); { tid_update = cw1200_tx_h_pm_state(priv, &t); @@ -770,11 +774,13 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) spin_unlock_bh(&priv->ps_state_lock); #if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) - if (tid_update) - ieee80211_sta_set_buffered(t.tx_info->control.sta, + if (tid_update && sta) + ieee80211_sta_set_buffered(sta, t.txpriv.tid, true); #endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ + rcu_read_unlock(); + cw1200_bh_wakeup(priv); return; -- cgit v1.2.3 From a69221ed6ed3da38bcd24dd96a21ac2ffb12582c Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:15:05 +0100 Subject: cw1200: Fixes for multicast delivery in AP/UAPSD mode. * Powersave state of UAPSD STA must be explicitly forced to "PS", mac80211 stack provides no PS notification for these STAs. * Mcast timeout was wrongly triggered due to wrongly used mod_timer call. * tx_multicast flag was not reset in case of successful mcast delivery. * FW may requeue a multicast frame. To deliver the frame, driver should start a new multicast session: set AID0 and wait for suspend/resume indication. * Due to a race with requeue a multicast-stop work could override a started multicast sequence in progress. ST-Ericsson ID: 361427 Change-Id: I42cfa14f5814c0e6a20b03e9fdf0d38e80288c3d Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39979 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/ap.c | 9 ++++++++- drivers/staging/cw1200/txrx.c | 9 +++++++++ drivers/staging/cw1200/wsm.c | 4 +++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index f58ccb13205..ff8d36cbedb 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -59,6 +59,8 @@ int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, entry = &priv->link_id_db[sta_priv->link_id - 1]; spin_lock_bh(&priv->ps_state_lock); + if (sta->uapsd_queues) + priv->sta_asleep_mask |= BIT(sta_priv->link_id); entry->status = CW1200_LINK_HARD; while ((skb = skb_dequeue(&entry->rx_queue))) ieee80211_rx_irqsafe(priv->hw, skb); @@ -617,11 +619,13 @@ void cw1200_multicast_start_work(struct work_struct *work) long tmo = priv->join_dtim_period * (priv->beacon_int + 20) * HZ / 1024; + cancel_work_sync(&priv->multicast_stop_work); + if (!priv->aid0_bit_set) { wsm_lock_tx(priv); cw1200_set_tim_impl(priv, true); priv->aid0_bit_set = true; - mod_timer(&priv->mcast_timeout, tmo); + mod_timer(&priv->mcast_timeout, jiffies + tmo); wsm_unlock_tx(priv); } } @@ -632,6 +636,7 @@ void cw1200_multicast_stop_work(struct work_struct *work) container_of(work, struct cw1200_common, multicast_stop_work); if (priv->aid0_bit_set) { + del_timer_sync(&priv->mcast_timeout); wsm_lock_tx(priv); priv->aid0_bit_set = false; cw1200_set_tim_impl(priv, false); @@ -644,6 +649,8 @@ void cw1200_mcast_timeout(unsigned long arg) struct cw1200_common *priv = (struct cw1200_common *)arg; + wiphy_warn(priv->hw->wiphy, + "Multicast delivery timeout.\n"); spin_lock_bh(&priv->ps_state_lock); priv->tx_multicast = priv->aid0_bit_set && priv->buffered_multicasts; diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 0d535f797a8..9569fd5849f 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -892,6 +892,15 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, priv->sta_asleep_mask); WARN_ON(cw1200_queue_requeue(queue, arg->packetID)); + spin_lock_bh(&priv->ps_state_lock); + if (!arg->link_id) { + priv->buffered_multicasts = true; + if (priv->sta_asleep_mask) { + queue_work(priv->workqueue, + &priv->multicast_start_work); + } + } + spin_unlock_bh(&priv->ps_state_lock); } else if (!WARN_ON(cw1200_queue_get_skb( queue, arg->packetID, &skb, &txpriv))) { struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb); diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index b1a2a9ecc6a..d3a23b44462 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1638,9 +1638,11 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, (priv->tx_multicast || !priv->sta_asleep_mask)) { priv->buffered_multicasts = false; - if (priv->tx_multicast) + if (priv->tx_multicast) { + priv->tx_multicast = false; queue_work(priv->workqueue, &priv->multicast_stop_work); + } } spin_unlock_bh(&priv->ps_state_lock); -- cgit v1.2.3 From d3b02e845ee735f9a8471036364c0bbd157fe58b Mon Sep 17 00:00:00 2001 From: Pawel Kulakowski Date: Wed, 29 Feb 2012 15:15:06 +0100 Subject: cw1200: Fixes possible corruption in list handling Fixes incorrect copying operation in cw1200_queue_register_post_gc. ST-Ericsson ID: 399704, 399824, 400116 Change-Id: I10314eec553b938c3d1676f9c62e4d1b736294e0 Signed-off-by: Pawel Kulakowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/40904 Reviewed-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c index 9a9fd2871e4..8145e891570 100644 --- a/drivers/staging/cw1200/queue.c +++ b/drivers/staging/cw1200/queue.c @@ -88,7 +88,7 @@ static void cw1200_queue_register_post_gc(struct list_head *gc_list, GFP_ATOMIC); BUG_ON(!gc_item); memcpy(gc_item, item, sizeof(struct cw1200_queue_item)); - list_move_tail(&gc_item->head, gc_list); + list_add_tail(&gc_item->head, gc_list); } static void __cw1200_queue_gc(struct cw1200_queue *queue, -- cgit v1.2.3 From 8d21eedde1693dbeec87f7374bed3e5ae61512a7 Mon Sep 17 00:00:00 2001 From: Lukasz Kucharczyk Date: Wed, 29 Feb 2012 15:15:07 +0100 Subject: cw1200: Change behavior on Action frame RX. When Action frame is received with linkid assigned (not equal 0), send reset and remap commands for this linkid to firmware. When Action frame is received without linkid (equal 0), assign new linkid, issue map and reset commands to firmware for this linkid. Note the different map-reset order for both cases! ST-Ericsson ID: 372718 ST-Ericsson FOSS-OUT ID: NA Signed-off-by: Lukasz Kucharczyk Change-Id: Ib839b92858564f4f759336cb83044217473cac53 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/41137 Tested-by: Lukasz KUCHARCZYK Reviewed-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/ap.c | 24 +++++++++++++++++++++++- drivers/staging/cw1200/cw1200.h | 7 +++++++ drivers/staging/cw1200/txrx.c | 16 +++++++++------- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index ff8d36cbedb..1211e127c52 100755 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -1025,8 +1025,30 @@ void cw1200_link_id_gc_work(struct work_struct *work) WARN_ON(wsm_reset(priv, &reset)); spin_lock_bh(&priv->ps_state_lock); } else { - next_gc = min(next_gc, (unsigned long)ttl); + next_gc = min_t(unsigned long, next_gc, ttl); } +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + } else if (priv->link_id_db[i].status == CW1200_LINK_RESET || + priv->link_id_db[i].status == + CW1200_LINK_RESET_REMAP) { + int status = priv->link_id_db[i].status; + priv->link_id_db[i].status = + priv->link_id_db[i].prev_status; + priv->link_id_db[i].timestamp = now; + reset.link_id = i + 1; + spin_unlock_bh(&priv->ps_state_lock); + WARN_ON(wsm_reset(priv, &reset)); + if (status == CW1200_LINK_RESET_REMAP) { + memcpy(map_link.mac_addr, + priv->link_id_db[i].mac, + ETH_ALEN); + map_link.link_id = i + 1; + WARN_ON(wsm_map_link(priv, &map_link)); + next_gc = min(next_gc, + CW1200_LINK_ID_GC_TIMEOUT); + } + spin_lock_bh(&priv->ps_state_lock); +#endif } if (need_reset) { skb_queue_purge(&priv->link_id_db[i].rx_queue); diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index d3ecb2a950f..c24dc420db4 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -65,6 +65,10 @@ enum cw1200_link_status { CW1200_LINK_RESERVE, CW1200_LINK_SOFT, CW1200_LINK_HARD, +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + CW1200_LINK_RESET, + CW1200_LINK_RESET_REMAP, +#endif }; enum cw1200_bss_loss_status { @@ -77,6 +81,9 @@ enum cw1200_bss_loss_status { struct cw1200_link_entry { unsigned long timestamp; enum cw1200_link_status status; +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + enum cw1200_link_status prev_status; +#endif u8 mac[ETH_ALEN]; u8 buffered[CW1200_MAX_TID]; struct sk_buff_head rx_queue; diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 9569fd5849f..b8859326612 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -1265,8 +1265,10 @@ void cw1200_link_id_reset(struct work_struct *work) flush_workqueue(priv->workqueue); /* Release the link ID */ spin_lock(&priv->ps_state_lock); + priv->link_id_db[temp_linkid - 1].prev_status = + priv->link_id_db[temp_linkid - 1].status; priv->link_id_db[temp_linkid - 1].status = - CW1200_LINK_RESERVE; + CW1200_LINK_RESET; spin_unlock(&priv->ps_state_lock); wsm_lock_tx_async(priv); if (queue_work(priv->workqueue, @@ -1275,15 +1277,15 @@ void cw1200_link_id_reset(struct work_struct *work) } } else { spin_lock(&priv->ps_state_lock); - entry = &priv->link_id_db[priv->action_linkid - 1]; + priv->link_id_db[priv->action_linkid - 1].prev_status = + priv->link_id_db[priv->action_linkid - 1].status; + priv->link_id_db[priv->action_linkid - 1].status = + CW1200_LINK_RESET_REMAP; + spin_unlock(&priv->ps_state_lock); wsm_lock_tx_async(priv); if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) - wsm_unlock_tx(priv); - spin_unlock(&priv->ps_state_lock); + wsm_unlock_tx(priv); flush_workqueue(priv->workqueue); - temp_linkid = cw1200_alloc_link_id(priv, - &priv->action_frame_sa[0]); - WARN_ON(!temp_linkid); } } #endif -- cgit v1.2.3 From 7632ca927cc0709d1d01dce5cca2d77201e0229c Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:15:08 +0100 Subject: cw1200: Deprioritize background queue. TC 5.2.27, step 7. Background queue needs to be deprioritized. ST-Ericsson ID: 355241 Change-Id: Ib9a22b8b79444eb8b5860f7b51e132f75bef764d Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/41161 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/wsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index d3a23b44462..5c52cdbb1c2 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1532,7 +1532,7 @@ static int cw1200_get_prio_queue(struct cw1200_common *priv, score = ((edca->aifns + edca->cwMin) << 16) + (edca->cwMax - edca->cwMin) * (random32() & 0xFFFF); - if (score < best) { + if (score < best && (winner < 0 || i != 3)) { best = score; winner = i; } -- cgit v1.2.3 From 68383a8207db856eaeb468775bed6720e4629390 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 29 Feb 2012 15:15:09 +0100 Subject: cw1200: Fix power consumption in STA mode Re-introduce setting operational mode when state changes to IDLE. ST-Ericsson ID: 401006 ST-Ericsson FOSS-OUT ID: NA Change-Id: I7b599818ad46d5972ee2cd422a4cd2c463948c4e Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/41528 Reviewed-by: QATOOLS --- drivers/staging/cw1200/sta.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 43d7843e417..3d82550231a 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -382,6 +382,11 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) } if (changed & IEEE80211_CONF_CHANGE_IDLE) { + struct wsm_operational_mode mode = { + .power_mode = wsm_power_mode_quiescent, + .disableMoreFlagUsage = true, + }; + wsm_lock_tx(priv); /* Disable p2p-dev mode forced by TX request */ if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) && @@ -390,6 +395,7 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) cw1200_disable_listening(priv); priv->join_status = CW1200_JOIN_STATUS_PASSIVE; } + WARN_ON(wsm_set_operational_mode(priv, &mode)); wsm_unlock_tx(priv); } -- cgit v1.2.3 From ca8df41a15456e13b8a77cbed969ad84bcf9e9d5 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 29 Feb 2012 15:15:10 +0100 Subject: cw1200: Adds wlan on u5500 support In u5500 platform reset pin is connected to PRCMU. * Kconfig option for wlan on u5500 * Implementation to set/unset RESOUT2_N_PIN by writing to PRCMU register (platform callback). ST-Ericsson ID: 375493 ST-Ericsson FOSS-OUT ID: NA Change-Id: I319622ab083c69c628bc52322d64339726e3e9bc Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/41168 Reviewed-by: QATOOLS Reviewed-by: Janusz DZIEDZIC --- drivers/staging/cw1200/Kconfig | 12 ++++++------ drivers/staging/cw1200/ap.c | 0 drivers/staging/cw1200/cw1200_plat.h | 2 ++ drivers/staging/cw1200/cw1200_sdio.c | 20 +++++++++++++++++--- 4 files changed, 25 insertions(+), 9 deletions(-) mode change 100755 => 100644 drivers/staging/cw1200/ap.c diff --git a/drivers/staging/cw1200/Kconfig b/drivers/staging/cw1200/Kconfig index dbb97adb829..3a4fc9813c6 100644 --- a/drivers/staging/cw1200/Kconfig +++ b/drivers/staging/cw1200/Kconfig @@ -42,18 +42,18 @@ config CW1200_WAPI_SUPPORT Say Y if your compat-wireless support WAPI. If unsure, say N. -config CW1200_U5500_SUPPORT - bool "Enable U5500 support" +config CW1200_DISABLE_BEACON_HINTS + bool "Disable 11d beacon hints" depends on CW1200 help - Say Y if you want to enable wlan on u5500 platform support. + Say Y if you want to disable 11d beacon hints. If unsure, say N. -config CW1200_DISABLE_BEACON_HINTS - bool "Disable 11d beacon hints" +config CW1200_U5500_SUPPORT + bool "Enable U5500 support" depends on CW1200 help - Say Y if you want to disable 11d beacon hints. + Say Y if you want to enable wlan on u5500 platform support. If unsure, say N. menu "Driver debug features" diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c old mode 100755 new mode 100644 diff --git a/drivers/staging/cw1200/cw1200_plat.h b/drivers/staging/cw1200/cw1200_plat.h index 4d30dbd3979..3a73183c9f8 100644 --- a/drivers/staging/cw1200/cw1200_plat.h +++ b/drivers/staging/cw1200/cw1200_plat.h @@ -18,6 +18,8 @@ struct cw1200_platform_data { bool enable); int (*clk_ctrl)(const struct cw1200_platform_data *pdata, bool enable); + int (*prcmu_ctrl)(const struct cw1200_platform_data *pdata, + bool enable); }; /* Declaration only. Should be implemented in arch/xxx/mach-yyy */ diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index 6f3eabd3967..c5de6dbbc2a 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -235,15 +235,24 @@ static int cw1200_detect_card(const struct cw1200_platform_data *pdata) static int cw1200_sdio_off(const struct cw1200_platform_data *pdata) { + int ret = 0; +#ifndef CONFIG_CW1200_U5500_SUPPORT const struct resource *reset = pdata->reset; gpio_set_value(reset->start, 0); - cw1200_detect_card(pdata); gpio_free(reset->start); - return 0; +#else + if (pdata->prcmu_ctrl) + ret = pdata->prcmu_ctrl(pdata, false); + msleep(50); +#endif + cw1200_detect_card(pdata); + return ret; } static int cw1200_sdio_on(const struct cw1200_platform_data *pdata) { + int ret = 0; +#ifndef CONFIG_CW1200_U5500_SUPPORT const struct resource *reset = pdata->reset; gpio_request(reset->start, reset->name); gpio_direction_output(reset->start, 1); @@ -259,8 +268,13 @@ static int cw1200_sdio_on(const struct cw1200_platform_data *pdata) /* The host should wait 32 ms after the WRESETN release * for the on-chip LDO to stabilize */ msleep(32); +#else + if (pdata->prcmu_ctrl) + ret = pdata->prcmu_ctrl(pdata, true); + msleep(50); +#endif cw1200_detect_card(pdata); - return 0; + return ret; } static int cw1200_sdio_reset(struct sbus_priv *self) -- cgit v1.2.3 From aaf70cd3abfd3a856c01382905021225b6fd1d8a Mon Sep 17 00:00:00 2001 From: Lukasz Kucharczyk Date: Wed, 29 Feb 2012 15:15:11 +0100 Subject: cw1200: limits maximum beacon skip period. If DTIM period in ms (calculated as dtim_period* beacon_interval) is greater than 1000ms disable beacon skipping and wake up for every beacon. ST-Ericsson ID: 402210 ST-Ericsson FOSS-OUT ID: NA Change-Id: I9894d5b99fc9a35676a39b8e5a53e99adcbe5397 Signed-off-by: Lukasz Kucharczyk Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/41865 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/ap.c | 15 +++++++++------ drivers/staging/cw1200/pm.c | 4 +++- drivers/staging/cw1200/sta.c | 5 +++-- drivers/staging/cw1200/wsm.h | 2 ++ 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 1211e127c52..c104b39d8f8 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -389,8 +389,9 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, BSS_CHANGED_ERP_SLOT)) { ap_printk(KERN_DEBUG "BSS_CHANGED_ASSOC.\n"); if (info->assoc) { /* TODO: ibss_joined */ - int dtim_interval = info->dtim_period; struct ieee80211_sta *sta = NULL; + priv->join_dtim_period = info->dtim_period; + priv->beacon_int = info->beacon_int; /* Associated: kill join timeout */ cancel_delayed_work_sync(&priv->join_timeout); @@ -465,11 +466,11 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, priv->bss_params.aid = info->aid; - if (dtim_interval < 1) - dtim_interval = 1; + if (priv->join_dtim_period < 1) + priv->join_dtim_period = 1; - ap_printk(KERN_DEBUG "[STA] DTIM %d\n", - dtim_interval); + ap_printk(KERN_DEBUG "[STA] DTIM %d, interval: %d\n", + priv->join_dtim_period, priv->beacon_int); ap_printk(KERN_DEBUG "[STA] Preamble: %d, " \ "Greenfield: %d, Aid: %d, " \ "Rates: 0x%.8X, Basic: 0x%.8X\n", @@ -484,7 +485,9 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, WARN_ON(wsm_set_bss_params(priv, &priv->bss_params)); priv->setbssparams_done = true; WARN_ON(wsm_set_beacon_wakeup_period(priv, - dtim_interval, 0)); + priv->beacon_int * priv->join_dtim_period > + MAX_BEACON_SKIP_TIME_MS ? 1 : + priv->join_dtim_period, 0)); if (sta && cw1200_is_ht(&priv->ht_info)) { ap_printk(KERN_DEBUG "[STA] Enabling Block ACK\n"); diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c index 9eece65a709..550ee1482b1 100644 --- a/drivers/staging/cw1200/pm.c +++ b/drivers/staging/cw1200/pm.c @@ -333,7 +333,9 @@ int cw1200_wow_resume(struct ieee80211_hw *hw) WARN_ON(cw1200_bh_resume(priv)); if (state->beacon_skipping) { - wsm_set_beacon_wakeup_period(priv, + wsm_set_beacon_wakeup_period(priv, priv->beacon_int * + priv->join_dtim_period > + MAX_BEACON_SKIP_TIME_MS ? 1 : priv->join_dtim_period, 0); state->beacon_skipping = false; } diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 3d82550231a..6c5ff577e32 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1288,9 +1288,10 @@ void cw1200_join_work(struct work_struct *work) if (tim && tim->dtim_period > 1) { join.dtimPeriod = tim->dtim_period; priv->join_dtim_period = tim->dtim_period; - sta_printk(KERN_DEBUG "[STA] Join DTIM: %d\n", - join.dtimPeriod); } + priv->beacon_int = bss->beacon_interval; + sta_printk(KERN_DEBUG "[STA] Join DTIM: %d, interval: %d\n", + join.dtimPeriod, priv->beacon_int); join.channelNumber = priv->channel->hw_value; join.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ? diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 0915b32bf96..61b2bd0c06b 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -585,6 +585,8 @@ struct wsm_hdr { #define WSM_TX_LINK_ID(link_id) \ ((link_id & WSM_TX_LINK_ID_MAX) << 6) +#define MAX_BEACON_SKIP_TIME_MS 1000 + /* ******************************************************************** */ /* WSM capcbility */ -- cgit v1.2.3 From cf34ec6ed84c0193c1c73d4f453542772dbb3d8b Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:15:12 +0100 Subject: cw1200: Correctly handle requeuing for NOA. Firmware may report exit of GO absence interval as a start: unicast suspend/resume notification with link id equals to 0. This case was not handled correctly. The patch implements (costly!) solution for that. Start: unicast indication resets power save states of all connected STAs, and driver performs an attempt to deliver buffered frames for them. For STAs in powersave frirmware rejects frames, syncronizing power state. The solution is really costly and can be used as a workaround in short time prospective. It's much better if firmware reports proper link id in the suspend/resume notification. ST-Ericsson ID: 400498 Change-Id: Iaa124ce7013605abee264e503e160eda32140bdc Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/42108 Tested-by: Bartosz MARKOWSKI Reviewed-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/ap.c | 46 ++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index c104b39d8f8..ca2abbe93b8 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -32,7 +32,7 @@ static int cw1200_enable_beaconing(struct cw1200_common *priv, static void __cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, enum sta_notify_cmd notify_cmd, - struct ieee80211_sta *sta); + int link_id); /* ******************************************************************** */ /* AP API */ @@ -94,13 +94,19 @@ int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, static void __cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, enum sta_notify_cmd notify_cmd, - struct ieee80211_sta *sta) + int link_id) { struct cw1200_common *priv = dev->priv; - struct cw1200_sta_priv *sta_priv = - (struct cw1200_sta_priv *)&sta->drv_priv; - u32 bit = BIT(sta_priv->link_id); - u32 prev = priv->sta_asleep_mask & bit; + u32 bit, prev; + + /* Zero link id means "for all link IDs" */ + if (link_id) + bit = BIT(link_id); + else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE)) + bit = 0; + else + bit = priv->link_id_map; + prev = priv->sta_asleep_mask & bit; switch (notify_cmd) { case STA_NOTIFY_SLEEP: @@ -116,7 +122,7 @@ static void __cw1200_sta_notify(struct ieee80211_hw *dev, if (prev) { priv->sta_asleep_mask &= ~bit; priv->pspoll_mask &= ~bit; - if (priv->tx_multicast && + if (priv->tx_multicast && link_id && !priv->sta_asleep_mask) queue_work(priv->workqueue, &priv->multicast_stop_work); @@ -132,8 +138,11 @@ void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_sta *sta) { struct cw1200_common *priv = dev->priv; + struct cw1200_sta_priv *sta_priv = + (struct cw1200_sta_priv *)&sta->drv_priv; + spin_lock_bh(&priv->ps_state_lock); - __cw1200_sta_notify(dev, vif, notify_cmd, sta); + __cw1200_sta_notify(dev, vif, notify_cmd, sta_priv->link_id); spin_unlock_bh(&priv->ps_state_lock); } @@ -142,21 +151,15 @@ static void cw1200_ps_notify(struct cw1200_common *priv, { struct ieee80211_sta *sta; - if (!link_id || link_id > CW1200_MAX_STA_IN_AP_MODE) + if (link_id > CW1200_MAX_STA_IN_AP_MODE) return; txrx_printk(KERN_DEBUG "%s for LinkId: %d. STAs asleep: %.8X\n", ps ? "Stop" : "Start", link_id, priv->sta_asleep_mask); - rcu_read_lock(); - sta = ieee80211_find_sta(priv->vif, - priv->link_id_db[link_id - 1].mac); - if (sta) { - __cw1200_sta_notify(priv->hw, priv->vif, - ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, sta); - } - rcu_read_unlock(); + __cw1200_sta_notify(priv->hw, priv->vif, + ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id); } static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set) @@ -680,12 +683,9 @@ int cw1200_ampdu_action(struct ieee80211_hw *hw, void cw1200_suspend_resume(struct cw1200_common *priv, struct wsm_suspend_resume *arg) { - /* if () is intendend to protect against spam. FW sends - * "start multicast" request on every DTIM. */ - if (arg->stop || !arg->multicast || priv->buffered_multicasts) - ap_printk(KERN_DEBUG "[AP] %s: %s\n", - arg->stop ? "stop" : "start", - arg->multicast ? "broadcast" : "unicast"); + ap_printk(KERN_DEBUG "[AP] %s: %s\n", + arg->stop ? "stop" : "start", + arg->multicast ? "broadcast" : "unicast"); if (arg->multicast) { bool cancel_tmo = false; -- cgit v1.2.3 From 2694f2ff0f4222b01cccaab18e9486d77547c512 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:15:13 +0100 Subject: cw1200: Set p2p mode info in p2p STA and GO modes. p2p powersave configuration request is called ar early stage, before p2p STA is associated with a GO. p2p mode info MIB was not set in firmware due to that. The change sets p2p MIB after successful association and inGO mode. ST-Ericsson ID: 400498 Change-Id: Ibee23637899a4e80fd99765aaa04454ce44339f9 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/42154 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/ap.c | 14 ++++++++++++++ drivers/staging/cw1200/sta.c | 24 ++++++++++++++++++------ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index ca2abbe93b8..e26529dc528 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -500,6 +500,13 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, } cw1200_set_pm(priv, &priv->powersave_mode); + if (priv->vif->p2p) { + ap_printk(KERN_DEBUG + "[STA] Setting p2p powersave " + "configuration.\n"); + WARN_ON(wsm_set_p2p_ps_modeinfo(priv, + &priv->p2p_ps_modeinfo)); + } if (priv->is_BT_Present) WARN_ON(cw1200_set_btcoexinfo(priv)); @@ -864,6 +871,13 @@ static int cw1200_start_ap(struct cw1200_common *priv) ret = WARN_ON(wsm_start(priv, &start)); if (!ret) ret = WARN_ON(cw1200_upload_keys(priv)); + if (!ret && priv->vif->p2p) { + ap_printk(KERN_DEBUG + "[AP] Setting p2p powersave " + "configuration.\n"); + WARN_ON(wsm_set_p2p_ps_modeinfo(priv, + &priv->p2p_ps_modeinfo)); + } if (!ret) { WARN_ON(wsm_set_block_ack_policy(priv, 0, 0)); diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 6c5ff577e32..ad85f29008a 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -322,6 +322,9 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) struct wsm_p2p_ps_modeinfo *modeinfo; modeinfo = &priv->p2p_ps_modeinfo; sta_printk(KERN_DEBUG "[STA] IEEE80211_CONF_CHANGE_P2P_PS\n"); + sta_printk(KERN_DEBUG "[STA] Legacy PS: %d for AID %d " + "in %d mode.\n", conf->p2p_ps.legacy_ps, + priv->bss_params.aid, priv->join_status); if (conf->p2p_ps.legacy_ps >= 0) { if (conf->p2p_ps.legacy_ps > 0) @@ -333,11 +336,15 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) cw1200_set_pm(priv, &priv->powersave_mode); } + sta_printk(KERN_DEBUG "[STA] CTWindow: %d\n", + conf->p2p_ps.ctwindow); if (conf->p2p_ps.ctwindow >= 128) modeinfo->oppPsCTWindow = 127; else if (conf->p2p_ps.ctwindow >= 0) modeinfo->oppPsCTWindow = conf->p2p_ps.ctwindow; + sta_printk(KERN_DEBUG "[STA] Opportunistic: %d\n", + conf->p2p_ps.opp_ps); switch (conf->p2p_ps.opp_ps) { case 0: modeinfo->oppPsCTWindow &= ~(BIT(7)); @@ -349,6 +356,11 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) break; } + sta_printk(KERN_DEBUG "[STA] NOA: %d, %d, %d, %d\n", + conf->p2p_ps.count, + conf->p2p_ps.start, + conf->p2p_ps.duration, + conf->p2p_ps.interval); /* Notice of Absence */ modeinfo->count = conf->p2p_ps.count; modeinfo->startTime = __cpu_to_le32(conf->p2p_ps.start); @@ -360,14 +372,14 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) else modeinfo->dtimCount = 0; - if (priv->join_status == CW1200_JOIN_STATUS_STA || - priv->join_status == CW1200_JOIN_STATUS_AP) { #if defined(CONFIG_CW1200_STA_DEBUG) - print_hex_dump_bytes("p2p_ps_modeinfo: ", - DUMP_PREFIX_NONE, - (u8 *)modeinfo, - sizeof(*modeinfo)); + print_hex_dump_bytes("p2p_ps_modeinfo: ", + DUMP_PREFIX_NONE, + (u8 *)modeinfo, + sizeof(*modeinfo)); #endif /* CONFIG_CW1200_STA_DEBUG */ + if (priv->join_status == CW1200_JOIN_STATUS_STA || + priv->join_status == CW1200_JOIN_STATUS_AP) { WARN_ON(wsm_set_p2p_ps_modeinfo(priv, modeinfo)); } } -- cgit v1.2.3 From 24e395493fd3bc14d88191d662cf1b808cf372be Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:15:14 +0100 Subject: cw1200: Workaround against throughput drop in b/g mixed mode. Device has problems (at least) switching from g-rates CTS to 1Mbps. This switch takes enormous amount of time (100-200 ms), leading to valuable throughput drop. As a workaround, additional g-rates are injected to the TX rate policy if initial rate is one of high g-rates and the fallback rate is a b-rate. ST-Ericsson ID: 402230 Change-Id: Ie90195035e77252310e4d9edc0f06b2a86b39712 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/42418 Tested-by: Bartosz MARKOWSKI Reviewed-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/txrx.c | 53 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index b8859326612..1a48689660c 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -127,6 +127,59 @@ static void tx_policy_build(const struct cw1200_common *priv, limit -= rates[i].count; } } + + /* HACK!!! Device has problems (at least) switching from + * 54Mbps CTS to 1Mbps. This switch takes enormous amount + * of time (100-200 ms), leading to valuable throughput drop. + * As a workaround, additional g-rates are injected to the + * policy. + */ + if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) && + rates[0].idx > 4 && rates[0].count > 2 && + rates[1].idx < 2) { + /* ">> 1" is an equivalent of "/ 2", but faster */ + int mid_rate = (rates[0].idx + 4) >> 1; + + /* Decrease number of retries for the initial rate */ + rates[0].count -= 2; + + if (mid_rate != 4) { + /* Keep fallback rate at 1Mbps. */ + rates[3] = rates[1]; + + /* Inject 1 transmission on lowest g-rate */ + rates[2].idx = 4; + rates[2].count = 1; + rates[2].flags = rates[1].flags; + + /* Inject 1 transmission on mid-rate */ + rates[1].idx = mid_rate; + rates[1].count = 1; + + /* Fallback to 1 Mbps is a really bad thing, + * so let's try to increase probability of + * successful transmission on the lowest g rate + * even more */ + if (rates[0].count >= 3) { + --rates[0].count; + ++rates[2].count; + } + + /* Adjust amount of rates defined */ + count += 2; + } else { + /* Keep fallback rate at 1Mbps. */ + rates[2] = rates[1]; + + /* Inject 2 transmissions on lowest g-rate */ + rates[1].idx = 4; + rates[1].count = 2; + + /* Adjust amount of rates defined */ + count += 1; + } + } + policy->defined = cw1200_get_tx_rate(priv, &rates[0])->hw_value + 1; for (i = 0; i < count; ++i) { -- cgit v1.2.3 From f08139e385b252031244c2fc2a08a5dee74c368f Mon Sep 17 00:00:00 2001 From: Lukasz Kucharczyk Date: Wed, 29 Feb 2012 15:15:15 +0100 Subject: cw1200: set barker_mode and non-ERP_present bits. In AP mode, barker_mode and non-ERP_present bits in ERP IE of beacons are now set accordingly to beacon templates recevied from upper layer. ST-Ericsson ID: 404687 ST-Ericsson FOSS-OUT ID: NA Change-Id: I331132fad8c8293c2ee40a17c776019a125559d2 Signed-off-by: Lukasz Kucharczyk Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/42922 Reviewed-by: QATOOLS Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/ap.c | 57 +++++++++++++++++++++++++++++++++-------- drivers/staging/cw1200/cw1200.h | 2 +- 2 files changed, 47 insertions(+), 12 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index e26529dc528..ccc75c7a2ae 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -22,6 +22,10 @@ #define CW1200_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ)) +#ifndef ERP_INFO_BYTE_OFFSET +#define ERP_INFO_BYTE_OFFSET 2 +#endif + static int cw1200_upload_beacon(struct cw1200_common *priv); static int cw1200_upload_pspoll(struct cw1200_common *priv); static int cw1200_upload_null(struct cw1200_common *priv); @@ -223,7 +227,6 @@ void cw1200_set_cts_work(struct work_struct *work) struct cw1200_common *priv = container_of(work, struct cw1200_common, set_cts_work.work); - struct ieee80211_mgmt *mgmt; u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0}; struct wsm_update_ie update_ie = { .what = WSM_UPDATE_IE_BEACON, @@ -231,17 +234,18 @@ void cw1200_set_cts_work(struct work_struct *work) .ies = erp_ie, .length = 3, }; - __le32 use_cts_prot = priv->use_cts_prot ? - __cpu_to_le32(1) : 0; - + u32 erp_info; + __le32 use_cts_prot; mutex_lock(&priv->conf_mutex); - if (priv->use_cts_prot) - erp_ie[ERP_INFO_BYTE_OFFSET] |= WLAN_ERP_USE_PROTECTION; - else - erp_ie[ERP_INFO_BYTE_OFFSET] &= ~(WLAN_ERP_USE_PROTECTION); + erp_info = priv->erp_info; mutex_unlock(&priv->conf_mutex); + use_cts_prot = + erp_info & WLAN_ERP_USE_PROTECTION ? + __cpu_to_le32(1) : 0; + + erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info; - ap_printk(KERN_DEBUG "[STA] CTS protection %d\n", use_cts_prot); + ap_printk(KERN_DEBUG "[STA] ERP information 0x%x\n", erp_info); WARN_ON(wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION, &use_cts_prot, sizeof(use_cts_prot))); @@ -542,11 +546,18 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, } } if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT)) { + u32 prev_erp_info = priv->erp_info; - priv->use_cts_prot = info->use_cts_prot; + if (info->use_cts_prot) + priv->erp_info |= WLAN_ERP_USE_PROTECTION; + else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT)) + priv->erp_info &= ~WLAN_ERP_USE_PROTECTION; - queue_delayed_work(priv->workqueue, &priv->set_cts_work, 0*HZ); + if (prev_erp_info != priv->erp_info) + queue_delayed_work(priv->workqueue, + &priv->set_cts_work, 0*HZ); } + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) { __le32 slot_time = info->use_short_slot ? __cpu_to_le32(9) : __cpu_to_le32(20); @@ -736,6 +747,9 @@ static int cw1200_upload_beacon(struct cw1200_common *priv) struct wsm_template_frame frame = { .frame_type = WSM_FRAME_TYPE_BEACON, }; + struct ieee80211_mgmt *mgmt; + u8 *erp_inf, *ies; + u32 ies_len; if (priv->vif->p2p) frame.rate = WSM_TRANSMIT_RATE_6; @@ -744,6 +758,27 @@ static int cw1200_upload_beacon(struct cw1200_common *priv) if (WARN_ON(!frame.skb)) return -ENOMEM; + mgmt = (void *)frame.skb->data; + ies = mgmt->u.beacon.variable; + ies_len = frame.skb->len - (u32)(ies - (u8 *)mgmt); + erp_inf = (u8 *)cfg80211_find_ie(WLAN_EID_ERP_INFO, ies, ies_len); + if (erp_inf) { + if (erp_inf[ERP_INFO_BYTE_OFFSET] + & WLAN_ERP_BARKER_PREAMBLE) + priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE; + else + priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE; + + if (erp_inf[ERP_INFO_BYTE_OFFSET] + & WLAN_ERP_NON_ERP_PRESENT) { + priv->erp_info |= WLAN_ERP_USE_PROTECTION; + priv->erp_info |= WLAN_ERP_NON_ERP_PRESENT; + } else { + priv->erp_info &= ~WLAN_ERP_USE_PROTECTION; + priv->erp_info &= ~WLAN_ERP_NON_ERP_PRESENT; + } + } + ret = wsm_set_template_frame(priv, &frame); if (!ret) { /* TODO: Distille probe resp; remove TIM diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index c24dc420db4..ff69098c8a7 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -163,7 +163,7 @@ struct cw1200_common { bool is_BT_Present; u8 conf_listen_interval; u32 listen_interval; - u32 use_cts_prot; + u32 erp_info; /* BH */ atomic_t bh_rx; -- cgit v1.2.3 From 0df82895afa0986680c874f33cbc70dfc61cc976 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Wed, 29 Feb 2012 15:15:16 +0100 Subject: cw1200: AP/GO UAPSD force STA PS minor update Force STA PS only in case STA enables all UAPSD ACs. ST-Ericsson ID: 405276 ST-Ericsson FOSS-OUT ID: NA Change-Id: I1f102f4be8b94282b8f74b6d0f20eb82c7579fd2 Signed-off-by: Janusz Dziedzic Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/43271 Reviewed-by: QATOOLS Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/ap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index ccc75c7a2ae..933b69b27b9 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -63,7 +63,8 @@ int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, entry = &priv->link_id_db[sta_priv->link_id - 1]; spin_lock_bh(&priv->ps_state_lock); - if (sta->uapsd_queues) + if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) == + IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) priv->sta_asleep_mask |= BIT(sta_priv->link_id); entry->status = CW1200_LINK_HARD; while ((skb = skb_dequeue(&entry->rx_queue))) -- cgit v1.2.3 From 5e3608c5921fdc5c1f4799fcce4b80459a3367fa Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 29 Feb 2012 15:15:17 +0100 Subject: cw1200: fix scan timeout issues * send stop-scan to FW on scan timeout to ensure we don't send another start-scan while the other is yet to finish * abort the whole scan on timeout to avoid deadlock also notify the upper layers of scan cancelation `cancel_delayed_work_sync` would get called from within a workqueue worker `cw1200_scan_timeout` causing a deadlock. ST-Ericsson ID: 398988 ST-Ericsson FOSS-OUT ID: NA Change-Id: I78b8d3bf88730bc9e1ba81d05e65cdf7b568e6e9 Signed-off-by: Michal Kazior Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/42484 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/scan.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index 32faf2b1c3f..8a113463c91 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -155,7 +155,11 @@ void cw1200_scan_work(struct work_struct *work) !(priv->powersave_mode.pmMode & WSM_PSM_PS)) cw1200_set_pm(priv, &priv->powersave_mode); - if (priv->scan.req) + if (priv->scan.status < 0) + wiphy_dbg(priv->hw->wiphy, + "[SCAN] Scan failed (%d).\n", + priv->scan.status); + else if (priv->scan.req) wiphy_dbg(priv->hw->wiphy, "[SCAN] Scan completed.\n"); else @@ -313,6 +317,8 @@ void cw1200_scan_timeout(struct work_struct *work) "Timeout waiting for scan " "complete notification.\n"); priv->scan.status = -ETIMEDOUT; + priv->scan.curr = priv->scan.end; + WARN_ON(wsm_stop_scan(priv)); } cw1200_scan_complete(priv); } -- cgit v1.2.3 From fc17ef7d49f54874500c3b56979ff99f00cabca4 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Wed, 29 Feb 2012 15:15:18 +0100 Subject: cw1200: Send NOA notification after NOA set Send Notice Of Absence notification to upper layer after we set NOA params in P2P_GO mode. ST-Ericsson ID: 401209 ST-Ericsson FOSS-OUT ID: NA Change-Id: I92bc8ce4d6e75c48a574857f576a9bc1fd7a7da6 Depends-On: I9424401fcd9e69067aebd4fbfd97141d91003b89 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/40156 Tested-by: Janusz DZIEDZIC Reviewed-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/ap.c | 36 ++++++++++++++++++++++++++++++++++++ drivers/staging/cw1200/ap.h | 3 +++ drivers/staging/cw1200/sta.c | 34 ++++++++++++++++++++++++++++------ drivers/staging/cw1200/wsm.h | 7 +++++++ 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 933b69b27b9..ce82ba5e87f 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -1116,3 +1116,39 @@ void cw1200_link_id_gc_work(struct work_struct *work) wsm_unlock_tx(priv); } +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) +void cw1200_notify_noa(struct cw1200_common *priv, int delay) +{ + struct cfg80211_p2p_ps p2p_ps = {0}; + struct wsm_p2p_ps_modeinfo *modeinfo; + modeinfo = &priv->p2p_ps_modeinfo; + + ap_printk(KERN_DEBUG "[AP]: %s called\n", __func__); + + if (priv->join_status != CW1200_JOIN_STATUS_AP) + return; + + if (delay) + msleep(delay); + + if (!WARN_ON(wsm_get_p2p_ps_modeinfo(priv, modeinfo))) { +#if defined(CONFIG_CW1200_STA_DEBUG) + print_hex_dump_bytes("[AP] p2p_get_ps_modeinfo: ", + DUMP_PREFIX_NONE, + (u8 *)modeinfo, + sizeof(*modeinfo)); +#endif /* CONFIG_CW1200_STA_DEBUG */ + p2p_ps.opp_ps = !!(modeinfo->oppPsCTWindow & BIT(7)); + p2p_ps.ctwindow = modeinfo->oppPsCTWindow & (~BIT(7)); + p2p_ps.count = modeinfo->count; + p2p_ps.start = __le32_to_cpu(modeinfo->startTime); + p2p_ps.duration = __le32_to_cpu(modeinfo->duration); + p2p_ps.interval = __le32_to_cpu(modeinfo->interval); + p2p_ps.index = modeinfo->reserved; + + ieee80211_p2p_noa_notify(priv->vif, + &p2p_ps, + GFP_KERNEL); + } +} +#endif diff --git a/drivers/staging/cw1200/ap.h b/drivers/staging/cw1200/ap.h index 2175a82d2a2..c10e4ef16d2 100644 --- a/drivers/staging/cw1200/ap.h +++ b/drivers/staging/cw1200/ap.h @@ -42,5 +42,8 @@ int cw1200_find_link_id(struct cw1200_common *priv, const u8 *mac); int cw1200_alloc_link_id(struct cw1200_common *priv, const u8 *mac); void cw1200_link_id_work(struct work_struct *work); void cw1200_link_id_gc_work(struct work_struct *work); +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) +void cw1200_notify_noa(struct cw1200_common *priv, int delay); +#endif #endif diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index ad85f29008a..85e57d2f7b3 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -15,6 +15,7 @@ #include "cw1200.h" #include "sta.h" +#include "ap.h" #include "fwio.h" #include "bh.h" #include "debug.h" @@ -363,17 +364,34 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) conf->p2p_ps.interval); /* Notice of Absence */ modeinfo->count = conf->p2p_ps.count; - modeinfo->startTime = __cpu_to_le32(conf->p2p_ps.start); - modeinfo->duration = __cpu_to_le32(conf->p2p_ps.duration); - modeinfo->interval = __cpu_to_le32(conf->p2p_ps.interval); - if (conf->p2p_ps.count) + if (conf->p2p_ps.count) { + /* In case P2P_GO we need some extra time to be sure + * we will update beacon/probe_resp IEs correctly */ +#define NOA_DELAY_START_MS 300 + if (priv->join_status == CW1200_JOIN_STATUS_AP) + modeinfo->startTime = + __cpu_to_le32(conf->p2p_ps.start + + NOA_DELAY_START_MS); + else + modeinfo->startTime = + __cpu_to_le32(conf->p2p_ps.start); + modeinfo->duration = + __cpu_to_le32(conf->p2p_ps.duration); + modeinfo->interval = + __cpu_to_le32(conf->p2p_ps.interval); modeinfo->dtimCount = 1; - else + modeinfo->reserved = 0; + } else { modeinfo->dtimCount = 0; + modeinfo->startTime = 0; + modeinfo->reserved = 0; + modeinfo->duration = 0; + modeinfo->interval = 0; + } #if defined(CONFIG_CW1200_STA_DEBUG) - print_hex_dump_bytes("p2p_ps_modeinfo: ", + print_hex_dump_bytes("p2p_set_ps_modeinfo: ", DUMP_PREFIX_NONE, (u8 *)modeinfo, sizeof(*modeinfo)); @@ -382,6 +400,10 @@ int cw1200_config(struct ieee80211_hw *dev, u32 changed) priv->join_status == CW1200_JOIN_STATUS_AP) { WARN_ON(wsm_set_p2p_ps_modeinfo(priv, modeinfo)); } + + /* Temporary solution while firmware don't support NOA change + * notification yet */ + cw1200_notify_noa(priv, 10); } #endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 61b2bd0c06b..004a55f1240 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -1658,6 +1658,13 @@ static inline int wsm_set_p2p_ps_modeinfo(struct cw1200_common *priv, mi, sizeof(*mi)); } +static inline int wsm_get_p2p_ps_modeinfo(struct cw1200_common *priv, + struct wsm_p2p_ps_modeinfo *mi) +{ + return wsm_read_mib(priv, WSM_MIB_ID_P2P_PS_MODE_INFO, + mi, sizeof(*mi)); +} + /* UseMultiTxConfMessage */ static inline int wsm_use_multi_tx_conf(struct cw1200_common *priv, -- cgit v1.2.3 From b5b460a09fa89a180a4e1b252eaba3b8b4ea4e8e Mon Sep 17 00:00:00 2001 From: Lukasz Kucharczyk Date: Wed, 29 Feb 2012 15:15:19 +0100 Subject: cw1200: fix deadlock on priv->ps_state_lock. priv->ps_state_lock was double locked on: cw1200_tx -> cw1200_queue_put -> cw1200_queue_post_gc -> priv->skb_dtor ( == cw1200_skb_dtor) -> cw1200_notify_buffered_tx ST-Ericsson ID: 398849, 361041 ST-Ericsson FOSS-OUT ID: NA Change-Id: I7d22591e58ac0463c691be145e7e35e0fab5535a Signed-off-by: Lukasz Kucharczyk Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44219 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/queue.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c index 8145e891570..19db63aa5bf 100644 --- a/drivers/staging/cw1200/queue.c +++ b/drivers/staging/cw1200/queue.c @@ -321,17 +321,13 @@ int cw1200_queue_put(struct cw1200_queue *queue, if (queue->num_queued >= queue->capacity) { queue->overfull = true; - __cw1200_queue_gc(queue, &gc_list, false); - if (queue->overfull) - __cw1200_queue_lock(queue); - + __cw1200_queue_lock(queue); + mod_timer(&queue->gc, jiffies); } } else { ret = -ENOENT; } spin_unlock_bh(&queue->lock); - if (unlikely(!list_empty(&gc_list))) - cw1200_queue_post_gc(stats, &gc_list); return ret; } -- cgit v1.2.3 From 93223eaaf59ff644862a1804c11b455950aa6865 Mon Sep 17 00:00:00 2001 From: Lukasz Kucharczyk Date: Wed, 29 Feb 2012 15:15:20 +0100 Subject: cw1200: fix crash during driver unloading. Debug utilities were disabled before disabling irq handler. Irq handler used debug utilities what could lead to crash sometimes. ST-Ericsson ID: 398849 ST-Ericsson FOSS-OUT ID: NA Change-Id: I8a8a54c841bbb7bf539ea150f3fe44e09c0f6c17 Signed-off-by: Lukasz Kucharczyk Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44221 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 7a2e12d23a7..f6ddac68823 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -428,13 +428,13 @@ void cw1200_unregister_common(struct ieee80211_hw *dev) struct cw1200_common *priv = dev->priv; int i; - cw1200_debug_release(priv); - ieee80211_unregister_hw(dev); priv->sbus_ops->irq_unsubscribe(priv->sbus_priv); cw1200_unregister_bh(priv); + cw1200_debug_release(priv); + #ifdef CONFIG_CW1200_LEDS cw1200_unregister_leds(priv); #endif /* CONFIG_CW1200_LEDS */ -- cgit v1.2.3 From d4cdabfadee8cfc9c0cad8e6559a518883bfa153 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 29 Feb 2012 15:15:21 +0100 Subject: cw1200: fix ps_state_lock locking Use _bh function variants for ps_state_lock everywhere. Should fix possible deadlocks. Change-Id: Ic316a302c57381f96858cf5337c1d819142b01e1 Signed-off-by: Michal Kazior Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/43916 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/txrx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 1a48689660c..fb80d218dfa 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -1317,24 +1317,24 @@ void cw1200_link_id_reset(struct work_struct *work) /* Make sure we execute the WQ */ flush_workqueue(priv->workqueue); /* Release the link ID */ - spin_lock(&priv->ps_state_lock); + spin_lock_bh(&priv->ps_state_lock); priv->link_id_db[temp_linkid - 1].prev_status = priv->link_id_db[temp_linkid - 1].status; priv->link_id_db[temp_linkid - 1].status = CW1200_LINK_RESET; - spin_unlock(&priv->ps_state_lock); + spin_unlock_bh(&priv->ps_state_lock); wsm_lock_tx_async(priv); if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) wsm_unlock_tx(priv); } } else { - spin_lock(&priv->ps_state_lock); + spin_lock_bh(&priv->ps_state_lock); priv->link_id_db[priv->action_linkid - 1].prev_status = priv->link_id_db[priv->action_linkid - 1].status; priv->link_id_db[priv->action_linkid - 1].status = CW1200_LINK_RESET_REMAP; - spin_unlock(&priv->ps_state_lock); + spin_unlock_bh(&priv->ps_state_lock); wsm_lock_tx_async(priv); if (queue_work(priv->workqueue, &priv->link_id_work) <= 0) wsm_unlock_tx(priv); -- cgit v1.2.3 From fd079d0c51ec4831187025d77de67a99f3ded897 Mon Sep 17 00:00:00 2001 From: Vitaly Wool Date: Wed, 29 Feb 2012 15:15:22 +0100 Subject: cw1200: Driver hung detection and reporting If there is a firmware exception or a bh error, report this to the userspace. Userspace will then take care of handling this event, e. g. by reloading the driver completely. Category: bugfix FIX=DMS01117799 ST-Ericsson ID: 401162 ST-Ericsson FOSS-OUT ID: NA Change-Id: I74a8a434f9befbab66ff4cf6c5b5ce82a359c766 Signed-off-by: Vitaly Wool Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44302 Reviewed-by: QATOOLS Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/bh.c | 4 ++++ drivers/staging/cw1200/debug.c | 34 +++++++++++++++++++++++++++++++ drivers/staging/cw1200/wsm.c | 45 +++++++++++++++++++++++++++++++----------- 3 files changed, 71 insertions(+), 12 deletions(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index 4b678f40c40..34bf7d733b8 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -549,6 +549,10 @@ tx: if (!term) { cw1200_dbg(CW1200_DBG_ERROR, "[BH] Fatal error, exitting.\n"); priv->bh_error = 1; +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL); + cw1200_pm_stay_awake(&priv->pm_state, 3*HZ); +#endif /* TODO: schedule_work(recovery) */ #ifndef HAS_PUT_TASK_STRUCT /* The only reason of having this stupid code here is diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 597f94de87f..2056101a3f4 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -426,6 +426,34 @@ static const struct file_operations fops_11n = { .llseek = default_llseek, }; +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) +static ssize_t cw1200_hang_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct cw1200_common *priv = file->private_data; + char buf[1]; + + if (!count) + return -EINVAL; + if (copy_from_user(buf, user_buf, 1)) + return -EFAULT; + + if (priv->vif) { + cw1200_pm_stay_awake(&priv->pm_state, 3*HZ); + ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL); + } else + return -ENODEV; + + return count; +} + +static const struct file_operations fops_hang = { + .open = cw1200_generic_open, + .write = cw1200_hang_write, + .llseek = default_llseek, +}; +#endif + int cw1200_debug_init(struct cw1200_common *priv) { struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv), @@ -451,6 +479,12 @@ int cw1200_debug_init(struct cw1200_common *priv) d->debugfs_phy, priv, &fops_11n)) goto err; +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + if (!debugfs_create_file("hang", S_IWUSR, d->debugfs_phy, + priv, &fops_hang)) + goto err; +#endif + return 0; err: diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 5c52cdbb1c2..bb94d19da08 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1083,9 +1083,15 @@ void wsm_lock_tx(struct cw1200_common *priv) { wsm_cmd_lock(priv); if (atomic_add_return(1, &priv->tx_lock) == 1) { - WARN_ON(wait_event_timeout(priv->bh_evt_wq, - !priv->hw_bufs_used, WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); - wsm_printk(KERN_DEBUG "[WSM] TX is locked.\n"); + if (priv->bh_error) { + wsm_printk(KERN_ERR "fatal error occured, " + "could not take lock\n"); + } else { + WARN_ON(wait_event_timeout(priv->bh_evt_wq, + !priv->hw_bufs_used, + WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); + wsm_printk(KERN_DEBUG "[WSM] TX is locked.\n"); + } } wsm_cmd_unlock(priv); } @@ -1098,20 +1104,29 @@ void wsm_lock_tx_async(struct cw1200_common *priv) void wsm_flush_tx(struct cw1200_common *priv) { - BUG_ON(!atomic_read(&priv->tx_lock)); - WARN_ON(wait_event_timeout(priv->bh_evt_wq, - !priv->hw_bufs_used, WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); + if (priv->bh_error) + wsm_printk(KERN_ERR "fatal error occured, will not flush\n"); + else { + BUG_ON(!atomic_read(&priv->tx_lock)); + WARN_ON(wait_event_timeout(priv->bh_evt_wq, + !priv->hw_bufs_used, + WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); + } } void wsm_unlock_tx(struct cw1200_common *priv) { int tx_lock; - tx_lock = atomic_sub_return(1, &priv->tx_lock); - if (tx_lock < 0) { - BUG_ON(1); - } else if (tx_lock == 0) { - cw1200_bh_wakeup(priv); - wsm_printk(KERN_DEBUG "[WSM] TX is unlocked.\n"); + if (priv->bh_error) + wsm_printk(KERN_ERR "fatal error occured, unlock is unsafe\n"); + else { + tx_lock = atomic_sub_return(1, &priv->tx_lock); + if (tx_lock < 0) { + BUG_ON(1); + } else if (tx_lock == 0) { + cw1200_bh_wakeup(priv); + wsm_printk(KERN_DEBUG "[WSM] TX is unlocked.\n"); + } } } @@ -1133,6 +1148,12 @@ int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len) "unknown error", }; +#if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) + /* Send the event upwards on the FW exception */ + cw1200_pm_stay_awake(&priv->pm_state, 3*HZ); + ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL); +#endif + buf.begin = buf.data = data; buf.end = &buf.begin[len]; -- cgit v1.2.3 From 8dbf0822728423475733c21d42a19c52479d812e Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:15:23 +0100 Subject: cw1200: Dynamic BA. Patch implements enabling of block ACK only if valuable data traffic is detected on the interface. BA is disabled for small packets (in assumption of voice/video) and low-thoughput data traffic. ST-Ericsson ID: 405634, 407777 ST-Ericsson FOSS-OUT ID: NA Change-Id: Ic30b65a5b8ea83fe6c865866209a786f26d00c18 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/41129 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/ap.c | 7 ------ drivers/staging/cw1200/cw1200.h | 12 +++++++++ drivers/staging/cw1200/debug.c | 14 +++++++++++ drivers/staging/cw1200/debug.h | 14 +++++++++++ drivers/staging/cw1200/main.c | 8 ++++++ drivers/staging/cw1200/pm.c | 10 ++++++++ drivers/staging/cw1200/sta.c | 56 +++++++++++++++++++++++++++++++++++++++++ drivers/staging/cw1200/sta.h | 2 ++ drivers/staging/cw1200/txrx.c | 25 +++++++++++++++++- 9 files changed, 140 insertions(+), 8 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index ce82ba5e87f..9f7f38681c1 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -496,13 +496,6 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, priv->beacon_int * priv->join_dtim_period > MAX_BEACON_SKIP_TIME_MS ? 1 : priv->join_dtim_period, 0)); - if (sta && cw1200_is_ht(&priv->ht_info)) { - ap_printk(KERN_DEBUG - "[STA] Enabling Block ACK\n"); - WARN_ON(wsm_set_block_ack_policy(priv, - priv->ba_tid_mask, - priv->ba_tid_mask)); - } cw1200_set_pm(priv, &priv->powersave_mode); if (priv->vif->p2p) { diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index ff69098c8a7..3ae1b915566 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -52,6 +52,11 @@ #define CW1200_MAX_TID (8) +#define CW1200_BLOCK_ACK_CNT (30) +#define CW1200_BLOCK_ACK_THLD (800) +#define CW1200_BLOCK_ACK_HIST (3) +#define CW1200_BLOCK_ACK_INTERVAL (1 * HZ / CW1200_BLOCK_ACK_HIST) + /* Please keep order */ enum cw1200_join_status { CW1200_JOIN_STATUS_PASSIVE = 0, @@ -156,6 +161,13 @@ struct cw1200_common { bool disable_beacon_filter; struct work_struct update_filtering_work; u8 ba_tid_mask; + int ba_acc; + int ba_cnt; + int ba_hist; + struct timer_list ba_timer; + spinlock_t ba_lock; + bool ba_ena; + struct work_struct ba_work; struct cw1200_pm_state pm_state; struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo; struct wsm_uapsd_info uapsd_info; diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 2056101a3f4..43b2c257c57 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -105,6 +105,17 @@ static int cw1200_status_show(struct seq_file *seq, void *v) struct list_head *item; struct cw1200_common *priv = seq->private; struct cw1200_debug_priv *d = priv->debug; + int ba_cnt, ba_acc, ba_avg = 0; + bool ba_ena; + + spin_lock_bh(&priv->ba_lock); + ba_cnt = priv->debug->ba_cnt; + ba_acc = priv->debug->ba_acc; + ba_ena = priv->ba_ena; + if (ba_cnt) + ba_avg = ba_acc / ba_cnt; + spin_unlock_bh(&priv->ba_lock); + seq_puts(seq, "CW1200 Wireless LAN driver status\n"); seq_printf(seq, "Hardware: %d.%d\n", priv->wsm_caps.hardwareId, @@ -216,6 +227,9 @@ static int cw1200_status_show(struct seq_file *seq, void *v) ++i; spin_unlock_bh(&priv->tx_policy_cache.lock); seq_printf(seq, "RC in use: %d\n", i); + seq_printf(seq, "BA stat: %d, %d (%d)\n", + ba_cnt, ba_acc, ba_avg); + seq_printf(seq, "Block ACK: %s\n", ba_ena ? "on" : "off"); seq_puts(seq, "\n"); for (i = 0; i < 4; ++i) { diff --git a/drivers/staging/cw1200/debug.h b/drivers/staging/cw1200/debug.h index 02943e699b8..c901a6a64e9 100644 --- a/drivers/staging/cw1200/debug.h +++ b/drivers/staging/cw1200/debug.h @@ -29,6 +29,8 @@ struct cw1200_debug_priv { int tx_ttl; int tx_burst; int rx_burst; + int ba_cnt; + int ba_acc; }; int cw1200_debug_init(struct cw1200_common *priv); @@ -86,6 +88,13 @@ static inline void cw1200_debug_rx_burst(struct cw1200_common *priv) ++priv->debug->rx_burst; } +static inline void cw1200_debug_ba(struct cw1200_common *priv, + int ba_cnt, int ba_acc) +{ + priv->debug->ba_cnt = ba_cnt; + priv->debug->ba_acc = ba_acc; +} + #else /* CONFIG_CW1200_DEBUGFS */ static inline int cw1200_debug_init(struct cw1200_common *priv) @@ -138,6 +147,11 @@ static inline void cw1200_debug_rx_burst(struct cw1200_common *priv) { } +static inline void cw1200_debug_ba(struct cw1200_common *priv, + int ba_cnt, int ba_acc) +{ +} + #endif /* CONFIG_CW1200_DEBUGFS */ #endif /* CW1200_DEBUG_H_INCLUDED */ diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index f6ddac68823..a6f920e3399 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -348,9 +348,14 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) INIT_WORK(&priv->linkid_reset_work, cw1200_link_id_reset); #endif INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work); + INIT_WORK(&priv->ba_work, cw1200_ba_work); init_timer(&priv->mcast_timeout); priv->mcast_timeout.data = (unsigned long)priv; priv->mcast_timeout.function = cw1200_mcast_timeout; + spin_lock_init(&priv->ba_lock); + init_timer(&priv->ba_timer); + priv->ba_timer.data = (unsigned long)priv; + priv->ba_timer.function = cw1200_ba_timer; if (unlikely(cw1200_pm_init(&priv->pm_state, priv))) { ieee80211_free_hw(hw); @@ -430,6 +435,9 @@ void cw1200_unregister_common(struct ieee80211_hw *dev) ieee80211_unregister_hw(dev); + del_timer_sync(&priv->mcast_timeout); + del_timer_sync(&priv->ba_timer); + priv->sbus_ops->irq_unsubscribe(priv->sbus_priv); cw1200_unregister_bh(priv); diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c index 550ee1482b1..fc9c2354758 100644 --- a/drivers/staging/cw1200/pm.c +++ b/drivers/staging/cw1200/pm.c @@ -274,6 +274,9 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) if (ret) goto revert5; + /* Cancel block ack stat timer */ + del_timer_sync(&priv->ba_timer); + /* Store suspend state */ pm_state->suspend_state = state; @@ -352,6 +355,13 @@ int cw1200_wow_resume(struct ieee80211_hw *hw) cw1200_resume_work(priv, &priv->link_id_gc_work, state->link_id_gc); + /* Restart block ack stat */ + spin_lock_bh(&priv->ba_lock); + if (priv->ba_cnt) + mod_timer(&priv->ba_timer, + jiffies + CW1200_BLOCK_ACK_INTERVAL); + spin_unlock_bh(&priv->ba_lock); + /* Unlock datapath */ wsm_unlock_tx(priv); diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 85e57d2f7b3..c5b79f25060 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -123,6 +123,7 @@ void cw1200_stop(struct ieee80211_hw *dev) cancel_delayed_work_sync(&priv->link_id_gc_work); flush_workqueue(priv->workqueue); del_timer_sync(&priv->mcast_timeout); + del_timer_sync(&priv->ba_timer); mutex_lock(&priv->conf_mutex); priv->mode = NL80211_IFTYPE_UNSPECIFIED; @@ -1360,6 +1361,13 @@ void cw1200_join_work(struct work_struct *work) WARN_ON(wsm_set_block_ack_policy(priv, 0, priv->ba_tid_mask)); + spin_lock_bh(&priv->ba_lock); + priv->ba_ena = false; + priv->ba_cnt = 0; + priv->ba_acc = 0; + priv->ba_hist = 0; + spin_unlock_bh(&priv->ba_lock); + mgmt_policy.protectedMgmtEnable = 0; mgmt_policy.unprotectedMgmtFramesAllowed = 1; mgmt_policy.encryptionForAuthFrame = 1; @@ -1409,6 +1417,7 @@ void cw1200_unjoin_work(struct work_struct *work) .reset_statistics = true, }; + del_timer_sync(&priv->ba_timer); mutex_lock(&priv->conf_mutex); if (unlikely(atomic_read(&priv->scan.in_progress))) { if (priv->delayed_unjoin) { @@ -1543,6 +1552,53 @@ int cw1200_set_uapsd_param(struct cw1200_common *priv, return ret; } +void cw1200_ba_work(struct work_struct *work) +{ + struct cw1200_common *priv = + container_of(work, struct cw1200_common, ba_work); + u8 tx_ba_tid_mask; + + if (priv->join_status != CW1200_JOIN_STATUS_STA) + return; + if (!priv->setbssparams_done) + return; + + spin_lock_bh(&priv->ba_lock); + tx_ba_tid_mask = priv->ba_ena ? priv->ba_tid_mask : 0; + spin_unlock_bh(&priv->ba_lock); + + WARN_ON(wsm_set_block_ack_policy(priv, + tx_ba_tid_mask, priv->ba_tid_mask)); +} + +void cw1200_ba_timer(unsigned long arg) +{ + bool ba_ena; + struct cw1200_common *priv = + (struct cw1200_common *)arg; + + spin_lock_bh(&priv->ba_lock); + cw1200_debug_ba(priv, priv->ba_cnt, priv->ba_acc); + + ba_ena = (priv->ba_cnt >= CW1200_BLOCK_ACK_CNT && + priv->ba_acc / priv->ba_cnt >= CW1200_BLOCK_ACK_THLD); + priv->ba_cnt = 0; + priv->ba_acc = 0; + + if (ba_ena != priv->ba_ena) { + if (ba_ena || ++priv->ba_hist >= CW1200_BLOCK_ACK_HIST) { + priv->ba_ena = ba_ena; + priv->ba_hist = 0; + sta_printk(KERN_DEBUG "[STA] %s block ACK:\n", + ba_ena ? "enable" : "disable"); + queue_work(priv->workqueue, &priv->ba_work); + } + } else if (priv->ba_hist) + --priv->ba_hist; + + spin_unlock_bh(&priv->ba_lock); +} + /* ******************************************************************** */ /* STA privates */ diff --git a/drivers/staging/cw1200/sta.h b/drivers/staging/cw1200/sta.h index e1257828004..4e4833afcf7 100644 --- a/drivers/staging/cw1200/sta.h +++ b/drivers/staging/cw1200/sta.h @@ -81,5 +81,7 @@ int cw1200_enable_listening(struct cw1200_common *priv); int cw1200_disable_listening(struct cw1200_common *priv); int cw1200_set_uapsd_param(struct cw1200_common *priv, const struct wsm_edca_params *arg); +void cw1200_ba_work(struct work_struct *work); +void cw1200_ba_timer(unsigned long arg); #endif diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index fb80d218dfa..39470c46f23 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -738,7 +738,7 @@ cw1200_tx_h_rate_policy(struct cw1200_common *priv, static bool cw1200_tx_h_pm_state(struct cw1200_common *priv, - struct cw1200_txinfo *t) + struct cw1200_txinfo *t) { int was_buffered = 1; @@ -757,6 +757,28 @@ cw1200_tx_h_pm_state(struct cw1200_common *priv, return !was_buffered; } +static void +cw1200_tx_h_ba_stat(struct cw1200_common *priv, + struct cw1200_txinfo *t) +{ + if (priv->join_status != CW1200_JOIN_STATUS_STA) + return; + if (!cw1200_is_ht(&priv->ht_info)) + return; + if (!priv->setbssparams_done) + return; + if (!ieee80211_is_data(t->hdr->frame_control)) + return; + + spin_lock_bh(&priv->ba_lock); + priv->ba_acc += t->skb->len - t->hdrlen; + if (!priv->ba_cnt++) { + mod_timer(&priv->ba_timer, + jiffies + CW1200_BLOCK_ACK_INTERVAL); + } + spin_unlock_bh(&priv->ba_lock); +} + /* ******************************************************************** */ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) @@ -818,6 +840,7 @@ void cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb) rcu_read_lock(); sta = rcu_dereference(t.tx_info->control.sta); + cw1200_tx_h_ba_stat(priv, &t); spin_lock_bh(&priv->ps_state_lock); { tid_update = cw1200_tx_h_pm_state(priv, &t); -- cgit v1.2.3 From e6e50032257783c06d65c4c6d0e302cf597adc9e Mon Sep 17 00:00:00 2001 From: Szymon Bigos Date: Wed, 29 Feb 2012 15:15:24 +0100 Subject: cw1200: Fix disabling BlockAck on scan. Traffic is measured during scan, so BlockAck was often disabled during/after scan. Now low traffic during scan does not increase low traffic history. ST-Ericsson ID: 405634, 407777 ST-Ericsson FOSS-OUT ID: NA Change-Id: Iff863c36fdbed870669a0835125249cca34954e9 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/43859 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/sta.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index c5b79f25060..a540a592fdd 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1580,6 +1580,9 @@ void cw1200_ba_timer(unsigned long arg) spin_lock_bh(&priv->ba_lock); cw1200_debug_ba(priv, priv->ba_cnt, priv->ba_acc); + if (atomic_read(&priv->scan.in_progress)) + goto skip_statistic_update; + ba_ena = (priv->ba_cnt >= CW1200_BLOCK_ACK_CNT && priv->ba_acc / priv->ba_cnt >= CW1200_BLOCK_ACK_THLD); priv->ba_cnt = 0; @@ -1596,6 +1599,7 @@ void cw1200_ba_timer(unsigned long arg) } else if (priv->ba_hist) --priv->ba_hist; +skip_statistic_update: spin_unlock_bh(&priv->ba_lock); } -- cgit v1.2.3 From ee498a1b8450845512b9262db8facd730421a272 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 29 Feb 2012 15:15:25 +0100 Subject: cw1200: Fix compiler warnings ST-Ericsson ID: 408523 ST-Ericsson FOSS-OUT ID: NA Change-Id: I2823026d9e5d522a032028a63fb99699310c919c Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44448 Reviewed-by: QATOOLS Reviewed-by: QABUILD --- drivers/staging/cw1200/ap.c | 2 -- drivers/staging/cw1200/sta.c | 16 ---------------- drivers/staging/cw1200/txrx.c | 1 - 3 files changed, 19 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 9f7f38681c1..fb903ad285a 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -154,8 +154,6 @@ void cw1200_sta_notify(struct ieee80211_hw *dev, static void cw1200_ps_notify(struct cw1200_common *priv, int link_id, bool ps) { - struct ieee80211_sta *sta; - if (link_id > CW1200_MAX_STA_IN_AP_MODE) return; diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index a540a592fdd..ef0f59dd7c4 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -26,9 +26,6 @@ #define sta_printk(...) #endif - -static int cw1200_cancel_scan(struct cw1200_common *priv); - static inline void __cw1200_free_event_queue(struct list_head *list) { while (!list_empty(list)) { @@ -1602,16 +1599,3 @@ void cw1200_ba_timer(unsigned long arg) skip_statistic_update: spin_unlock_bh(&priv->ba_lock); } - -/* ******************************************************************** */ -/* STA privates */ - -static int cw1200_cancel_scan(struct cw1200_common *priv) -{ - while (down_trylock(&priv->scan.lock)) { - priv->scan.req = NULL; - schedule(); - } - up(&priv->scan.lock); - return 0; -} diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index 39470c46f23..f568c6275c3 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -1329,7 +1329,6 @@ void cw1200_link_id_reset(struct work_struct *work) struct cw1200_common *priv = container_of(work, struct cw1200_common, linkid_reset_work); int temp_linkid; - struct cw1200_link_entry *entry; if (!priv->action_linkid) { /* In GO mode we can receive ACTION frames without a linkID */ -- cgit v1.2.3 From 4375ad44d3db59085ecb351bd0bad19d31645fbb Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:15:26 +0100 Subject: cw1200: Enable REPORTS_TX_ACK_STATUS HW CAP bit. High power consumption was reported with a particular AP. 1. IEEE80211_HW_REPORTS_TX_ACK_STATUS bit was not set in the driver, so direct probe responses were used for probing BSS after wakeup instead of more efficient null-frame requests. 2. The AP does not answer to direct probes at all. 3. Both stack (explicitly) and firmware (implicitly) configure IEEE PS after exiting scan. And they were doing it in a right opposite way. Sometimes device entered full-power state instead of IEEE PS state. The fix changes probing policy from probe requests to null frames. ST-Ericsson ID: 410123 ST-Ericsson FOSS-OUT ID: NA Change-Id: I5be243ef861c173ba9c0a36a069dd63365234ba1 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/45658 Reviewed-by: QATOOLS Tested-by: Janusz DZIEDZIC Reviewed-by: Janusz DZIEDZIC --- drivers/staging/cw1200/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index a6f920e3399..579aee899c5 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -253,6 +253,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | + IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_CQM_RSSI | -- cgit v1.2.3 From b0132d12aaa9695ec9bc0a779d6fccd14c7e43a2 Mon Sep 17 00:00:00 2001 From: Vitaly Wool Date: Wed, 29 Feb 2012 15:15:27 +0100 Subject: cw1200: Bypass BSS loss confirmation in P2P mode BSS loss confirmation mechanism is used as a workaround for some buggy APs that don't send beacons after join. This mechanism has some side effects in P2P case which we'd rather avoid so let's just bypass it then. ST-Ericsson ID: 411069 Change-Id: Ie33b3eb4452b9ebce5e1fc0ab49dbabe5f5755c1 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/46079 Reviewed-by: Filip MATUSIAK Tested-by: Filip MATUSIAK Reviewed-by: Janusz DZIEDZIC --- drivers/staging/cw1200/sta.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index ef0f59dd7c4..2dd90c3b929 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1015,6 +1015,10 @@ void cw1200_bss_loss_work(struct work_struct *work) timeout = priv->cqm_link_loss_count - priv->cqm_beacon_loss_count; + /* Skip the confimration procedure in P2P case */ + if (priv->vif->p2p) + goto report; + spin_lock(&priv->bss_loss_lock); if (priv->bss_loss_status == CW1200_BSS_LOSS_CHECKING) { spin_unlock(&priv->bss_loss_lock); @@ -1034,6 +1038,7 @@ void cw1200_bss_loss_work(struct work_struct *work) } spin_unlock(&priv->bss_loss_lock); +report: if (priv->cqm_beacon_loss_count) { sta_printk(KERN_DEBUG "[CQM] Beacon loss.\n"); if (timeout <= 0) -- cgit v1.2.3 From 28c5c8e6c34d13328ce217e7fc94374f9902368c Mon Sep 17 00:00:00 2001 From: Piotr Nakraszewicz Date: Wed, 29 Feb 2012 15:15:28 +0100 Subject: cw1200: Set empty probe response template ST-Ericsson ID: 409603 ST-Ericsson FOSS-OUT ID: Trivial In order to receive probe request from FW set empty probe response template before scan. Change-Id: I3c17027c1bf30b9915a21d735e32e824f84a5f41 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/46094 Tested-by: Piotr NAKRASZEWICZ Reviewed-by: Janusz DZIEDZIC --- drivers/staging/cw1200/scan.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index 8a113463c91..da5a966ed8a 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -74,6 +74,15 @@ int cw1200_hw_scan(struct ieee80211_hw *hw, mutex_lock(&priv->conf_mutex); if (frame.skb) { int ret = wsm_set_template_frame(priv, &frame); + if (0 == ret) { + /* + * set empty probe response template in order + * to receive probe requests from firmware + */ + frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE; + frame.disable = true; + ret = wsm_set_template_frame(priv, &frame); + } if (ret) { mutex_unlock(&priv->conf_mutex); up(&priv->scan.lock); -- cgit v1.2.3 From 1d980979fd378e33bbb52c21961c63687679b3db Mon Sep 17 00:00:00 2001 From: Szymon Bigos Date: Wed, 29 Feb 2012 15:15:29 +0100 Subject: cw1200: Add Operational Mode setting. Now, after Start Request, Operational Mode setting is performed when device go into P2P_GO mode or AP mode. ST-Ericsson ID: 407162 ST-Ericsson FOSS-OUT ID: NA Change-Id: I5c3246abfa1ab6d4bcd2925b22f219f22b7a31c1 Signed-off-by: Szymon Bigos Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/44815 Reviewed-by: Szymon BIGOS Tested-by: Szymon BIGOS Reviewed-by: Janusz DZIEDZIC --- drivers/staging/cw1200/ap.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index fb903ad285a..17de92d5da0 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -860,6 +860,10 @@ static int cw1200_start_ap(struct cw1200_common *priv) .basicRateSet = cw1200_rate_mask_to_wsm(priv, conf->basic_rates), }; + struct wsm_operational_mode mode = { + .power_mode = wsm_power_mode_quiescent, + .disableMoreFlagUsage = true, + }; /* Get SSID */ skb = ieee80211_beacon_get(priv->hw, priv->vif); @@ -911,6 +915,7 @@ static int cw1200_start_ap(struct cw1200_common *priv) priv->join_status = CW1200_JOIN_STATUS_AP; cw1200_update_filtering(priv); } + WARN_ON(wsm_set_operational_mode(priv, &mode)); return ret; } -- cgit v1.2.3 From d041782c503a1967d40fd6273f22f3cb3d53b4b3 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 29 Feb 2012 15:15:30 +0100 Subject: cw1200: Skip packets with Rssi equal to zero. Do not handle beacons and probe responses with wrong Rssi value. This is a workaround for FW. Sometimes Rssi value in frames from FW is zero. It causes confusion in userspace, so it is better to detect malformed packets and skip them. ST-Ericsson ID: 412823 ST-Ericsson FOSS-OUT ID: NA Change-Id: Iac97485dc87f50143e75625b22e36b42381da25f Signed-off-by: Marek Puzyniak Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/47567 Reviewed-by: QATOOLS Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/wsm.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index bb94d19da08..085868b532e 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -858,6 +858,7 @@ static int wsm_receive_indication(struct cw1200_common *priv, priv->rx_timestamp = jiffies; if (priv->wsm_cbc.rx) { struct wsm_rx rx; + struct ieee80211_hdr *hdr; size_t hdr_len; __le16 fctl; @@ -866,6 +867,16 @@ static int wsm_receive_indication(struct cw1200_common *priv, rx.rxedRate = WSM_GET8(buf); rx.rcpiRssi = WSM_GET8(buf); rx.flags = WSM_GET32(buf); + + /* FW Workaround: Drop probe resp or + beacon when RSSI is 0 */ + hdr = (struct ieee80211_hdr *) (*skb_p)->data; + + if (!rx.rcpiRssi && + (ieee80211_is_probe_resp(hdr->frame_control) || + ieee80211_is_beacon(hdr->frame_control))) + return 0; + rx.link_id = link_id; fctl = *(__le16 *)buf->data; hdr_len = buf->data - buf->begin; -- cgit v1.2.3 From e9ae53167bf730792813618295e2f5654ee0d91c Mon Sep 17 00:00:00 2001 From: Lukasz Kucharczyk Date: Wed, 29 Feb 2012 15:15:31 +0100 Subject: cw1200: ITP Commands: 1,,: Start RX test 2: Show RX statistics 3: Stop RX/TX test 4,,,,, ,,, ,: Start TX test. 5: Get chip ID. 6: Get firmware ID. Return codes: 0[,...] command executed succesfully 1,,,, ,: response to commands 2, 3. 2,: response to commands 5, 6 -,: Error ST-Ericsson ID: 359166 Signed-off-by: Dmitry Tarnyagin Signed-off-by: Lukasz Kucharczyk Change-Id: I97b58b6e662f6f28fdd24241bb6b1dc431189b20 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/47731 Reviewed-by: QABUILD Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/Kconfig | 14 + drivers/staging/cw1200/Makefile | 1 + drivers/staging/cw1200/debug.c | 8 +- drivers/staging/cw1200/debug.h | 11 + drivers/staging/cw1200/itp.c | 739 ++++++++++++++++++++++++++++++++++++++++ drivers/staging/cw1200/itp.h | 151 ++++++++ drivers/staging/cw1200/txrx.c | 10 +- drivers/staging/cw1200/wsm.c | 5 + 8 files changed, 936 insertions(+), 3 deletions(-) create mode 100644 drivers/staging/cw1200/itp.c create mode 100644 drivers/staging/cw1200/itp.h diff --git a/drivers/staging/cw1200/Kconfig b/drivers/staging/cw1200/Kconfig index 3a4fc9813c6..044f7e6585a 100644 --- a/drivers/staging/cw1200/Kconfig +++ b/drivers/staging/cw1200/Kconfig @@ -42,6 +42,13 @@ config CW1200_WAPI_SUPPORT Say Y if your compat-wireless support WAPI. If unsure, say N. +config CW1200_USE_STE_EXTENSIONS + bool "STE extensions" + depends on CW1200 + help + Say Y if you want to include STE extensions. + If unsure, say N. + config CW1200_DISABLE_BEACON_HINTS bool "Disable 11d beacon hints" depends on CW1200 @@ -82,4 +89,11 @@ config CW1200_STA_DEBUG endmenu +config CW1200_ITP + bool "Enable ITP DebugFS" + depends on CW1200 + help + Say Y if you want to include ITP code. + If unsure, say N. + endif diff --git a/drivers/staging/cw1200/Makefile b/drivers/staging/cw1200/Makefile index c0e88bd0f11..67d7867c1b5 100644 --- a/drivers/staging/cw1200/Makefile +++ b/drivers/staging/cw1200/Makefile @@ -10,6 +10,7 @@ cw1200_core-y := \ ap.o \ scan.o cw1200_core-$(CONFIG_CW1200_DEBUGFS) += debug.o +cw1200_core-$(CONFIG_CW1200_ITP) += itp.o cw1200_core-$(CONFIG_PM) += pm.o cw1200_wlan-y := cw1200_sdio.o diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 43b2c257c57..3a8b79f7556 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -470,11 +470,12 @@ static const struct file_operations fops_hang = { int cw1200_debug_init(struct cw1200_common *priv) { + int ret = -ENOMEM; struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv), GFP_KERNEL); priv->debug = d; if (!d) - return -ENOMEM; + return ret; d->debugfs_phy = debugfs_create_dir("cw1200", priv->hw->wiphy->debugfsdir); @@ -511,6 +512,11 @@ err: void cw1200_debug_release(struct cw1200_common *priv) { struct cw1200_debug_priv *d = priv->debug; + if (d) { + cw1200_itp_release(priv); + priv->debug = NULL; + kfree(d); + } priv->debug = NULL; if (d) { diff --git a/drivers/staging/cw1200/debug.h b/drivers/staging/cw1200/debug.h index c901a6a64e9..72b827f296b 100644 --- a/drivers/staging/cw1200/debug.h +++ b/drivers/staging/cw1200/debug.h @@ -12,6 +12,8 @@ #ifndef CW1200_DEBUG_H_INCLUDED #define CW1200_DEBUG_H_INCLUDED +#include "itp.h" + struct cw200_common; #ifdef CONFIG_CW1200_DEBUGFS @@ -31,6 +33,9 @@ struct cw1200_debug_priv { int rx_burst; int ba_cnt; int ba_acc; +#ifdef CONFIG_CW1200_ITP + struct cw1200_itp itp; +#endif /* CONFIG_CW1200_ITP */ }; int cw1200_debug_init(struct cw1200_common *priv); @@ -95,6 +100,8 @@ static inline void cw1200_debug_ba(struct cw1200_common *priv, priv->debug->ba_acc = ba_acc; } +int cw1200_print_fw_version(struct cw1200_common *priv, u8 *buf, size_t len); + #else /* CONFIG_CW1200_DEBUGFS */ static inline int cw1200_debug_init(struct cw1200_common *priv) @@ -152,6 +159,10 @@ static inline void cw1200_debug_ba(struct cw1200_common *priv, { } +int cw1200_print_fw_version(struct cw1200_common *priv, u8 *buf, size_t len) +{ +} + #endif /* CONFIG_CW1200_DEBUGFS */ #endif /* CW1200_DEBUG_H_INCLUDED */ diff --git a/drivers/staging/cw1200/itp.c b/drivers/staging/cw1200/itp.c new file mode 100644 index 00000000000..eb7e53bf096 --- /dev/null +++ b/drivers/staging/cw1200/itp.c @@ -0,0 +1,739 @@ +/* + * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers + * ITP code + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include "cw1200.h" +#include "debug.h" +#include "itp.h" +#include "sta.h" + +static int __cw1200_itp_open(struct cw1200_common *priv); +static int __cw1200_itp_close(struct cw1200_common *priv); +static void cw1200_itp_rx_start(struct cw1200_common *priv); +static void cw1200_itp_rx_stop(struct cw1200_common *priv); +static void cw1200_itp_rx_stats(struct cw1200_common *priv); +static void cw1200_itp_rx_reset(struct cw1200_common *priv); +static void cw1200_itp_tx_stop(struct cw1200_common *priv); +static void cw1200_itp_handle(struct cw1200_common *priv, + struct sk_buff *skb); +static void cw1200_itp_err(struct cw1200_common *priv, + int err, + int arg); +static void __cw1200_itp_tx_stop(struct cw1200_common *priv); + +static ssize_t cw1200_itp_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct cw1200_common *priv = file->private_data; + struct cw1200_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + int ret; + + if (skb_queue_empty(&itp->log_queue)) + return 0; + + skb = skb_dequeue(&itp->log_queue); + ret = copy_to_user(user_buf, skb->data, skb->len); + *ppos += skb->len; + skb->data[skb->len] = 0; + itp_printk(KERN_DEBUG "[ITP] >>> %s", skb->data); + consume_skb(skb); + + return skb->len - ret; +} + +static ssize_t cw1200_itp_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct cw1200_common *priv = file->private_data; + struct sk_buff *skb; + + if (!count || count > 1024) + return -EINVAL; + skb = dev_alloc_skb(count + 1); + if (!skb) + return -ENOMEM; + skb_trim(skb, 0); + skb_put(skb, count + 1); + if (copy_from_user(skb->data, user_buf, count)) { + kfree_skb(skb); + return -EFAULT; + } + skb->data[count] = 0; + + cw1200_itp_handle(priv, skb); + consume_skb(skb); + return count; +} + +static unsigned int cw1200_itp_poll(struct file *file, poll_table *wait) +{ + struct cw1200_common *priv = file->private_data; + struct cw1200_itp *itp = &priv->debug->itp; + unsigned int mask = 0; + + poll_wait(file, &itp->read_wait, wait); + + if (!skb_queue_empty(&itp->log_queue)) + mask |= POLLIN | POLLRDNORM; + + mask |= POLLOUT | POLLWRNORM; + + return mask; +} + +static int cw1200_itp_open(struct inode *inode, struct file *file) +{ + struct cw1200_common *priv = inode->i_private; + struct cw1200_itp *itp = &priv->debug->itp; + int ret = 0; + + file->private_data = priv; + if (atomic_inc_return(&itp->open_count) == 1) { + ret = __cw1200_itp_open(priv); + if (ret && !atomic_dec_return(&itp->open_count)) + __cw1200_itp_close(priv); + } else { + atomic_dec(&itp->open_count); + ret = -EBUSY; + } + + return ret; +} + +static int cw1200_itp_close(struct inode *inode, struct file *file) +{ + struct cw1200_common *priv = file->private_data; + struct cw1200_itp *itp = &priv->debug->itp; + if (!atomic_dec_return(&itp->open_count)) { + __cw1200_itp_close(priv); + wake_up(&itp->close_wait); + } + return 0; +} + +static const struct file_operations fops_itp = { + .open = cw1200_itp_open, + .read = cw1200_itp_read, + .write = cw1200_itp_write, + .poll = cw1200_itp_poll, + .release = cw1200_itp_close, + .llseek = default_llseek, + .owner = THIS_MODULE, +}; + +static void cw1200_itp_fill_pattern(u8 *data, int size, + enum cw1200_itp_data_modes mode) +{ + u8 *p = data; + + if (size <= 0) + return; + + switch (mode) { + default: + case ITP_DATA_ZEROS: + memset(data, 0x0, size); + break; + case ITP_DATA_ONES: + memset(data, 0xff, size); + break; + case ITP_DATA_ZERONES: + memset(data, 0x55, size); + break; + case ITP_DATA_RANDOM: + while (p < data+size-sizeof(u32)) { + (*(u32 *)p) = random32(); + p += sizeof(u32); + } + while (p < data+size) { + (*p) = random32() & 0xFF; + p++; + } + break; + } + return; +} + +static void cw1200_itp_tx_work(struct work_struct *work) +{ + struct cw1200_itp *itp = container_of(work, struct cw1200_itp, + tx_work.work); + struct cw1200_common *priv = itp->priv; + atomic_set(&priv->bh_tx, 1); + wake_up(&priv->bh_wq); +} + +static void cw1200_itp_tx_finish(struct work_struct *work) +{ + struct cw1200_itp *itp = container_of(work, struct cw1200_itp, + tx_finish.work); + __cw1200_itp_tx_stop(itp->priv); +} + +int cw1200_itp_init(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + + itp->priv = priv; + atomic_set(&itp->open_count, 0); + atomic_set(&itp->stop_tx, 0); + atomic_set(&itp->awaiting_confirm, 0); + skb_queue_head_init(&itp->log_queue); + spin_lock_init(&itp->tx_lock); + init_waitqueue_head(&itp->read_wait); + init_waitqueue_head(&itp->write_wait); + init_waitqueue_head(&itp->close_wait); + INIT_DELAYED_WORK(&itp->tx_work, cw1200_itp_tx_work); + INIT_DELAYED_WORK(&itp->tx_finish, cw1200_itp_tx_finish); + itp->data = NULL; + itp->hdr_len = WSM_TX_EXTRA_HEADROOM + + sizeof(struct ieee80211_hdr_3addr); + + if (!debugfs_create_file("itp", S_IRUSR | S_IWUSR, + priv->debug->debugfs_phy, priv, &fops_itp)) + return -ENOMEM; + + return 0; +} + +void cw1200_itp_release(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + + wait_event_interruptible(itp->close_wait, + !atomic_read(&itp->open_count)); + + WARN_ON(atomic_read(&itp->open_count)); + + skb_queue_purge(&itp->log_queue); + cw1200_itp_tx_stop(priv); +} + +static int __cw1200_itp_open(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + + if (!priv->vif) + return -EINVAL; + if (priv->join_status) + return -EINVAL; + itp->saved_channel = priv->channel; + if (!priv->channel) + priv->channel = &priv->hw-> + wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; + wsm_set_bssid_filtering(priv, false); + cw1200_itp_rx_reset(priv); + return 0; +} + +static int __cw1200_itp_close(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) + cw1200_itp_rx_stop(priv); + cw1200_itp_tx_stop(priv); + cw1200_disable_listening(priv); + cw1200_update_filtering(priv); + priv->channel = itp->saved_channel; + return 0; +} + +bool cw1200_is_itp(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + return atomic_read(&itp->open_count) != 0; +} + +static void cw1200_itp_rx_reset(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + itp->rx_cnt = 0; + itp->rx_rssi = 0; + itp->rx_rssi_max = -1000; + itp->rx_rssi_min = 1000; +} + +static void cw1200_itp_rx_start(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + + itp_printk(KERN_DEBUG "[ITP] RX start, band = %d, ch = %d\n", + itp->band, itp->ch); + atomic_set(&itp->test_mode, TEST_MODE_RX_TEST); + cw1200_update_listening(priv, false); + priv->channel = &priv->hw-> + wiphy->bands[itp->band]->channels[itp->ch]; + cw1200_update_listening(priv, true); + wsm_set_bssid_filtering(priv, false); +} + +static void cw1200_itp_rx_stop(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + itp_printk(KERN_DEBUG "[ITP] RX stop\n"); + atomic_set(&itp->test_mode, TEST_MODE_NO_TEST); + cw1200_itp_rx_reset(priv); +} + +static void cw1200_itp_rx_stats(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + char buf[128]; + int len, ret; + struct wsm_counters_table counters; + + ret = wsm_get_counters_table(priv, &counters); + + if (ret) + cw1200_itp_err(priv, -EBUSY, 20); + + if (!itp->rx_cnt) + len = snprintf(buf, sizeof(buf), "1,0,0,0,0,%d\n", + counters.countRxPacketErrors); + else + len = snprintf(buf, sizeof(buf), "1,%d,%ld,%d,%d,%d\n", + itp->rx_cnt, + itp->rx_cnt ? itp->rx_rssi / itp->rx_cnt : 0, + itp->rx_rssi_min, itp->rx_rssi_max, + counters.countRxPacketErrors); + + if (len <= 0) { + cw1200_itp_err(priv, -EBUSY, 21); + return; + } + + skb = dev_alloc_skb(len); + if (!skb) { + cw1200_itp_err(priv, -ENOMEM, 22); + return; + } + + itp->rx_cnt = 0; + itp->rx_rssi = 0; + itp->rx_rssi_max = -1000; + itp->rx_rssi_min = 1000; + + skb_trim(skb, 0); + skb_put(skb, len); + + memcpy(skb->data, buf, len); + skb_queue_tail(&itp->log_queue, skb); + wake_up(&itp->read_wait); +} + +static void cw1200_itp_tx_start(struct cw1200_common *priv) +{ + struct wsm_tx *tx; + struct ieee80211_hdr_3addr *hdr; + struct cw1200_itp *itp = &priv->debug->itp; + struct wsm_association_mode assoc_mode = { + .flags = WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE, + .preambleType = itp->preamble, + }; + int len; + u8 da_addr[6] = ITP_DEFAULT_DA_ADDR; + + /* Rates index 4 and 5 are not supported */ + if (itp->rate > 3) + itp->rate += 2; + + itp_printk(KERN_DEBUG "[ITP] TX start: band = %d, ch = %d, rate = %d," + " preamble = %d, number = %d, data_mode = %d," + " interval = %d, power = %d, data_len = %d\n", + itp->band, itp->ch, itp->rate, itp->preamble, + itp->number, itp->data_mode, itp->interval_us, + itp->power, itp->data_len); + + len = itp->hdr_len + itp->data_len; + + itp->data = kmalloc(len, GFP_KERNEL); + tx = (struct wsm_tx *)itp->data; + tx->hdr.len = itp->data_len + itp->hdr_len; + tx->hdr.id = __cpu_to_le16(0x0004 | 1 << 6); + tx->maxTxRate = itp->rate; + tx->queueId = 3; + tx->more = 0; + tx->flags = 0xc; + tx->packetID = 0x55ff55; + tx->reserved = 0; + tx->expireTime = 1; + + if (itp->preamble == ITP_PREAMBLE_GREENFIELD) + tx->htTxParameters = WSM_HT_TX_GREENFIELD; + else if (itp->preamble == ITP_PREAMBLE_MIXED) + tx->htTxParameters = WSM_HT_TX_MIXED; + + hdr = (struct ieee80211_hdr_3addr *)&itp->data[sizeof(struct wsm_tx)]; + memset(hdr, 0, sizeof(*hdr)); + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_FCTL_TODS); + memcpy(hdr->addr1, da_addr, ETH_ALEN); + memcpy(hdr->addr2, priv->vif->addr, ETH_ALEN); + memcpy(hdr->addr3, da_addr, ETH_ALEN); + + cw1200_itp_fill_pattern(&itp->data[itp->hdr_len], + itp->data_len, itp->data_mode); + + cw1200_update_listening(priv, false); + priv->channel = &priv->hw-> + wiphy->bands[itp->band]->channels[itp->ch]; + WARN_ON(wsm_set_output_power(priv, itp->power)); + if (itp->preamble == ITP_PREAMBLE_SHORT || + itp->preamble == ITP_PREAMBLE_LONG) + WARN_ON(wsm_set_association_mode(priv, + &assoc_mode)); + wsm_set_bssid_filtering(priv, false); + cw1200_update_listening(priv, true); + + spin_lock_bh(&itp->tx_lock); + atomic_set(&itp->test_mode, TEST_MODE_TX_TEST); + atomic_set(&itp->awaiting_confirm, 0); + atomic_set(&itp->stop_tx, 0); + atomic_set(&priv->bh_tx, 1); + ktime_get_ts(&itp->last_sent); + wake_up(&priv->bh_wq); + spin_unlock_bh(&itp->tx_lock); +} + +void __cw1200_itp_tx_stop(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + spin_lock_bh(&itp->tx_lock); + kfree(itp->data); + itp->data = NULL; + atomic_set(&itp->test_mode, TEST_MODE_NO_TEST); + spin_unlock_bh(&itp->tx_lock); +} + +static void cw1200_itp_tx_stop(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + itp_printk(KERN_DEBUG "[ITP] TX stop\n"); + atomic_set(&itp->stop_tx, 1); + flush_workqueue(priv->workqueue); + + /* time for FW to confirm all tx requests */ + msleep(500); + + __cw1200_itp_tx_stop(priv); +} + +static void cw1200_itp_get_version(struct cw1200_common *priv, + enum cw1200_itp_version_type type) +{ + struct cw1200_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + char buf[ITP_BUF_SIZE]; + size_t size = 0; + int len; + itp_printk(KERN_DEBUG "[ITP] print %s version\n", type == ITP_CHIP_ID ? + "chip" : "firmware"); + + len = snprintf(buf, ITP_BUF_SIZE, "2,"); + if (len <= 0) { + cw1200_itp_err(priv, -EINVAL, 40); + return; + } + size += len; + + switch (type) { + case ITP_CHIP_ID: + len = cw1200_print_fw_version(priv, buf+size, + ITP_BUF_SIZE - size); + + if (len <= 0) { + cw1200_itp_err(priv, -EINVAL, 41); + return; + } + size += len; + break; + case ITP_FW_VER: + len = snprintf(buf+size, ITP_BUF_SIZE - size, + "%d.%d", priv->wsm_caps.hardwareId, + priv->wsm_caps.hardwareSubId); + if (len <= 0) { + cw1200_itp_err(priv, -EINVAL, 42); + return; + } + size += len; + break; + default: + cw1200_itp_err(priv, -EINVAL, 43); + break; + } + + len = snprintf(buf+size, ITP_BUF_SIZE-size, "\n"); + if (len <= 0) { + cw1200_itp_err(priv, -EINVAL, 44); + return; + } + size += len; + + skb = dev_alloc_skb(size); + if (!skb) { + cw1200_itp_err(priv, -ENOMEM, 45); + return; + } + + skb_trim(skb, 0); + skb_put(skb, size); + + memcpy(skb->data, buf, size); + skb_queue_tail(&itp->log_queue, skb); + wake_up(&itp->read_wait); +} + +int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data, + size_t *tx_len, int *burst) +{ + struct cw1200_itp *itp; + struct timespec now; + int time_left_us; + + if (!priv->debug) + return 0; + + itp = &priv->debug->itp; + + if (!itp) + return 0; + + spin_lock_bh(&itp->tx_lock); + if (atomic_read(&itp->test_mode) != TEST_MODE_TX_TEST) + goto out; + + if (atomic_read(&itp->stop_tx)) + goto out; + + if (itp->number == 0) { + atomic_set(&itp->stop_tx, 1); + queue_delayed_work(priv->workqueue, &itp->tx_finish, + HZ/10); + goto out; + } + + if (!itp->data) + goto out; + + if (priv->hw_bufs_used >= 2) { + if (!atomic_read(&priv->bh_rx)) + atomic_set(&priv->bh_rx, 1); + atomic_set(&priv->bh_tx, 1); + goto out; + } + + ktime_get_ts(&now); + time_left_us = (itp->last_sent.tv_sec - + now.tv_sec)*1000000 + + (itp->last_sent.tv_nsec - now.tv_nsec)/1000 + + itp->interval_us; + + if (time_left_us > ITP_TIME_THRES_US) { + queue_delayed_work(priv->workqueue, &itp->tx_work, + ITP_US_TO_MS(time_left_us)*HZ/1000); + goto out; + } + + if (time_left_us > 50) + udelay(time_left_us); + + if (itp->number > 0) + itp->number--; + + *data = itp->data; + *tx_len = itp->data_len + itp->hdr_len; + + if (itp->data_mode == ITP_DATA_RANDOM) + cw1200_itp_fill_pattern(&itp->data[itp->hdr_len], + itp->data_len, itp->data_mode); + *burst = 2; + atomic_set(&priv->bh_tx, 1); + ktime_get_ts(&itp->last_sent); + atomic_add(1, &itp->awaiting_confirm); + spin_unlock_bh(&itp->tx_lock); + return 1; + +out: + spin_unlock_bh(&itp->tx_lock); + return 0; +} + +bool cw1200_itp_rxed(struct cw1200_common *priv, struct sk_buff *skb) +{ + struct cw1200_itp *itp = &priv->debug->itp; + struct ieee80211_rx_status *rx = IEEE80211_SKB_RXCB(skb); + int signal; + + if (atomic_read(&itp->test_mode) != TEST_MODE_RX_TEST) + return cw1200_is_itp(priv); + if (rx->freq != priv->channel->center_freq) + return true; + + signal = rx->signal; + itp->rx_cnt++; + itp->rx_rssi += signal; + if (itp->rx_rssi_min > rx->signal) + itp->rx_rssi_min = rx->signal; + if (itp->rx_rssi_max < rx->signal) + itp->rx_rssi_max = rx->signal; + + return true; +} + +void cw1200_itp_wake_up_tx(struct cw1200_common *priv) +{ + wake_up(&priv->debug->itp.write_wait); +} + +bool cw1200_itp_tx_running(struct cw1200_common *priv) +{ + if (atomic_read(&priv->debug->itp.awaiting_confirm) || + atomic_read(&priv->debug->itp.test_mode) == + TEST_MODE_TX_TEST) { + atomic_sub(1, &priv->debug->itp.awaiting_confirm); + return true; + } + return false; +} + +static void cw1200_itp_handle(struct cw1200_common *priv, + struct sk_buff *skb) +{ + struct cw1200_itp *itp = &priv->debug->itp; + const struct wiphy *wiphy = priv->hw->wiphy; + int cmd; + int ret; + + itp_printk(KERN_DEBUG "[ITP] <<< %s", skb->data); + if (sscanf(skb->data, "%d", &cmd) != 1) { + cw1200_itp_err(priv, -EINVAL, 1); + return; + } + + switch (cmd) { + case 1: /* RX test */ + if (atomic_read(&itp->test_mode)) { + cw1200_itp_err(priv, -EBUSY, 0); + return; + } + ret = sscanf(skb->data, "%d,%d,%d", + &cmd, &itp->band, &itp->ch); + if (ret != 3) { + cw1200_itp_err(priv, -EINVAL, ret + 1); + return; + } + if (itp->band >= 2) + cw1200_itp_err(priv, -EINVAL, 2); + else if (!wiphy->bands[itp->band]) + cw1200_itp_err(priv, -EINVAL, 2); + else if (itp->ch >= + wiphy->bands[itp->band]->n_channels) + cw1200_itp_err(priv, -EINVAL, 3); + else { + cw1200_itp_rx_stats(priv); + cw1200_itp_rx_start(priv); + } + break; + case 2: /* RX stat */ + cw1200_itp_rx_stats(priv); + break; + case 3: /* RX/TX stop */ + if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) { + cw1200_itp_rx_stats(priv); + cw1200_itp_rx_stop(priv); + } else if (atomic_read(&itp->test_mode) == TEST_MODE_TX_TEST) { + cw1200_itp_tx_stop(priv); + } else + cw1200_itp_err(priv, -EBUSY, 0); + break; + case 4: /* TX start */ + if (atomic_read(&itp->test_mode) != TEST_MODE_NO_TEST) { + cw1200_itp_err(priv, -EBUSY, 0); + return; + } + ret = sscanf(skb->data, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + &cmd, &itp->band, &itp->ch, &itp->rate, + &itp->preamble, &itp->number, &itp->data_mode, + &itp->interval_us, &itp->power, &itp->data_len); + if (ret != 10) { + cw1200_itp_err(priv, -EINVAL, ret + 1); + return; + } + if (itp->band >= 2) + cw1200_itp_err(priv, -EINVAL, 2); + else if (!wiphy->bands[itp->band]) + cw1200_itp_err(priv, -EINVAL, 2); + else if (itp->ch >= + wiphy->bands[itp->band]->n_channels) + cw1200_itp_err(priv, -EINVAL, 3); + else if (itp->rate >= 20) + cw1200_itp_err(priv, -EINVAL, 4); + else if (itp->preamble >= ITP_PREAMBLE_MAX) + cw1200_itp_err(priv, -EINVAL, 5); + else if (itp->data_mode >= ITP_DATA_MAX_MODE) + cw1200_itp_err(priv, -EINVAL, 7); + else if (itp->data_len < ITP_MIN_DATA_SIZE || + itp->data_len > priv->wsm_caps.sizeInpChBuf - + itp->hdr_len) + cw1200_itp_err(priv, -EINVAL, 8); + else { + cw1200_itp_tx_start(priv); + } + break; + case 5: + cw1200_itp_get_version(priv, ITP_CHIP_ID); + break; + case 6: + cw1200_itp_get_version(priv, ITP_FW_VER); + break; + + } +} + +static void cw1200_itp_err(struct cw1200_common *priv, + int err, int arg) +{ + struct cw1200_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + static char buf[255]; + int len; + + len = snprintf(buf, sizeof(buf), "%d,%d\n", + err, arg); + if (len <= 0) + return; + + skb = dev_alloc_skb(len); + if (!skb) + return; + + skb_trim(skb, 0); + skb_put(skb, len); + + memcpy(skb->data, buf, len); + skb_queue_tail(&itp->log_queue, skb); + wake_up(&itp->read_wait); + + len = sprint_symbol(buf, + (unsigned long)__builtin_return_address(0)); + if (len <= 0) + return; + itp_printk(KERN_DEBUG "[ITP] error %d,%d from %s\n", + err, arg, buf); +} diff --git a/drivers/staging/cw1200/itp.h b/drivers/staging/cw1200/itp.h new file mode 100644 index 00000000000..635e7f85ff9 --- /dev/null +++ b/drivers/staging/cw1200/itp.h @@ -0,0 +1,151 @@ +/* + * ITP code for ST-Ericsson CW1200 mac80211 driver + * + * Copyright (c) 2011, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * 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. + */ + +#ifndef CW1200_ITP_H_INCLUDED +#define CW1200_ITP_H_INCLUDED + +struct cw200_common; +struct wsm_tx_confirm; +struct dentry; + +#ifdef CONFIG_CW1200_ITP + +/*extern*/ struct ieee80211_channel; + +#define TEST_MODE_NO_TEST (0) +#define TEST_MODE_RX_TEST (1) +#define TEST_MODE_TX_TEST (2) + +#define itp_printk(...) printk(__VA_ARGS__) +#define ITP_DEFAULT_DA_ADDR {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} +#define ITP_MIN_DATA_SIZE 6 +#define ITP_MAX_DATA_SIZE 1600 +#define ITP_TIME_THRES_US 10000 +#define ITP_US_TO_MS(x) ((x)/1000) +#define ITP_MS_TO_US(x) ((x)*1000) +#if ((ITP_US_TO_MS(ITP_TIME_THRES_US))*HZ/1000) < 1 +#warning not able to achieve non-busywaiting ITP_TIME_THRES_US\ +precision with current HZ value ! +#endif +#define ITP_BUF_SIZE 255 + + +enum cw1200_itp_data_modes { + ITP_DATA_ZEROS, + ITP_DATA_ONES, + ITP_DATA_ZERONES, + ITP_DATA_RANDOM, + ITP_DATA_MAX_MODE, +}; + +enum cw1200_itp_version_type { + ITP_CHIP_ID, + ITP_FW_VER, +}; + +enum cw1200_itp_preamble_type { + ITP_PREAMBLE_LONG, + ITP_PREAMBLE_SHORT, + ITP_PREAMBLE_OFDM, + ITP_PREAMBLE_MIXED, + ITP_PREAMBLE_GREENFIELD, + ITP_PREAMBLE_MAX, +}; + + +struct cw1200_itp { + struct cw1200_common *priv; + atomic_t open_count; + atomic_t awaiting_confirm; + struct sk_buff_head log_queue; + wait_queue_head_t read_wait; + wait_queue_head_t write_wait; + wait_queue_head_t close_wait; + struct ieee80211_channel *saved_channel; + atomic_t stop_tx; + struct delayed_work tx_work; + struct delayed_work tx_finish; + spinlock_t tx_lock; + struct timespec last_sent; + atomic_t test_mode; + int rx_cnt; + long rx_rssi; + int rx_rssi_max; + int rx_rssi_min; + unsigned band; + unsigned ch; + unsigned rate; + unsigned preamble; + unsigned int number; + unsigned data_mode; + int interval_us; + int power; + u8 *data; + int hdr_len; + int data_len; +}; + +int cw1200_itp_init(struct cw1200_common *priv); +void cw1200_itp_release(struct cw1200_common *priv); + +bool cw1200_is_itp(struct cw1200_common *priv); +bool cw1200_itp_rxed(struct cw1200_common *priv, struct sk_buff *skb); +void cw1200_itp_wake_up_tx(struct cw1200_common *priv); +int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data, + size_t *tx_len, int *burst); +bool cw1200_itp_tx_running(struct cw1200_common *priv); + +#else /* CONFIG_CW1200_ITP */ + +static inline int +cw1200_itp_init(struct cw1200_common *priv) +{ + return 0; +} + +static inline void cw1200_itp_release(struct cw1200_common *priv) +{ +} + +static inline bool cw1200_is_itp(struct cw1200_common *priv) +{ + return false; +} + +static inline bool cw1200_itp_rxed(struct cw1200_common *priv, + struct sk_buff *skb) +{ + return false; +} + + +static inline void cw1200_itp_consume_txed(struct cw1200_common *priv) +{ +} + +static inline void cw1200_itp_wake_up_tx(struct cw1200_common *priv) +{ +} + +static inline int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data, + size_t *tx_len, int *burst) +{ + return 0; +} + +static inline bool cw1200_itp_tx_running(struct cw1200_common *priv) +{ + return false; +} + +#endif /* CONFIG_CW1200_ITP */ + +#endif /* CW1200_ITP_H_INCLUDED */ diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index f568c6275c3..7c0fa0b0f2a 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -940,6 +940,9 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, txrx_printk(KERN_DEBUG "[TX] TX confirm: %d, %d.\n", arg->status, arg->ackFailures); + if (unlikely(cw1200_itp_tx_running(priv))) + return; + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { /* STA is stopped. */ return; @@ -1085,7 +1088,8 @@ void cw1200_skb_dtor(struct cw1200_common *priv, txpriv->raw_link_id, txpriv->tid); tx_policy_put(priv, txpriv->rate_id); } - ieee80211_tx_status(priv->hw, skb); + if (likely(!cw1200_is_itp(priv))) + ieee80211_tx_status(priv->hw, skb); } void cw1200_rx_cb(struct cw1200_common *priv, @@ -1262,7 +1266,9 @@ void cw1200_rx_cb(struct cw1200_common *priv, grace_period = 1 * HZ; cw1200_pm_stay_awake(&priv->pm_state, grace_period); - if (unlikely(early_data)) { + if (unlikely(cw1200_itp_rxed(priv, skb))) + consume_skb(skb); + else if (unlikely(early_data)) { spin_lock_bh(&priv->ps_state_lock); /* Double-check status with lock held */ if (entry->status == CW1200_LINK_SOFT) diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 085868b532e..834524666a7 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -21,6 +21,7 @@ #include "wsm.h" #include "bh.h" #include "debug.h" +#include "itp.h" #if defined(CONFIG_CW1200_WSM_DEBUG) #define wsm_printk(...) printk(__VA_ARGS__) @@ -1644,6 +1645,10 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, /* More is used only for broadcasts. */ bool more = false; + count = cw1200_itp_get_tx(priv, data, tx_len, burst); + if (count) + return count; + if (priv->wsm_cmd.ptr) { ++count; spin_lock(&priv->wsm_cmd.lock); -- cgit v1.2.3 From d1ca211045ac275be0351dcdd5fd5677f20af9ab Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 29 Feb 2012 15:15:32 +0100 Subject: cw1200: Fix missing debug parts of ITP implementation * cw1200_itp_init missing in cw1200_debug_init * cw1200_itp_release missing in cw1200_debug_release * missing cw1200_print_fw_version definition ST-Ericsson ID: 359166 ST-Ericsson FOSS-OUT ID: NA Change-Id: I82560ec2d3d6214ca9884a1ac4cb5950b6ac9975 Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/47989 --- drivers/staging/cw1200/debug.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 3a8b79f7556..7a2da551a52 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -500,13 +500,17 @@ int cw1200_debug_init(struct cw1200_common *priv) goto err; #endif + ret = cw1200_itp_init(priv); + if (ret) + goto err; + return 0; err: priv->debug = NULL; debugfs_remove_recursive(d->debugfs_phy); kfree(d); - return -ENOMEM; + return ret; } void cw1200_debug_release(struct cw1200_common *priv) @@ -517,10 +521,12 @@ void cw1200_debug_release(struct cw1200_common *priv) priv->debug = NULL; kfree(d); } - priv->debug = NULL; - - if (d) { - debugfs_remove_recursive(d->debugfs_phy); - kfree(d); } + +int cw1200_print_fw_version(struct cw1200_common *priv, u8 *buf, size_t len) +{ + return snprintf(buf, len, "%s %d.%d", + cw1200_debug_fw_types[priv->wsm_caps.firmwareType], + priv->wsm_caps.firmwareVersion, + priv->wsm_caps.firmwareBuildNumber); } -- cgit v1.2.3 From 1780db0bfde8da1b36c5c2087407ef9ea8744d23 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 29 Feb 2012 15:15:33 +0100 Subject: cw1200: fix queue locking Fixes a very rare BUG_ON in cw1200_tx by reserving extra queue slots for an overflow scenario. It's possible for a parallel TX to happen when in softap since there are two interfaces (wlan1 and mon.wlan1). Softirq may switch from one thread to another asynchronously. In such a case __ieee80211_tx may call driver tx callback just before a queue is locked. ST-Ericsson ID: 413677 ST-Ericsson FOSS-OUT: NA Change-Id: I2f8a9fa556c718797bd832644e46b984f038b958 Signed-off-by: Michal Kazior Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/46444 Reviewed-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/queue.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c index 19db63aa5bf..ba2cc436eaf 100644 --- a/drivers/staging/cw1200/queue.c +++ b/drivers/staging/cw1200/queue.c @@ -319,7 +319,13 @@ int cw1200_queue_put(struct cw1200_queue *queue, ++stats->link_map_cache[txpriv->link_id]; spin_unlock_bh(&stats->lock); - if (queue->num_queued >= queue->capacity) { + /* + * TX may happen in parallel sometimes. + * Leave extra queue slots so we don't overflow. + */ + if (queue->overfull == false && + queue->num_queued >= + (queue->capacity - (num_present_cpus() - 1))) { queue->overfull = true; __cw1200_queue_lock(queue); mod_timer(&queue->gc, jiffies); -- cgit v1.2.3 From b23b9fe752a732812e5ac57f7d11464efcedf629 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 29 Feb 2012 15:15:34 +0100 Subject: cw1200: Makes the FW report RCPI instead of RSSI ST-Ericsson ID: 413663 ST-Ericsson FOSS-OUT ID: NA Change-Id: Ic86d0e6142ac57ed8fe40c3a3a9366235ab8e78b Signed-off-by: Bartosz Markowski Signed-off-by: Johan Lilje Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/48364 Reviewed-by: QATOOLS Reviewed-by: QABUILD --- drivers/staging/cw1200/ap.c | 1 - drivers/staging/cw1200/sta.c | 3 +-- drivers/staging/cw1200/wsm.c | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index 17de92d5da0..b30afe0ce98 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -560,7 +560,6 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, } if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) { struct wsm_rcpi_rssi_threshold threshold = { - .rssiRcpiMode = WSM_RCPI_RSSI_USE_RSSI, .rollingAverageCount = 1, }; diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 2dd90c3b929..2d2392f7a75 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1150,8 +1150,7 @@ int cw1200_setup_mac(struct cw1200_common *priv) * as RSSI if RSSI subscription is enabled. * It's not enough to set WSM_RCPI_RSSI_USE_RSSI. */ struct wsm_rcpi_rssi_threshold threshold = { - .rssiRcpiMode = WSM_RCPI_RSSI_USE_RSSI | - WSM_RCPI_RSSI_THRESHOLD_ENABLE | + .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE | WSM_RCPI_RSSI_DONT_USE_UPPER | WSM_RCPI_RSSI_DONT_USE_LOWER, .rollingAverageCount = 16, diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 834524666a7..f47950f1d24 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -866,7 +866,7 @@ static int wsm_receive_indication(struct cw1200_common *priv, rx.status = WSM_GET32(buf); rx.channelNumber = WSM_GET16(buf); rx.rxedRate = WSM_GET8(buf); - rx.rcpiRssi = WSM_GET8(buf); + rx.rcpiRssi = (WSM_GET8(buf) / 2 - 110); rx.flags = WSM_GET32(buf); /* FW Workaround: Drop probe resp or -- cgit v1.2.3 From 8e1699448eaef8d90f884c1a8b2eda967c76ca62 Mon Sep 17 00:00:00 2001 From: Piotr Nakraszewicz Date: Wed, 29 Feb 2012 15:15:35 +0100 Subject: cw1200: Do not set bssid filter when in p2p mode When acting as p2p client being connected to p2p GO, in order to receive frames from a different p2p device (such as GO negotiation request) turn off bssid filter. WARNING: This patch can only be used with FW WSM371 and its successors. In that FW version even with bssid filter turned off, device will block most of the frames from outside the BSS and not pass them to the host. If using FW WSM373 and its successors this patch will be causing high power consumption in p2p mode. ST-Ericsson ID: 409603 ST-Ericsson FOSS-OUT ID: NA Signed-off-by: Piotr Nakraszewicz Change-Id: Icbcd6c57f8c646f56c41b174c79abb45f7118d15 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/46739 Reviewed-by: QABUILD Tested-by: Piotr NAKRASZEWICZ Reviewed-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/sta.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 2d2392f7a75..edce5b436cf 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -466,6 +466,18 @@ void cw1200_update_filtering(struct cw1200_common *priv) else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) bssid_filtering = false; + /* + * When acting as p2p client being connected to p2p GO, in order to + * receive frames from a different p2p device, turn off bssid filter. + * + * WARNING: FW dependency! + * This can only be used with FW WSM371 and its successors. + * In that FW version even with bssid filter turned off, + * device will block most of the unwanted frames. + */ + if (priv->vif->p2p) + bssid_filtering = false; + ret = wsm_set_rx_filter(priv, &priv->rx_filter); if (!ret) ret = wsm_set_beacon_filter_table(priv, &priv->bf_table); -- cgit v1.2.3 From f82ec4d432168b9d20ecc4c51252f31ceddf7945 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 29 Feb 2012 15:15:36 +0100 Subject: cw1200: Handle WSM_EVENT_RCPI_RSSI correctly After switch to RCPI based signal level reports a RCPI/RSSI EVNTS have been omitted. This patch fix this and also increse the average count of samples which are used to report signal change. A check has been also added to distinguish in receive indication handler if RCPI needs to be converted to RSSI. ST-Ericsson ID: 419276 ST-Ericsson FOSS-OUT ID: NA Change-Id: I9c0a27181a0d2057db4418878b4b0847f9671372 Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/49622 --- drivers/staging/cw1200/ap.c | 2 +- drivers/staging/cw1200/cw1200.h | 1 + drivers/staging/cw1200/sta.c | 25 +++++++++++++++++++++---- drivers/staging/cw1200/wsm.c | 7 ++++++- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c index b30afe0ce98..55096cf0e2c 100644 --- a/drivers/staging/cw1200/ap.c +++ b/drivers/staging/cw1200/ap.c @@ -560,7 +560,7 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev, } if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) { struct wsm_rcpi_rssi_threshold threshold = { - .rollingAverageCount = 1, + .rollingAverageCount = 8, }; #if 0 diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 3ae1b915566..46b8087eaa6 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -141,6 +141,7 @@ struct cw1200_common { unsigned cqm_rssi_hyst; unsigned cqm_tx_failure_thold; unsigned cqm_tx_failure_count; + bool cqm_use_rssi; int cqm_link_loss_count; int cqm_beacon_loss_count; int channel_switch_in_progress; diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index edce5b436cf..90988e69ce2 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -997,11 +997,19 @@ void cw1200_event_handler(struct work_struct *work) break; case WSM_EVENT_RCPI_RSSI: { - int rssi = (int)(s8)(event->evt.eventData & 0xFF); - int cqm_evt = (rssi <= priv->cqm_rssi_thold) ? + /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 + * RSSI = RCPI / 2 - 110 */ + int rcpiRssi = (int)(event->evt.eventData & 0xFF); + int cqm_evt; + if (priv->cqm_use_rssi) + rcpiRssi = (s8)rcpiRssi; + else + rcpiRssi = rcpiRssi / 2 - 110; + + cqm_evt = (rcpiRssi <= priv->cqm_rssi_thold) ? NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW : NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; - sta_printk(KERN_DEBUG "[CQM] RSSI event: %d", rssi); + sta_printk(KERN_DEBUG "[CQM] RSSI event: %d", rcpiRssi); ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, GFP_KERNEL); break; @@ -1158,16 +1166,25 @@ static int cw1200_parse_SDD_file(struct cw1200_common *priv) int cw1200_setup_mac(struct cw1200_common *priv) { + int ret = 0; + /* NOTE: There is a bug in FW: it reports signal * as RSSI if RSSI subscription is enabled. * It's not enough to set WSM_RCPI_RSSI_USE_RSSI. */ + /* NOTE2: RSSI based reports have been switched to RCPI, since + * FW has a bug and RSSI reported values are not stable, + * what can leads to signal level oscilations in user-end applications */ struct wsm_rcpi_rssi_threshold threshold = { .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE | WSM_RCPI_RSSI_DONT_USE_UPPER | WSM_RCPI_RSSI_DONT_USE_LOWER, .rollingAverageCount = 16, }; - int ret = 0; + + /* Remember the decission here to make sure, we will handle + * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS */ + if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI) + priv->cqm_use_rssi = true; if (!priv->sdd) { const char *sdd_path = NULL; diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index f47950f1d24..245a6400358 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -866,7 +866,7 @@ static int wsm_receive_indication(struct cw1200_common *priv, rx.status = WSM_GET32(buf); rx.channelNumber = WSM_GET16(buf); rx.rxedRate = WSM_GET8(buf); - rx.rcpiRssi = (WSM_GET8(buf) / 2 - 110); + rx.rcpiRssi = WSM_GET8(buf); rx.flags = WSM_GET32(buf); /* FW Workaround: Drop probe resp or @@ -878,6 +878,11 @@ static int wsm_receive_indication(struct cw1200_common *priv, ieee80211_is_beacon(hdr->frame_control))) return 0; + /* If no RSSI subscription has been made, + * convert RCPI to RSSI here */ + if (!priv->cqm_use_rssi) + rx.rcpiRssi = rx.rcpiRssi / 2 - 110; + rx.link_id = link_id; fctl = *(__le16 *)buf->data; hdr_len = buf->data - buf->begin; -- cgit v1.2.3 From 8e8a74dbaf4f3e58046aaf7a3a817e88590335de Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:15:37 +0100 Subject: cw1200: Start advanced filtering on system suspend Enables advanced filtering when system is entering suspend to avoid device-driven wakeups on frames the system can ignore in suspend. ST-Ericsson ID: 418353 ST-Ericsson FOSS-OUT ID: NA Change-Id: I7d62c28e769448388b32bacc2d2e95dcd795c925 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/49996 Reviewed-by: Filip MATUSIAK Tested-by: Filip MATUSIAK Reviewed-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/pm.c | 79 +++++++++++++++++++++++++++++++++++++++++++- drivers/staging/cw1200/wsm.h | 54 ++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c index fc9c2354758..049703155db 100644 --- a/drivers/staging/cw1200/pm.c +++ b/drivers/staging/cw1200/pm.c @@ -10,6 +10,7 @@ */ #include +#include #include "cw1200.h" #include "pm.h" #include "sta.h" @@ -18,6 +19,67 @@ #define CW1200_BEACON_SKIPPING_MULTIPLIER 3 +struct cw1200_udp_port_filter { + struct wsm_udp_port_filter_hdr hdr; + struct wsm_udp_port_filter dhcp; + struct wsm_udp_port_filter upnp; +} __packed; + +struct cw1200_ether_type_filter { + struct wsm_ether_type_filter_hdr hdr; + struct wsm_ether_type_filter ip; + struct wsm_ether_type_filter pae; + struct wsm_ether_type_filter wapi; +} __packed; + +static struct cw1200_udp_port_filter cw1200_udp_port_filter_on = { + .hdr.nrFilters = 2, + .dhcp = { + .filterAction = WSM_FILTER_ACTION_FILTER_OUT, + .portType = WSM_FILTER_PORT_TYPE_DST, + .udpPort = __cpu_to_le16(67), + }, + .upnp = { + .filterAction = WSM_FILTER_ACTION_FILTER_OUT, + .portType = WSM_FILTER_PORT_TYPE_DST, + .udpPort = __cpu_to_le16(1900), + }, + /* Please add other known ports to be filtered out here and + * update nrFilters field in the header. + * Up to 4 filters are allowed. */ +}; + +static struct wsm_udp_port_filter_hdr cw1200_udp_port_filter_off = { + .nrFilters = 0, +}; + +#ifndef ETH_P_WAPI +#define ETH_P_WAPI 0x88B4 +#endif + +static struct cw1200_ether_type_filter cw1200_ether_type_filter_on = { + .hdr.nrFilters = 3, + .ip = { + .filterAction = WSM_FILTER_ACTION_FILTER_IN, + .etherType = __cpu_to_le16(ETH_P_IP), + }, + .pae = { + .filterAction = WSM_FILTER_ACTION_FILTER_IN, + .etherType = __cpu_to_le16(ETH_P_PAE), + }, + .wapi = { + .filterAction = WSM_FILTER_ACTION_FILTER_IN, + .etherType = __cpu_to_le16(ETH_P_WAPI), + }, + /* Please add other known ether types to be filtered out here and + * update nrFilters field in the header. + * Up to 4 filters are allowed. */ +}; + +static struct wsm_ether_type_filter_hdr cw1200_ether_type_filter_off = { + .nrFilters = 0, +}; + static int cw1200_suspend_late(struct device *dev); static void cw1200_pm_release(struct device *dev); static int cw1200_pm_probe(struct platform_device *pdev); @@ -236,7 +298,13 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) /* Lock TX. */ wsm_lock_tx_async(priv); if (priv->hw_bufs_used) - goto revert3; + goto revert2; + + /* Set UDP filter */ + wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_on.hdr); + + /* Set ethernet frame type filter */ + wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_on.hdr); /* Allocate state */ state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL); @@ -313,6 +381,9 @@ revert4: state->link_id_gc); kfree(state); revert3: + wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off); + wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off); +revert2: wsm_unlock_tx(priv); up(&priv->scan.lock); revert1: @@ -362,6 +433,12 @@ int cw1200_wow_resume(struct ieee80211_hw *hw) jiffies + CW1200_BLOCK_ACK_INTERVAL); spin_unlock_bh(&priv->ba_lock); + /* Remove UDP port filter */ + wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off); + + /* Remove ethernet frame type filter */ + wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off); + /* Unlock datapath */ wsm_unlock_tx(priv); diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 004a55f1240..4f7360d0bf3 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -572,6 +572,17 @@ struct cw1200_common; /* This is applicable only to Transmit */ #define WSM_REQUEUE (11) +/* Advanced filtering options */ +#define WSM_MAX_FILTER_ELEMENTS (4) + +#define WSM_FILTER_ACTION_IGNORE (0) +#define WSM_FILTER_ACTION_FILTER_IN (1) +#define WSM_FILTER_ACTION_FILTER_OUT (2) + +#define WSM_FILTER_PORT_TYPE_DST (0) +#define WSM_FILTER_PORT_TYPE_SRC (1) + + struct wsm_hdr { __le16 len; @@ -1516,6 +1527,49 @@ static inline int wsm_set_tx_rate_retry_policy(struct cw1200_common *priv, size); } +/* 4.32 SetEtherTypeDataFrameFilter */ +struct wsm_ether_type_filter_hdr { + u8 nrFilters; /* Up to WSM_MAX_FILTER_ELEMENTS */ + u8 reserved[3]; +} __packed; + +struct wsm_ether_type_filter { + u8 filterAction; /* WSM_FILTER_ACTION_XXX */ + u8 reserved; + __le16 etherType; /* Type of ethernet frame */ +} __packed; + +static inline int wsm_set_ether_type_filter(struct cw1200_common *priv, + struct wsm_ether_type_filter_hdr *arg) +{ + size_t size = sizeof(struct wsm_ether_type_filter_hdr) + + arg->nrFilters * sizeof(struct wsm_ether_type_filter); + return wsm_write_mib(priv, WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER, + arg, size); +} + + +/* 4.33 SetUDPPortDataFrameFilter */ +struct wsm_udp_port_filter_hdr { + u8 nrFilters; /* Up to WSM_MAX_FILTER_ELEMENTS */ + u8 reserved[3]; +} __packed; + +struct wsm_udp_port_filter { + u8 filterAction; /* WSM_FILTER_ACTION_XXX */ + u8 portType; /* WSM_FILTER_PORT_TYPE_XXX */ + __le16 udpPort; /* Port number */ +} __packed; + +static inline int wsm_set_udp_port_filter(struct cw1200_common *priv, + struct wsm_udp_port_filter_hdr *arg) +{ + size_t size = sizeof(struct wsm_udp_port_filter_hdr) + + arg->nrFilters * sizeof(struct wsm_udp_port_filter); + return wsm_write_mib(priv, WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER, + arg, size); +} + /* Undocumented MIBs: */ /* 4.35 P2PDeviceInfo */ #define D11_MAX_SSID_LEN (32) -- cgit v1.2.3 From fa90221559af088c0e95b698e3ac2cb971f6ecdf Mon Sep 17 00:00:00 2001 From: Marek Kwaczynski Date: Wed, 29 Feb 2012 15:15:38 +0100 Subject: cw1200: Removes unnecessary WARN_ON. The FW returns error when the host trigers BG SCAN between TX DISASSOC frame and TX DEAUTH frame. ST-Ericsson ID: 407629 ST-Ericsson FOSS-OUT ID: NA Change-Id: If3eae1b775d39e07f56ab236132050e57186e574 Signed-off-by: Marek Kwaczynski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/47913 Reviewed-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/scan.c b/drivers/staging/cw1200/scan.c index da5a966ed8a..b12af9ded62 100644 --- a/drivers/staging/cw1200/scan.c +++ b/drivers/staging/cw1200/scan.c @@ -237,7 +237,7 @@ void cw1200_scan_work(struct work_struct *work) } priv->scan.status = cw1200_scan_start(priv, &scan); kfree(scan.ch); - if (WARN_ON(priv->scan.status)) + if (priv->scan.status) goto fail; priv->scan.curr = it; } -- cgit v1.2.3 From 97f810839b26de6787fe45dfb9c4ae4f9fc62eb9 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:15:39 +0100 Subject: cw1200: Fixing checkpatch warnings. --- drivers/staging/cw1200/cw1200_sdio.c | 2 +- drivers/staging/cw1200/pm.c | 2 +- drivers/staging/cw1200/sta.c | 7 +++++-- drivers/staging/cw1200/wsm.c | 2 +- drivers/staging/cw1200/wsm.h | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/staging/cw1200/cw1200_sdio.c b/drivers/staging/cw1200/cw1200_sdio.c index c5de6dbbc2a..4b9d622689f 100644 --- a/drivers/staging/cw1200/cw1200_sdio.c +++ b/drivers/staging/cw1200/cw1200_sdio.c @@ -263,7 +263,7 @@ static int cw1200_sdio_on(const struct cw1200_platform_data *pdata) /* A valid reset shall be obtained by maintaining WRESETN * active (low) for at least two cycles of LP_CLK after VDDIO * is stable within it operating range. */ - msleep(1); + usleep_range(1000, 20000); gpio_set_value(reset->start, 1); /* The host should wait 32 ms after the WRESETN release * for the on-chip LDO to stabilize */ diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c index 049703155db..f542e7d51f6 100644 --- a/drivers/staging/cw1200/pm.c +++ b/drivers/staging/cw1200/pm.c @@ -118,7 +118,7 @@ static int cw1200_pm_init_common(struct cw1200_pm_state *pm, pm->pm_dev = platform_device_alloc("cw1200_power", 0); if (!pm->pm_dev) { platform_driver_unregister(&cw1200_power_driver); - return ENOMEM; + return -ENOMEM; } pm->pm_dev->dev.platform_data = priv; diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 90988e69ce2..54b681aba0e 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1120,13 +1120,13 @@ static int cw1200_parse_SDD_file(struct cw1200_common *priv) } *pElement; int parsedLength = 0; #define SDD_PTA_CFG_ELT_ID 0xEB - #define FIELD_OFFSET(type, field) ((u8 *)&((type*)0)->field - (u8 *)0) + #define FIELD_OFFSET(type, field) ((u8 *)&((type *)0)->field - (u8 *)0) priv->is_BT_Present = false; pElement = (struct cw1200_sdd *)sdd_data; - pElement = (struct cw1200_sdd *)((u8*)pElement + + pElement = (struct cw1200_sdd *)((u8 *)pElement + FIELD_OFFSET(struct cw1200_sdd, data) + pElement->length); parsedLength += (FIELD_OFFSET(struct cw1200_sdd, data) + @@ -1161,6 +1161,9 @@ static int cw1200_parse_SDD_file(struct cw1200_common *priv) priv->conf_listen_interval = 0; } return 0; + + #undef SDD_PTA_CFG_ELT_ID + #undef FIELD_OFFSET } diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 245a6400358..21df2bf0bfe 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1158,7 +1158,7 @@ int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len) char fname[48]; size_t i; - static const char *reason_str[] = { + static const char * const reason_str[] = { "undefined instruction", "prefetch abort", "data abort", diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 4f7360d0bf3..9c66c1ab654 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -1771,7 +1771,7 @@ void wsm_unlock_tx(struct cw1200_common *priv); /* ******************************************************************** */ /* WSM / BH API */ -int wsm_handle_exception(struct cw1200_common *priv, u8 * data, size_t len); +int wsm_handle_exception(struct cw1200_common *priv, u8 *data, size_t len); int wsm_handle_rx(struct cw1200_common *priv, int id, struct wsm_hdr *wsm, struct sk_buff **skb_p); -- cgit v1.2.3 From 30044654a02bc9fcf123b015c17a192d95f22190 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 29 Feb 2012 15:15:40 +0100 Subject: cw1200: Dynamic WSM messages dumps Adds dynamic WSM dumps support. Creates new debugFS entry 'wsm_dumps', which can be used to dynamicly enable and disable WSM messages dumps. ST-Ericsson ID: 419537 ST-Ericsson FOSS-OUT ID: NA Change-Id: Id87f2e47cbc3023551dbae9d79981a6ce1cf6d29 Signed-off-by: Bartosz Markowski Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/50357 Reviewed-by: QATOOLS Reviewed-by: QABUILD --- drivers/staging/cw1200/bh.c | 12 ++++++++---- drivers/staging/cw1200/cw1200.h | 3 +++ drivers/staging/cw1200/debug.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index 34bf7d733b8..5c0fc628a89 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -397,8 +397,10 @@ rx: break; #if defined(CONFIG_CW1200_WSM_DUMPS) - print_hex_dump_bytes("<-- ", DUMP_PREFIX_NONE, - data, wsm_len); + if (unlikely(priv->wsm_enable_wsm_dumps)) + print_hex_dump_bytes("<-- ", + DUMP_PREFIX_NONE, + data, wsm_len); #endif /* CONFIG_CW1200_WSM_DUMPS */ wsm_id = __le32_to_cpu(wsm->id) & 0xFFF; @@ -520,8 +522,10 @@ tx: } #if defined(CONFIG_CW1200_WSM_DUMPS) - print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE, - data, __le32_to_cpu(wsm->len)); + if (unlikely(priv->wsm_enable_wsm_dumps)) + print_hex_dump_bytes("--> ", + DUMP_PREFIX_NONE, + data, __le32_to_cpu(wsm->len)); #endif /* CONFIG_CW1200_WSM_DUMPS */ wsm_txed(priv, data); diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 46b8087eaa6..81b453339ae 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -206,6 +206,9 @@ struct cw1200_common { struct wsm_cbc wsm_cbc; atomic_t tx_lock; + /* WSM debug */ + int wsm_enable_wsm_dumps; + /* Scan status */ struct cw1200_scan scan; diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 7a2da551a52..46db83316d5 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -468,6 +468,31 @@ static const struct file_operations fops_hang = { }; #endif +static ssize_t cw1200_wsm_dumps(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct cw1200_common *priv = file->private_data; + char buf[1]; + + if (!count) + return -EINVAL; + if (copy_from_user(buf, user_buf, 1)) + return -EFAULT; + + if (buf[0] == '1') + priv->wsm_enable_wsm_dumps = 1; + else + priv->wsm_enable_wsm_dumps = 0; + + return count; +} + +static const struct file_operations fops_wsm_dumps = { + .open = cw1200_generic_open, + .write = cw1200_wsm_dumps, + .llseek = default_llseek, +}; + int cw1200_debug_init(struct cw1200_common *priv) { int ret = -ENOMEM; @@ -500,6 +525,10 @@ int cw1200_debug_init(struct cw1200_common *priv) goto err; #endif + if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy, + priv, &fops_wsm_dumps)) + goto err; + ret = cw1200_itp_init(priv); if (ret) goto err; -- cgit v1.2.3 From b85ef3fd664e8c73e9979e56af72ae13b44eadd6 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Wed, 29 Feb 2012 15:15:41 +0100 Subject: cw1200: WSM debug improvements New config options added: * CW1200_WSM_DUMPS_SHORT * CW1200_DUMP_ON_ERROR ST-Ericsson ID: 419537 ST-Ericsson FOSS-OUT ID: NA Signed-off-by: Janusz Dziedzic Signed-off-by: Bartosz Markowski Change-Id: Ib47989fb8b21d418507ef9109c48f440e10b4a18 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/50378 Reviewed-by: QATOOLS --- drivers/staging/cw1200/Kconfig | 6 +++++ drivers/staging/cw1200/bh.c | 22 ++++++++++++++--- drivers/staging/cw1200/cw1200.h | 1 + drivers/staging/cw1200/debug.c | 52 ++++++++++++++++++++++++++++++++++++++++- drivers/staging/cw1200/main.c | 3 +++ 5 files changed, 80 insertions(+), 4 deletions(-) diff --git a/drivers/staging/cw1200/Kconfig b/drivers/staging/cw1200/Kconfig index 044f7e6585a..4be840c1576 100644 --- a/drivers/staging/cw1200/Kconfig +++ b/drivers/staging/cw1200/Kconfig @@ -78,6 +78,9 @@ config CW1200_WSM_DEBUG config CW1200_WSM_DUMPS bool "Verbose WSM API logging (DEVELOPMENT)" +config CW1200_WSM_DUMPS_SHORT + bool "Dump only first x bytes (default 20) (DEVELOPMENT)" + config CW1200_TXRX_DEBUG bool "Enable TX/RX debug messages (DEVELOPMENT)" @@ -87,6 +90,9 @@ config CW1200_TX_POLICY_DEBUG config CW1200_STA_DEBUG bool "Enable STA/AP debug (DEVELOPMENT)" +config CW1200_DUMP_ON_ERROR + bool "Dump kernel in case of critical error (DEVELOPMENT)" + endmenu config CW1200_ITP diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index 5c0fc628a89..1acc972b6d4 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -265,6 +265,7 @@ static int cw1200_bh(void *arg) int tx_burst; int rx_burst = 0; long status; + size_t wsm_dump_max = -1; u32 dummy; for (;;) { @@ -283,6 +284,9 @@ static int cw1200_bh(void *arg) (atomic_read(&priv->bh_tx) == 0))) cw1200_reg_read(priv, ST90TDS_CONFIG_REG_ID, &dummy, sizeof(dummy)); +#if defined(CONFIG_CW1200_WSM_DUMPS_SHORT) + wsm_dump_max = priv->wsm_dump_max_size; +#endif /* CONFIG_CW1200_WSM_DUMPS_SHORT */ status = wait_event_interruptible_timeout(priv->bh_wq, ({ rx = atomic_xchg(&priv->bh_rx, 0); @@ -298,6 +302,9 @@ static int cw1200_bh(void *arg) if (!status && priv->hw_bufs_used) { wiphy_warn(priv->hw->wiphy, "Missed interrupt?\n"); rx = 1; +#if defined(CONFIG_CW1200_DUMP_ON_ERROR) + BUG_ON(1); +#endif /* CONFIG_CW1200_DUMP_ON_ERROR */ } else if (!status) { bh_printk(KERN_DEBUG "[BH] Device wakedown.\n"); WARN_ON(cw1200_reg_write_16(priv, @@ -400,7 +407,7 @@ rx: if (unlikely(priv->wsm_enable_wsm_dumps)) print_hex_dump_bytes("<-- ", DUMP_PREFIX_NONE, - data, wsm_len); + data, min(wsm_len, wsm_dump_max)); #endif /* CONFIG_CW1200_WSM_DUMPS */ wsm_id = __le32_to_cpu(wsm->id) & 0xFFF; @@ -414,8 +421,12 @@ rx: wsm_len - sizeof(*wsm)); break; } else if (unlikely(!rx_resync)) { - if (WARN_ON(wsm_seq != priv->wsm_rx_seq)) + if (WARN_ON(wsm_seq != priv->wsm_rx_seq)) { +#if defined(CONFIG_CW1200_DUMP_ON_ERROR) + BUG_ON(1); +#endif /* CONFIG_CW1200_DUMP_ON_ERROR */ break; + } } priv->wsm_rx_seq = (wsm_seq + 1) & 7; rx_resync = 0; @@ -525,7 +536,9 @@ tx: if (unlikely(priv->wsm_enable_wsm_dumps)) print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE, - data, __le32_to_cpu(wsm->len)); + data, + min(__le32_to_cpu(wsm->len), + wsm_dump_max)); #endif /* CONFIG_CW1200_WSM_DUMPS */ wsm_txed(priv, data); @@ -552,6 +565,9 @@ tx: if (!term) { cw1200_dbg(CW1200_DBG_ERROR, "[BH] Fatal error, exitting.\n"); +#if defined(CONFIG_CW1200_DUMP_ON_ERROR) + BUG_ON(1); +#endif /* CONFIG_CW1200_DUMP_ON_ERROR */ priv->bh_error = 1; #if defined(CONFIG_CW1200_USE_STE_EXTENSIONS) ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL); diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h index 81b453339ae..5869c917fe6 100644 --- a/drivers/staging/cw1200/cw1200.h +++ b/drivers/staging/cw1200/cw1200.h @@ -208,6 +208,7 @@ struct cw1200_common { /* WSM debug */ int wsm_enable_wsm_dumps; + u32 wsm_dump_max_size; /* Scan status */ struct cw1200_scan scan; diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 46db83316d5..3da603ac87d 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -493,6 +493,50 @@ static const struct file_operations fops_wsm_dumps = { .llseek = default_llseek, }; +#if defined(CONFIG_CW1200_WSM_DUMPS_SHORT) +static ssize_t cw1200_short_dump_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct cw1200_common *priv = file->private_data; + char buf[20]; + size_t size = 0; + + sprintf(buf, "Size: %u\n", priv->wsm_dump_max_size); + size = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, + buf, size); +} + +static ssize_t cw1200_short_dump_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct cw1200_common *priv = file->private_data; + char buf[20]; + unsigned long dump_size = 0; + + if (!count || count > 20) + return -EINVAL; + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + if (kstrtoul(buf, 10, &dump_size)) + return -EINVAL; + printk(KERN_ERR "%s get %lu\n", __func__, dump_size); + + priv->wsm_dump_max_size = dump_size; + + return count; +} + +static const struct file_operations fops_short_dump = { + .open = cw1200_generic_open, + .write = cw1200_short_dump_write, + .read = cw1200_short_dump_read, + .llseek = default_llseek, +}; +#endif /* CONFIG_CW1200_WSM_DUMPS_SHORT */ + int cw1200_debug_init(struct cw1200_common *priv) { int ret = -ENOMEM; @@ -529,6 +573,12 @@ int cw1200_debug_init(struct cw1200_common *priv) priv, &fops_wsm_dumps)) goto err; +#if defined(CONFIG_CW1200_WSM_DUMPS_SHORT) + if (!debugfs_create_file("wsm_dump_size", S_IRUSR | S_IWUSR, + d->debugfs_phy, priv, &fops_short_dump)) + goto err; +#endif /* CONFIG_CW1200_WSM_DUMPS_SHORT */ + ret = cw1200_itp_init(priv); if (ret) goto err; @@ -550,7 +600,7 @@ void cw1200_debug_release(struct cw1200_common *priv) priv->debug = NULL; kfree(d); } - } +} int cw1200_print_fw_version(struct cw1200_common *priv, u8 *buf, size_t len) { diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 579aee899c5..92f127e2f33 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -389,6 +389,9 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) wsm_buf_init(&priv->wsm_cmd_buf); spin_lock_init(&priv->wsm_cmd.lock); tx_policy_init(priv); +#if defined(CONFIG_CW1200_WSM_DUMPS_SHORT) + priv->wsm_dump_max_size = 20; +#endif /* CONFIG_CW1200_WSM_DUMPS_SHORT */ return hw; } -- cgit v1.2.3 From ddf179a33044c1b6b9dabce1fc166335f7a53a75 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:15:42 +0100 Subject: cw1200: Fix for false negatives in suspend decision. There was a race between PM and BH code which sometimes leaded to false negatives in system suspend decision. This race is fixed by this patch. ST-Ericsoon ID: 419543 Change-ID: I607a37c1046ea9737b625da65e04be0f42901aaa Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/50440 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/pm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c index f542e7d51f6..d95e5d3dc9e 100644 --- a/drivers/staging/cw1200/pm.c +++ b/drivers/staging/cw1200/pm.c @@ -297,7 +297,11 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) /* Lock TX. */ wsm_lock_tx_async(priv); - if (priv->hw_bufs_used) + + /* Wait to avoid possible race with bh code. + * But do not wait too long... */ + if (wait_event_timeout(priv->bh_evt_wq, + !priv->hw_bufs_used, HZ / 10) <= 0) goto revert2; /* Set UDP filter */ -- cgit v1.2.3 From a6031489766def3da20dfe173310207dca95b06c Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:15:43 +0100 Subject: cw1200: Do not allow device to suspend when it is not fully awake. Patch fixes a race between device wakeup and device_suspend. The race might confuse state machine in firmware. ST-Ericsson ID: 419543 Change-Id: I07fa22879b3735fe4d5a1f70f5660a693a1b0211 Signed-off-by: Dmitry Tarnyagin --- drivers/staging/cw1200/bh.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index 1acc972b6d4..fe8ecc1287f 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -292,7 +292,8 @@ static int cw1200_bh(void *arg) rx = atomic_xchg(&priv->bh_rx, 0); tx = atomic_xchg(&priv->bh_tx, 0); term = atomic_xchg(&priv->bh_term, 0); - suspend = atomic_read(&priv->bh_suspend); + suspend = pending_tx ? + 0 : atomic_read(&priv->bh_suspend); (rx || tx || term || suspend); }), status); -- cgit v1.2.3 From cef37561c3ff9419a4467e1f2b6f8580d0ce2808 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:15:44 +0100 Subject: cw1200: Avoid sleeping on TX lock for more than 1.5 sec. The patch protects against possible timeout in system suspend code. It implements timestamps for TX frames, and do not wait more than 1.5 sec in total since the oldest frame is sent to firmware. Firmware has to reply within this interval, otherwise driver commits suicide by terminating device communication thread. ST-Ericsson ID: 418642 Change-Id: Ifb5abf1147cb48665ffebf56254ce7a21c9a7d10 Signed-off-by: Dmitry Tarnyagin --- drivers/staging/cw1200/bh.c | 4 +-- drivers/staging/cw1200/queue.c | 28 +++++++++++++++--- drivers/staging/cw1200/queue.h | 3 ++ drivers/staging/cw1200/wsm.c | 67 ++++++++++++++++++++++++++++++------------ drivers/staging/cw1200/wsm.h | 2 +- 5 files changed, 79 insertions(+), 25 deletions(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index fe8ecc1287f..22b43a5f1cd 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -294,10 +294,10 @@ static int cw1200_bh(void *arg) term = atomic_xchg(&priv->bh_term, 0); suspend = pending_tx ? 0 : atomic_read(&priv->bh_suspend); - (rx || tx || term || suspend); + (rx || tx || term || suspend || priv->bh_error); }), status); - if (status < 0 || term) + if (status < 0 || term || priv->bh_error) break; if (!status && priv->hw_bufs_used) { diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c index ba2cc436eaf..014e6b2a8a4 100644 --- a/drivers/staging/cw1200/queue.c +++ b/drivers/staging/cw1200/queue.c @@ -20,7 +20,8 @@ struct list_head head; struct sk_buff *skb; u32 packetID; - unsigned long timestamp; + unsigned long queue_timestamp; + unsigned long xmit_timestamp; struct cw1200_txpriv txpriv; u8 generation; }; @@ -102,7 +103,7 @@ static void __cw1200_queue_gc(struct cw1200_queue *queue, while (!list_empty(&queue->queue)) { item = list_first_entry( &queue->queue, struct cw1200_queue_item, head); - if (jiffies - item->timestamp < queue->ttl) + if (jiffies - item->queue_timestamp < queue->ttl) break; --queue->num_queued; --queue->link_map_cache[item->txpriv.link_id]; @@ -126,7 +127,7 @@ static void __cw1200_queue_gc(struct cw1200_queue *queue, if (unlock) __cw1200_queue_unlock(queue); } else { - unsigned long tmo = item->timestamp + queue->ttl; + unsigned long tmo = item->queue_timestamp + queue->ttl; mod_timer(&queue->gc, tmo); cw1200_pm_stay_awake(&stats->priv->pm_state, tmo - jiffies); @@ -309,7 +310,7 @@ int cw1200_queue_put(struct cw1200_queue *queue, item->packetID = cw1200_queue_make_packet_id( queue->generation, queue->queue_id, item->generation, item - queue->pool); - item->timestamp = jiffies; + item->queue_timestamp = jiffies; ++queue->num_queued; ++queue->link_map_cache[txpriv->link_id]; @@ -364,6 +365,7 @@ int cw1200_queue_get(struct cw1200_queue *queue, list_move_tail(&item->head, &queue->pending); ++queue->num_pending; --queue->link_map_cache[item->txpriv.link_id]; + item->xmit_timestamp = jiffies; spin_lock_bh(&stats->lock); --stats->num_queued; @@ -539,6 +541,24 @@ void cw1200_queue_unlock(struct cw1200_queue *queue) spin_unlock_bh(&queue->lock); } +bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue, + unsigned long *timestamp) +{ + struct cw1200_queue_item *item; + bool ret; + + spin_lock_bh(&queue->lock); + ret = !list_empty(&queue->pending); + if (ret) { + list_for_each_entry(item, &queue->pending, head) { + if (time_before(item->xmit_timestamp, *timestamp)) + *timestamp = item->xmit_timestamp; + } + } + spin_unlock_bh(&queue->lock); + return ret; +} + bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, u32 link_id_map) { diff --git a/drivers/staging/cw1200/queue.h b/drivers/staging/cw1200/queue.h index 2403b2519ba..aa9a7f9444c 100644 --- a/drivers/staging/cw1200/queue.h +++ b/drivers/staging/cw1200/queue.h @@ -96,6 +96,9 @@ int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID, const struct cw1200_txpriv **txpriv); void cw1200_queue_lock(struct cw1200_queue *queue); void cw1200_queue_unlock(struct cw1200_queue *queue); +bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue, + unsigned long *timestamp); + bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats, u32 link_id_map); diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 21df2bf0bfe..d1ea95c28e4 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -33,7 +33,7 @@ #define WSM_CMD_JOIN_TIMEOUT (7 * HZ) /* Join timeout is 5 sec. in FW */ #define WSM_CMD_START_TIMEOUT (7 * HZ) #define WSM_CMD_RESET_TIMEOUT (3 * HZ) /* 2 sec. timeout was observed. */ -#define WSM_CMD_LAST_CHANCE_TIMEOUT (10 * HZ) +#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 3 / 2) #define WSM_SKIP(buf, size) \ do { \ @@ -1082,6 +1082,10 @@ int wsm_cmd_send(struct cw1200_common *priv, priv->wsm_cmd_wq, priv->wsm_cmd.done, WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); } + + /* Kill BH thread to report the error to the top layer. */ + priv->bh_error = 1; + wake_up(&priv->bh_wq); ret = -ETIMEDOUT; } else { spin_lock(&priv->wsm_cmd.lock); @@ -1100,15 +1104,8 @@ void wsm_lock_tx(struct cw1200_common *priv) { wsm_cmd_lock(priv); if (atomic_add_return(1, &priv->tx_lock) == 1) { - if (priv->bh_error) { - wsm_printk(KERN_ERR "fatal error occured, " - "could not take lock\n"); - } else { - WARN_ON(wait_event_timeout(priv->bh_evt_wq, - !priv->hw_bufs_used, - WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); + if (wsm_flush_tx(priv)) wsm_printk(KERN_DEBUG "[WSM] TX is locked.\n"); - } } wsm_cmd_unlock(priv); } @@ -1116,18 +1113,52 @@ void wsm_lock_tx(struct cw1200_common *priv) void wsm_lock_tx_async(struct cw1200_common *priv) { if (atomic_add_return(1, &priv->tx_lock) == 1) - wsm_printk(KERN_DEBUG "[WSM] TX is locked.\n"); + wsm_printk(KERN_DEBUG "[WSM] TX is locked (async).\n"); } -void wsm_flush_tx(struct cw1200_common *priv) +bool wsm_flush_tx(struct cw1200_common *priv) { - if (priv->bh_error) - wsm_printk(KERN_ERR "fatal error occured, will not flush\n"); - else { - BUG_ON(!atomic_read(&priv->tx_lock)); - WARN_ON(wait_event_timeout(priv->bh_evt_wq, - !priv->hw_bufs_used, - WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); + unsigned long timestamp = jiffies; + bool pending = false; + long timeout; + int i; + + /* Flush must be called with TX lock held. */ + BUG_ON(!atomic_read(&priv->tx_lock)); + + /* First check if we really need to do something. + * It is safe to use unprotected access, as hw_bufs_used + * can only decrements. */ + if (!priv->hw_bufs_used) + return true; + + if (priv->bh_error) { + /* In case of failure do not wait for magic. */ + wsm_printk(KERN_ERR "[WSM] Fatal error occured, " + "will not flush TX.\n"); + return false; + } else { + /* Get a timestamp of "oldest" frame */ + for (i = 0; i < 4; ++i) + pending |= cw1200_queue_get_xmit_timestamp( + &priv->tx_queue[i], + ×tamp); + /* It is allowed to lock TX with only a command in the pipe. */ + if (!pending) + return true; + + timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies; + if (timeout < 0 || wait_event_timeout(priv->bh_evt_wq, + !priv->hw_bufs_used, + timeout) <= 0) { + /* Hmmm... Not good. Frame had stuck in firmware. */ + priv->bh_error = 1; + wake_up(&priv->bh_wq); + return false; + } + + /* Ok, everything is flushed. */ + return true; } } diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index 9c66c1ab654..fb15ef53294 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -1765,7 +1765,7 @@ static inline int wsm_set_override_internal_txrate(struct cw1200_common *priv, void wsm_lock_tx(struct cw1200_common *priv); void wsm_lock_tx_async(struct cw1200_common *priv); -void wsm_flush_tx(struct cw1200_common *priv); +bool wsm_flush_tx(struct cw1200_common *priv); void wsm_unlock_tx(struct cw1200_common *priv); /* ******************************************************************** */ -- cgit v1.2.3 From 5b83acdbc846ce58aca54ff4b490eb0be076c5df Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:15:45 +0100 Subject: cw1200: Balance calls to cw1200_pm_(de)init. In case of firmware downloading problem, a call to cw1200_pm_init was not followed by a call to cw1200_pm_deinit. CW1200 PM device was not removed properly due to that and consecutive attempt to initialize driver failed. The patch balances calls to cw1200_pm_(de)init. ST-Ericsson ID: 418642 Change-Id: I3d22372a89291f394655c6363b82d33c4f9b936f Signed-off-by: Dmitry Tarnyagin --- drivers/staging/cw1200/main.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 92f127e2f33..06434629dcd 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -358,11 +358,6 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) priv->ba_timer.data = (unsigned long)priv; priv->ba_timer.function = cw1200_ba_timer; - if (unlikely(cw1200_pm_init(&priv->pm_state, priv))) { - ieee80211_free_hw(hw); - return NULL; - } - if (unlikely(cw1200_queue_stats_init(&priv->tx_queue_stats, CW1200_LINK_ID_MAX, cw1200_skb_dtor, @@ -402,17 +397,28 @@ int cw1200_register_common(struct ieee80211_hw *dev) struct cw1200_common *priv = dev->priv; int err; + err = cw1200_pm_init(&priv->pm_state, priv); + if (err) { + cw1200_dbg(CW1200_DBG_ERROR, "Cannot init PM. (%d).\n", + err); + return err; + } + err = ieee80211_register_hw(dev); if (err) { cw1200_dbg(CW1200_DBG_ERROR, "Cannot register device (%d).\n", err); + cw1200_pm_deinit(&priv->pm_state); return err; } #ifdef CONFIG_CW1200_LEDS err = cw1200_init_leds(priv); - if (err) + if (err) { + cw1200_pm_deinit(&priv->pm_state); + ieee80211_unregister_hw(dev); return err; + } #endif /* CONFIG_CW1200_LEDS */ cw1200_debug_init(priv); -- cgit v1.2.3 From 78762a0d0df46d2014248887936378e126fc184c Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 29 Feb 2012 15:15:46 +0100 Subject: cw1200: Detect "stuck" TX frames at early stage. The patch implements early detection of frames, not acknowledged by firmware. ST-Ericsson ID: 418642 Change-Id: I7af4e2b0aa87a31dd38a3820cb66f5ebc3141978 Signed-off-by: Dmitry Tarnyagin --- drivers/staging/cw1200/bh.c | 29 +++++++++++++++++++++++++++++ drivers/staging/cw1200/wsm.c | 1 - drivers/staging/cw1200/wsm.h | 2 ++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c index 22b43a5f1cd..427f3e2ba52 100644 --- a/drivers/staging/cw1200/bh.c +++ b/drivers/staging/cw1200/bh.c @@ -265,7 +265,9 @@ static int cw1200_bh(void *arg) int tx_burst; int rx_burst = 0; long status; +#if defined(CONFIG_CW1200_WSM_DUMPS) size_t wsm_dump_max = -1; +#endif u32 dummy; for (;;) { @@ -301,8 +303,35 @@ static int cw1200_bh(void *arg) break; if (!status && priv->hw_bufs_used) { + unsigned long timestamp = jiffies; + long timeout; + bool pending = false; + int i; + wiphy_warn(priv->hw->wiphy, "Missed interrupt?\n"); rx = 1; + + /* Get a timestamp of "oldest" frame */ + for (i = 0; i < 4; ++i) + pending |= cw1200_queue_get_xmit_timestamp( + &priv->tx_queue[i], + ×tamp); + + /* Check if frame transmission is timed out. + * Add an extra second with respect to possible + * interrupt loss. */ + timeout = timestamp + + WSM_CMD_LAST_CHANCE_TIMEOUT + + 1 * HZ - + jiffies; + + /* And terminate BH tread if the frame is "stuck" */ + if (pending && timeout < 0) { + wiphy_warn(priv->hw->wiphy, + "Timeout waiting for TX confirm.\n"); + break; + } + #if defined(CONFIG_CW1200_DUMP_ON_ERROR) BUG_ON(1); #endif /* CONFIG_CW1200_DUMP_ON_ERROR */ diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index d1ea95c28e4..3cf53704f07 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -33,7 +33,6 @@ #define WSM_CMD_JOIN_TIMEOUT (7 * HZ) /* Join timeout is 5 sec. in FW */ #define WSM_CMD_START_TIMEOUT (7 * HZ) #define WSM_CMD_RESET_TIMEOUT (3 * HZ) /* 2 sec. timeout was observed. */ -#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 3 / 2) #define WSM_SKIP(buf, size) \ do { \ diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h index fb15ef53294..c3bd002d432 100644 --- a/drivers/staging/cw1200/wsm.h +++ b/drivers/staging/cw1200/wsm.h @@ -598,6 +598,8 @@ struct wsm_hdr { #define MAX_BEACON_SKIP_TIME_MS 1000 +#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 3 / 2) + /* ******************************************************************** */ /* WSM capcbility */ -- cgit v1.2.3 From 050d441e966431926e92357d1810340ddecc2290 Mon Sep 17 00:00:00 2001 From: Yann Gautier Date: Tue, 10 Jan 2012 09:36:14 +0100 Subject: wlan: mach-ux500: Add support for 9540 ST-Ericsson ID: 398896 Signed-off-by: Yann Gautier --- arch/arm/mach-ux500/board-mop500-wlan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/board-mop500-wlan.c b/arch/arm/mach-ux500/board-mop500-wlan.c index 46037ca76ae..6ef93e3cbf0 100644 --- a/arch/arm/mach-ux500/board-mop500-wlan.c +++ b/arch/arm/mach-ux500/board-mop500-wlan.c @@ -188,7 +188,7 @@ int __init mop500_wlan_init(void) } else if (machine_is_u8500() || machine_is_nomadik() || machine_is_snowball()) { cw1200_device.num_resources = ARRAY_SIZE(cw1200_href_resources); cw1200_device.resource = cw1200_href_resources; - } else if (machine_is_hrefv60()) { + } else if (machine_is_hrefv60() || machine_is_u9540()) { cw1200_device.num_resources = ARRAY_SIZE(cw1200_href60_resources); cw1200_device.resource = cw1200_href60_resources; -- cgit v1.2.3 From 99dc13f4e5ccbdb914f76e2fbf76e1a27c31df96 Mon Sep 17 00:00:00 2001 From: Bengt Jonsson Date: Thu, 2 Feb 2012 11:36:49 +0100 Subject: wlan: ux500: Add U8520 machine ST-Ericsson ID: 371953 Signed-off-by: Bengt Jonsson --- arch/arm/mach-ux500/board-mop500-wlan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-ux500/board-mop500-wlan.c b/arch/arm/mach-ux500/board-mop500-wlan.c index 6ef93e3cbf0..86cce36c9a9 100644 --- a/arch/arm/mach-ux500/board-mop500-wlan.c +++ b/arch/arm/mach-ux500/board-mop500-wlan.c @@ -188,7 +188,8 @@ int __init mop500_wlan_init(void) } else if (machine_is_u8500() || machine_is_nomadik() || machine_is_snowball()) { cw1200_device.num_resources = ARRAY_SIZE(cw1200_href_resources); cw1200_device.resource = cw1200_href_resources; - } else if (machine_is_hrefv60() || machine_is_u9540()) { + } else if (machine_is_hrefv60() || machine_is_u8520() + || machine_is_u9540()) { cw1200_device.num_resources = ARRAY_SIZE(cw1200_href60_resources); cw1200_device.resource = cw1200_href60_resources; -- cgit v1.2.3 From 3980686d6ccb1201b543a590fc31bc839157bb5b Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 22 Mar 2012 09:00:13 +0100 Subject: cw1200: wakelock: Change for new Google wakelock implementation over PM sleep Signed-off-by: Philippe Langlais --- drivers/staging/cw1200/pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/pm.c b/drivers/staging/cw1200/pm.c index d95e5d3dc9e..98796cb2b0b 100644 --- a/drivers/staging/cw1200/pm.c +++ b/drivers/staging/cw1200/pm.c @@ -166,7 +166,7 @@ void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, { long cur_tmo; spin_lock_bh(&pm->lock); - cur_tmo = pm->wakelock.expires - jiffies; + cur_tmo = pm->wakelock.ws.timer_expires - jiffies; if (!wake_lock_active(&pm->wakelock) || cur_tmo < (long)tmo) wake_lock_timeout(&pm->wakelock, tmo); -- cgit v1.2.3 From aca1adce326e06dcdb18e8201719ad7eaab274f5 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 20 Apr 2012 08:46:22 +0200 Subject: cw1200: Fix after interface change in mac80211 for 3.4 Signed-off-by: Philippe Langlais --- drivers/staging/cw1200/main.c | 4 +--- drivers/staging/cw1200/sta.c | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c index 06434629dcd..c31a4d45929 100644 --- a/drivers/staging/cw1200/main.c +++ b/drivers/staging/cw1200/main.c @@ -256,8 +256,6 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_CONNECTION_MONITOR | - IEEE80211_HW_SUPPORTS_CQM_RSSI | - IEEE80211_HW_NEED_DTIM_PERIOD | /* Aggregation is fully controlled by firmware. * Do not need any support from the mac80211 stack */ /* IEEE80211_HW_AMPDU_AGGREGATION | */ @@ -266,7 +264,7 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len) IEEE80211_HW_SUPPORTS_CQM_BEACON_MISS | IEEE80211_HW_SUPPORTS_CQM_TX_FAIL | #endif /* CONFIG_CW1200_USE_STE_EXTENSIONS */ - IEEE80211_HW_BEACON_FILTER; + IEEE80211_HW_NEED_DTIM_PERIOD; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 54b681aba0e..cc9ed7b1507 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -158,6 +158,9 @@ int cw1200_add_interface(struct ieee80211_hw *dev, struct cw1200_common *priv = dev->priv; /* __le32 auto_calibration_mode = __cpu_to_le32(1); */ + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_CQM_RSSI; + mutex_lock(&priv->conf_mutex); if (priv->mode != NL80211_IFTYPE_MONITOR) { -- cgit v1.2.3 From 5c5f4d2df9e9c8eacfdab76ac8060d80c802616c Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 25 Apr 2012 16:36:08 +0200 Subject: cw1200: Manage parent device during initialization Signed-off-by: Philippe Langlais --- arch/arm/mach-ux500/board-mop500-wlan.c | 3 ++- arch/arm/mach-ux500/board-mop500-wlan.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-wlan.c b/arch/arm/mach-ux500/board-mop500-wlan.c index 86cce36c9a9..821baae5273 100644 --- a/arch/arm/mach-ux500/board-mop500-wlan.c +++ b/arch/arm/mach-ux500/board-mop500-wlan.c @@ -178,7 +178,7 @@ static int cw1200_clk_ctrl(const struct cw1200_platform_data *pdata, return ret; } -int __init mop500_wlan_init(void) +int __init mop500_wlan_init(struct device *parent) { int ret; @@ -218,6 +218,7 @@ int __init mop500_wlan_init(void) if (WARN_ON(ret)) return ret; + cw1200_device.dev.parent = parent; ret = platform_device_register(&cw1200_device); if (ret) cw1200_pins_enable(false); diff --git a/arch/arm/mach-ux500/board-mop500-wlan.h b/arch/arm/mach-ux500/board-mop500-wlan.h index c6788adc46f..19a12d57ded 100644 --- a/arch/arm/mach-ux500/board-mop500-wlan.h +++ b/arch/arm/mach-ux500/board-mop500-wlan.h @@ -12,6 +12,6 @@ #ifndef __BOARD_MOP500_WLAN_H #define __BOARD_MOP500_WLAN_H -int mop500_wlan_init(void); +int mop500_wlan_init(struct device *parent); #endif -- cgit v1.2.3