From 80a86868435a1641fdcf036532d5f58b62a1a0b2 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 2d82d71ff8bb110554bfa4a822ea2ad166bc30e8 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 183a45f5a8718ee48025ae0b5fcd2fddddac8ba5 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 7811377b17e9b13adb4f0ff80d87184efd72b242 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 9aa13340137269e4d702edac3be8909697982a6b 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 35ee5c9e28b3453f26da70c6962904262b3a3c48 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 e2431c3b0264c4c7794d4fa0e9cef084fa61d574 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 f7ff31625fa062bedbd9d5e65268df6954e109e1 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 4229f098abdc0f217fb883888c9051fdb4afe521 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 43b72da01ec9a49636a82c8b028586c65db77fa0 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 3b964b666ce4b5e049b15bebc77f939b3617556f 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 10515bd6fef6b3c2452d181d39a7a34852cb7632 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 3f623c504de3c070f1aa1a229e89d0edf289a41c 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 7706df572f06fb349386339ee32759184f8df7b9 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 636aed7c6f4421d0047b7ce44bf5a89beb0857f5 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 fffcf20874be6ce2e6f34603a193cc42f4418411 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 d59d4325fd12dcf3af905de98204f3208705dbc8 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 6cda0908db8c131dcf54733446b3e13a31cfa615 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 d7f073c04c7b38574f561e31fc66e72210463546 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 3af26e2e664c70b913d633c6fe961eca6b8d748d 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 79898de1426965d8351822d1408771933a6851bb 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 9fe52a9e1e60bd80658fa403f3047a85139c0aa8 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 e52903261344b084c57623c5b37b1c21f937c7a5 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 25c16635507132473dbd411829065fb745b7812a 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 c36d56e3e79edb601d8935c4d3149241d53ed43f 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 985b179f8f8ec51387481c697ea026c203863da9 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 a6918ea55399bd8f1e673193bef08432a7a0f844 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 f336fa5e26364004b93823d44adf613bd65a6fce 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 36353eb67a222bea14af57707eab62448e9088d5 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 00df8595a13716d44a838138f020bf7c9c7318ca 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 b47e5b37ee165a28de830f5c62c6478e3c16297e 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 14e24915d46da25b7e6a4045d52702a02b5b04cf 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 439925f964af4ef84bafe78f85dd15be5c074e28 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 74a91a54e5634d861deb1ed453c226f164f61ea0 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 d34ffdb0d3cc01ffc2b8ff85c21a488a592fe327 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 8048eed5c5baba1420d61c8698a392cefbc62ff9 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 1381a5abcae9422ee6e4b9458463cc0644d83a20 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 61f7a18e050286e6b6cae52a3042941e7a13a060 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 485e1d6d22cbe81312b113a63dbb931ad20e16e1 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 2175093878d8bb1f2f26fe5591ecde35d8c84457 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 a2b373bc9ed47e1bbc8a9d340f0df1ea5175bf6b 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 f748ae2a7b28e2326b78b9bb894993d1c36fc5b4 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 49e69970e3e3f4a719a6c2cb1492f76805522dad 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 292a1fd2298c937349db8a27b25ef0bd041186d8 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 e89ba097b4a787730cc1c9a238e8013f5d3d02f7 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 4dbfd5fc8146211d67a3bdf7c242ca059f0376d3 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 1404ca9d9607fbd0c0343e9004d868c934ae89c5 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 610a2c828316269024f687998546535ee5a2ad4c 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 36c52ec9796d47ecfcb13c5009bf683b74b3a193 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 c0d0cd8b7dfee896b7cb781ab9bc76706eef10eb 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 f65a9c82d86a0ba96a7661bf279ee8d8e349b71e 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 37370c6981a4bacd10c7913354ed01d317f3dfa5 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 e39768381acd2a2e6225f26c8b8d797e77c0f136 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 72bc73e348a4636cf5f49680aab4a6efe32d6527 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 51cb47b3ed76d8578da5bf0644a6315382a3aa56 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 9092c7c6b1676df34d41d7c3f756844ac9f6f371 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 91cf1e02534d4495e885d541c38bc00ce733b993 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 f0cb80a66dda0fe4bd0925916e187fd852f0bb96 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 a455544d5cd0c712ca71bcfbc58d624840f9b950 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 e1f5e512b55982a466eb30fe4c1dd3d325be40ad 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 9e4b4621ac36e7f1e39d0e2e857c83f810244499 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 6d3685c580cf70fee8b72d191ddb7556f35f1947 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 f181c08faf4b93ad64938e8a5f7b986d9c6f3ee5 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 ab3582e2297b1f46955b0bea119e51e5e20a4484 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 df0d534b560b059520be52bb6e6f8581945593e6 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 202fa9eb4ea63022712ce3d8f6a233fd14a0fd79 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 cc3c1b947fc3ebcb7fc63f5acc990748178481f8 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 02144716b946abbcd2c9958e5e8d8e6fec7683a8 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 945c4db441e8c2c675f1bab0bf07694818af8777 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 1d0c333db7312bc21fa3d3aad594cf6e20573891 Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 10 Aug 2011 19:15:13 +0200 Subject: cw1200: Purge cfg80211 beacon cache before authentication. cw1200 device requires SSID to be available at AUTH stage. cfg80211 beacon cache is designed to handle multi-SSID BSSes, so bss struct returned by cfg80211_get_bss() has random SSID if BSS just changed SSID before authentication (typical for p2p). This is a firmware design fault, however as a workaround cfg80211 beacon cache is purged to make sure target BSS is searchable in rb-tree at the AUTH stage. Likely will not be accepted by community. Change-Id: I38d071e0d32bf414906170a19134718b0e834cce Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28558 Tested-by: Dmitry TARNYAGIN Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/28696 Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33504 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/sta.c | 3 ++- net/mac80211/ieee80211_i.h | 1 + net/mac80211/mlme.c | 1 + net/mac80211/work.c | 24 ++++++++++++++++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c index 17a4184a360..d6a25a27358 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1027,7 +1027,8 @@ 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); + bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, + bssid, NULL, 0, 0, 0); if (!bss) { cw1200_queue_remove(&priv->tx_queue[queueId], priv, __le32_to_cpu(wsm->packetID)); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 400c09bea63..e34b986b472 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -324,6 +324,7 @@ struct ieee80211_work { u8 key_len, key_idx; bool privacy; bool synced; + struct cfg80211_bss *bss; } probe_auth; struct { struct cfg80211_bss *bss; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d6470c7fd6c..19fecc95391 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2414,6 +2414,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, wk->probe_auth.algorithm = auth_alg; wk->probe_auth.privacy = req->bss->capability & WLAN_CAPABILITY_PRIVACY; + wk->probe_auth.bss = req->bss; /* if we already have a probe, don't probe again */ if (req->bss->proberesp_ies) diff --git a/net/mac80211/work.c b/net/mac80211/work.c index 380b9a7462b..c0c97317591 100644 --- a/net/mac80211/work.c +++ b/net/mac80211/work.c @@ -475,6 +475,30 @@ ieee80211_authenticate(struct ieee80211_work *wk) struct ieee80211_sub_if_data *sdata = wk->sdata; struct ieee80211_local *local = sdata->local; + /* HACK!!! cw1200 device requires SSID to be available at AUTH stage. + * cfg80211 beacon cache is designed to handle multi-SSID BSSes, so + * bss struct returned by cfg80211_get_bss() has random SSID if BSS + * just changed SSID before authentication (typical for p2p). + * This is a firmware design fault, however as a workaround cfg80211 + * beacon cache is purged to make sure target BSS is searchable + * in rb-tree at the AUTH stage. + */ + struct cfg80211_bss *bss; + while (true) { + bss = cfg80211_get_bss(local->hw.wiphy, + wk->probe_auth.bss->channel, + wk->probe_auth.bss->bssid, + NULL, 0, 0, 0); + if (WARN_ON(!bss)) + break; + if (bss == wk->probe_auth.bss) { + cfg80211_put_bss(bss); + break; + } + cfg80211_unlink_bss(local->hw.wiphy, bss); + } + /* End of the hack */ + if (!wk->probe_auth.synced) { int ret = drv_tx_sync(local, sdata, wk->filter_ta, IEEE80211_TX_SYNC_AUTH); -- cgit v1.2.3 From e5708c1cbb9bd5eaefb82088eebe4be22ba09b12 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 d6a25a27358..c1cedf6db52 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 bfd0cec25c781df22eed161ba48c39381dfe4fad 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 c1cedf6db52..df9a8ead315 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 f8f91f793b61f914a8da6a2a6377402d4b0f1d5e 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 d619f430580c22b00418e3e9e09249ea13e90ed4 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 d4f5c0feea301e80eac58d7dad4b2b78daa2a9b1 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 df9a8ead315..055d231fc01 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) @@ -1169,6 +1198,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); @@ -1222,6 +1253,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 44cba5c7d6b5fdc8a5cabba49ce6b8495e60e5c2 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 35208e857904a71e999d45aa9d35df10de5e8887 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 68cf68c8e0f01100e7e59fa178555704e9b9f5f6 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 055d231fc01..47608bb5ed8 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1166,13 +1166,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 7b4804016029ff07e210edfc9eb4b3bf7af55cc9 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 47608bb5ed8..b35dbca7ba0 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 d1fc21f98070c32635b359f80d0197df3800008d 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 07fc1c4b43c0f2eb92c5d3f17411a56dfa416f0e 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 0070e1138383bc9e5550f58d403441cdeb6d326a 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 fc71cbe95ab318cf194a4b00805e32bfef829066 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 441326a5651b027b063e270e9e26e609a7e5cf8b 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 b35dbca7ba0..f6227833248 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 10805d92cef1536137760c5d3c5f81bca1077bcf 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 f6227833248..3da13d597ef 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1114,6 +1114,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 61b44d060e79d6d1853bb2cacc20e8a5b7d66a71 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 3da13d597ef..9e14d30253c 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 c6a5d509eb3dc99fb9fddde116c9bcbafbf84124 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 3f59b14e183867bb8552695acf7ca6df556ca37b 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 9e14d30253c..49a62014b2e 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 3ce31e196afb31390406f4a521f682e30bbbffb6 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 5dd0464f6fe76a4d602a63b42f12b23be91f22f4 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 49a62014b2e..47fe596f59f 100644 --- a/drivers/staging/cw1200/sta.c +++ b/drivers/staging/cw1200/sta.c @@ -1120,9 +1120,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); @@ -1143,6 +1140,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); @@ -1203,6 +1202,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 88c274cff704c69caf675eb45bd744f939ef77d1 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 47fe596f59f..a8bebd1060e 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 6d1835d9dd0e939620f404d3f3d90a63004f7f40 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 1196db7a4cf0aebacaf1fd19de83a9ae9acedc56 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 a8bebd1060e..82c410c6c88 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 5eaf74153164036623d19a2ce771a4177ae4366c 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 82c410c6c88..4aa320d76d9 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; @@ -1099,6 +1161,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 42de2566ec670146ce17b21f94c1a1c4543030d9 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 ad3034e50e80d117fb43b833a215d517a12564d1 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 815a1fc0a2fd54e41bdecfb7e250b4884a582faf 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 642b1a5cc10ed5f54624e3a336a15a4f6fab9aad 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 64dd38d2e499c9d7bbaf6db616eee2a7f15adcca 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 604bcb624622087eea3bb7e21ae4369a0455129e 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 4aa320d76d9..ecfdc7562f3 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 f6070378982ec4ad526eff193b8aff1ef455ce6e 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 7c6c20b6471e9a4b981b8bc820fb3693c93b3f36 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 e946c1738cce44a90a12213b582f4ab45c423558 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 8a2fe0ce9f2d7b236d9c3e9eab368daf1c07014a 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 99e405cad1688a8bdc4314d3fac1ecd4a94b09b4 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 d27c66e34f62332072fd54af4c852bff43573207 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 9a3c55f33c054e4fe6dd2ddd507ba324d5c89d87 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 ecfdc7562f3..b6ea70f3418 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 6d417cf73f58f20eabe9668d6f78427cfc65a4f4 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 b6ea70f3418..1ba769df65d 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, @@ -1136,7 +1136,7 @@ void cw1200_join_work(struct work_struct *work) 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; } @@ -1211,7 +1211,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 a342c6e449c929907bfe48faec05dc95318257c0 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 1ba769df65d..ddc73e45744 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 c073734dcc2984ff15ee9094d95cd750efdb69e5 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 ddc73e45744..2ddbc1aa2b0 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 d7878c2068b0449a270cfd27c04f20da61ec3ea5 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 2ddbc1aa2b0..908294c8a93 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,12 +1157,10 @@ 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, priv->channel, 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; } @@ -1213,16 +1234,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 032c7554976fd8d02f2c91440de69c7a89587ab5 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 908294c8a93..9eb7879bab2 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 ce2f3ab9f201ce759d77d6f30e4568f6a6ac8274 Mon Sep 17 00:00:00 2001 From: "Ajitpal.Singh" Date: Wed, 21 Sep 2011 16:56:14 +0530 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 Change-Id: Idbb60459ca645575c2afc74f2421b0fa86121cf0 Signed-off-by: Ajitpal.Singh Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33668 Tested-by: Dmitry TARNYAGIN Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/debug.c | 4 ++-- drivers/staging/cw1200/debug.h | 8 ++++---- drivers/staging/cw1200/txrx.c | 27 +++++++++++++++++++-------- drivers/staging/cw1200/wsm.c | 15 +++++++-------- drivers/staging/cw1200/wsm.h | 3 +++ 5 files changed, 35 insertions(+), 22 deletions(-) diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 91c690ad4e6..297b83e1ef5 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -288,8 +288,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 6f7d8acab00..aab0c061648 100644 --- a/drivers/staging/cw1200/debug.h +++ b/drivers/staging/cw1200/debug.h @@ -14,7 +14,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; }; @@ -53,9 +53,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) @@ -99,7 +99,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 ef5accb19a0..a03a17bb79c 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -508,14 +508,22 @@ 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; 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 " @@ -524,10 +532,11 @@ cw1200_tx_h_align(struct cw1200_common *priv, 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); + skb_push(t->skb, offset); + t->hdrlen += offset; + t->txpriv.offset += offset; + *flags |= WSM_TX_2BYTES_SHIFT; + cw1200_debug_tx_align(priv); return 0; } @@ -645,7 +654,7 @@ 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; + wsm->flags |= t->txpriv.rate_id << 4; if (tx_policy_renew) { tx_policy_printk(KERN_DEBUG "[TX] TX policy renew.\n"); @@ -696,6 +705,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, @@ -722,7 +732,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); @@ -733,6 +743,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 10303fae3c8..295b13f5676 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 @@ -1619,8 +1618,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( @@ -1635,7 +1633,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 f0b07dc08a2..6698d771396 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 c6dfa907e87d3e7abde694713325ac893a87814b 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 295b13f5676..a8c49494e49 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -1403,17 +1403,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 c8e2ca0b678b4ec50020e1b061857e02bdf9ad1a 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 d8cda82be9d0401e4ca0ea1c5a0dbf1047272dff 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 95ab4f036a4a36883ccd943fde1c88a9b4b0f905 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 49224007368ee4054073aa7ab8536461dddc4bb8 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 0ffb416d682df31c959b4769659d8c47bc0e8c91 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 a03a17bb79c..30532f3ea90 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 7f7ae327ebaad3f22e7cef63e31f1d25e68176f9 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Wed, 1 Jun 2011 14:40:13 +0200 Subject: wireless-next: WAPI support for hardware-accelerated drivers Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24551 Tested-by: Janusz DZIEDZIC Reviewed-by: Janusz DZIEDZIC Change-Id: I0300854865f8de07413164fd4461a9c8aae5dff3 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/26976 Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33473 Reviewed-by: Philippe LANGLAIS --- include/linux/ieee80211.h | 3 ++ include/linux/nl80211.h | 5 ++-- include/linux/wireless.h | 6 ++++ net/mac80211/Makefile | 1 + net/mac80211/ieee80211_i.h | 2 +- net/mac80211/key.c | 4 +++ net/mac80211/key.h | 2 ++ net/mac80211/main.c | 1 + net/mac80211/rx.c | 4 +++ net/mac80211/tx.c | 6 ++++ net/mac80211/wapi.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++ net/mac80211/wapi.h | 27 ++++++++++++++++++ net/wireless/nl80211.c | 13 +++++---- net/wireless/util.c | 8 ++++++ net/wireless/wext-compat.c | 39 ++++++++++++++++++++++--- 15 files changed, 180 insertions(+), 12 deletions(-) create mode 100644 net/mac80211/wapi.c create mode 100644 net/mac80211/wapi.h diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 54c87896087..a721171ac77 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1316,6 +1316,7 @@ enum ieee80211_key_len { WLAN_KEY_LEN_CCMP = 16, WLAN_KEY_LEN_TKIP = 32, WLAN_KEY_LEN_AES_CMAC = 16, + WLAN_KEY_LEN_SMS4 = 32, }; /** @@ -1442,12 +1443,14 @@ enum ieee80211_sa_query_action { #define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 #define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 #define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 +#define WLAN_CIPHER_SUITE_SMS4 0x000FAC07 /* AKM suite selectors */ #define WLAN_AKM_SUITE_8021X 0x000FAC01 #define WLAN_AKM_SUITE_PSK 0x000FAC02 #define WLAN_AKM_SUITE_SAE 0x000FAC08 #define WLAN_AKM_SUITE_FT_OVER_SAE 0x000FAC09 +#define WLAN_AKM_SUITE_WAPI_PSK 0x000FAC03 #define WLAN_MAX_KEY_LEN 32 diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 8ad70dcac3f..9d0c8ac8d6b 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1266,8 +1266,8 @@ enum nl80211_attrs { #define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 #define NL80211_HT_CAPABILITY_LEN 26 -#define NL80211_MAX_NR_CIPHER_SUITES 5 -#define NL80211_MAX_NR_AKM_SUITES 2 +#define NL80211_MAX_NR_CIPHER_SUITES 6 +#define NL80211_MAX_NR_AKM_SUITES 3 /** * enum nl80211_iftype - (virtual) interface types @@ -2063,6 +2063,7 @@ enum nl80211_mfp { enum nl80211_wpa_versions { NL80211_WPA_VERSION_1 = 1 << 0, NL80211_WPA_VERSION_2 = 1 << 1, + NL80211_WAPI_VERSION_1 = 1 << 2, }; /** diff --git a/include/linux/wireless.h b/include/linux/wireless.h index 4395b28bb86..f3e2375ee24 100644 --- a/include/linux/wireless.h +++ b/include/linux/wireless.h @@ -586,6 +586,7 @@ #define IW_AUTH_WPA_VERSION_DISABLED 0x00000001 #define IW_AUTH_WPA_VERSION_WPA 0x00000002 #define IW_AUTH_WPA_VERSION_WPA2 0x00000004 +#define IW_AUTH_WPA_VERSION_WAPI 0x00000008 /* IW_AUTH_PAIRWISE_CIPHER, IW_AUTH_GROUP_CIPHER, and IW_AUTH_CIPHER_GROUP_MGMT * values (bit field) */ @@ -595,10 +596,12 @@ #define IW_AUTH_CIPHER_CCMP 0x00000008 #define IW_AUTH_CIPHER_WEP104 0x00000010 #define IW_AUTH_CIPHER_AES_CMAC 0x00000020 +#define IW_AUTH_CIPHER_SMS4 0x00000040 /* IW_AUTH_KEY_MGMT values (bit field) */ #define IW_AUTH_KEY_MGMT_802_1X 1 #define IW_AUTH_KEY_MGMT_PSK 2 +#define IW_AUTH_KEY_MGMT_WAPI_PSK 4 /* IW_AUTH_80211_AUTH_ALG values (bit field) */ #define IW_AUTH_ALG_OPEN_SYSTEM 0x00000001 @@ -624,6 +627,7 @@ #define IW_ENCODE_ALG_CCMP 3 #define IW_ENCODE_ALG_PMK 4 #define IW_ENCODE_ALG_AES_CMAC 5 +#define IW_ENCODE_ALG_SMS4 6 /* struct iw_encode_ext ->ext_flags */ #define IW_ENCODE_EXT_TX_SEQ_VALID 0x00000001 #define IW_ENCODE_EXT_RX_SEQ_VALID 0x00000002 @@ -644,6 +648,8 @@ #define IW_ENC_CAPA_CIPHER_TKIP 0x00000004 #define IW_ENC_CAPA_CIPHER_CCMP 0x00000008 #define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010 +#define IW_ENC_CAPA_WAPI 0x00000020 +#define IW_ENC_CAPA_CIPHER_SMS4 0x00000040 /* Event capability macros - in (struct iw_range *)->event_capa * Because we have more than 32 possible events, we use an array of diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index fdb54e61d63..ed3dd35f708 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -6,6 +6,7 @@ mac80211-y := \ sta_info.o \ wep.o \ wpa.o \ + wapi.o \ scan.o offchannel.o \ ht.o agg-tx.o agg-rx.o \ ibss.o \ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index e34b986b472..1d4a81f4c1b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -42,7 +42,7 @@ struct ieee80211_local; #define TOTAL_MAX_TX_BUFFER 512 /* Required encryption head and tailroom */ -#define IEEE80211_ENCRYPT_HEADROOM 8 +#define IEEE80211_ENCRYPT_HEADROOM 20 #define IEEE80211_ENCRYPT_TAILROOM 18 /* IEEE 802.11 (Ch. 9.5 Defragmentation) requires support for concurrent diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 5150c6d11b5..1e8bf5d8298 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -409,6 +409,10 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, return ERR_PTR(err); } break; + case WLAN_CIPHER_SUITE_SMS4: + key->conf.iv_len = WAPI_IV_LEN; + key->conf.icv_len = WAPI_ICV_LEN; + break; } memcpy(key->conf.key, key_data, key_len); INIT_LIST_HEAD(&key->list); diff --git a/net/mac80211/key.h b/net/mac80211/key.h index 7d4e31f037d..455f1febd5b 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -29,6 +29,8 @@ #define TKIP_IV_LEN 8 #define TKIP_ICV_LEN 4 #define CMAC_PN_LEN 6 +#define WAPI_IV_LEN 18 +#define WAPI_ICV_LEN 16 #define NUM_RX_DATA_QUEUES 16 diff --git a/net/mac80211/main.c b/net/mac80211/main.c index acb44230b25..64bb140bb67 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -695,6 +695,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_SMS4, /* keep last -- depends on hw flags! */ WLAN_CIPHER_SUITE_AES_CMAC diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index fe2c2a71779..a86e0df2fe8 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -27,6 +27,7 @@ #include "wpa.h" #include "tkip.h" #include "wme.h" +#include "wapi.h" /* * monitor mode reception @@ -1053,6 +1054,9 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx) case WLAN_CIPHER_SUITE_AES_CMAC: result = ieee80211_crypto_aes_cmac_decrypt(rx); break; + case WLAN_CIPHER_SUITE_SMS4: + result = ieee80211_crypto_wapi_decrypt(rx); + break; default: /* * We can reach here only with HW-only algorithms diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 8cb0d2d0ac6..b80a0ff095e 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -30,6 +30,7 @@ #include "mesh.h" #include "wep.h" #include "wpa.h" +#include "wapi.h" #include "wme.h" #include "rate.h" @@ -587,6 +588,11 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) if (!ieee80211_is_mgmt(hdr->frame_control)) tx->key = NULL; break; + + case WLAN_CIPHER_SUITE_SMS4: + if (tx->ethertype == ETH_P_WAPI) + tx->key = NULL; + break; } if (unlikely(tx->key && tx->key->flags & KEY_FLAG_TAINTED)) diff --git a/net/mac80211/wapi.c b/net/mac80211/wapi.c new file mode 100644 index 00000000000..4780808ad2f --- /dev/null +++ b/net/mac80211/wapi.c @@ -0,0 +1,71 @@ +/* + * Software WAPI encryption implementation + * Copyright (c) 2011, ST-Ericsson + * Author: Janusz Dziedzic + * + * 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 +#include + +#include +#include "ieee80211_i.h" +#include "wapi.h" + + +static int ieee80211_wapi_decrypt(struct ieee80211_local *local, + struct sk_buff *skb, + struct ieee80211_key *key) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + int hdrlen = ieee80211_hdrlen(hdr->frame_control); + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + int data_len; + + if (!(status->flag & RX_FLAG_DECRYPTED)) { + /* TODO - SMS4 decryption for firmware without + * SMS4 support */ + return RX_DROP_UNUSABLE; + } + + + data_len = skb->len - hdrlen - WAPI_IV_LEN - WAPI_ICV_LEN; + if (data_len < 0) + return RX_DROP_UNUSABLE; + + /* Trim ICV */ + skb_trim(skb, skb->len - WAPI_ICV_LEN); + + /* Remove IV */ + memmove(skb->data + WAPI_IV_LEN, skb->data, hdrlen); + skb_pull(skb, WAPI_IV_LEN); + + return RX_CONTINUE; +} + +ieee80211_rx_result +ieee80211_crypto_wapi_decrypt(struct ieee80211_rx_data *rx) +{ + struct sk_buff *skb = rx->skb; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + if (!ieee80211_is_data(hdr->frame_control)) + return RX_CONTINUE; + + if (ieee80211_wapi_decrypt(rx->local, rx->skb, rx->key)) + return RX_DROP_UNUSABLE; + + return RX_CONTINUE; +} diff --git a/net/mac80211/wapi.h b/net/mac80211/wapi.h new file mode 100644 index 00000000000..f06eee0cb7c --- /dev/null +++ b/net/mac80211/wapi.h @@ -0,0 +1,27 @@ +/* + * Software WAPI encryption implementation + * Copyright (c) 2011, ST-Ericsson + * Author: Janusz Dziedzic + * + * 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 WAPI_H +#define WAPI_H + +#include +#include +#include "ieee80211_i.h" +#include "key.h" + +#ifndef ETH_P_WAPI +#define ETH_P_WAPI 0x88B4 +#endif + + +ieee80211_rx_result +ieee80211_crypto_wapi_decrypt(struct ieee80211_rx_data *rx); + +#endif /* WAPI_H */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ea40d540a99..39180512bfc 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -92,7 +92,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 }, [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 }, [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG }, - [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 }, + [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 }, [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 }, [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 }, @@ -185,7 +185,7 @@ static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = { [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, [NL80211_KEY_IDX] = { .type = NLA_U8 }, [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, - [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 }, + [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 }, [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG }, [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG }, [NL80211_KEY_TYPE] = { .type = NLA_U32 }, @@ -3932,13 +3932,15 @@ static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) static bool nl80211_valid_wpa_versions(u32 wpa_versions) { return !(wpa_versions & ~(NL80211_WPA_VERSION_1 | - NL80211_WPA_VERSION_2)); + NL80211_WPA_VERSION_2 | + NL80211_WAPI_VERSION_1)); } static bool nl80211_valid_akm_suite(u32 akm) { return akm == WLAN_AKM_SUITE_8021X || - akm == WLAN_AKM_SUITE_PSK; + akm == WLAN_AKM_SUITE_PSK || + akm == WLAN_AKM_SUITE_WAPI_PSK; } static bool nl80211_valid_cipher_suite(u32 cipher) @@ -3947,7 +3949,8 @@ static bool nl80211_valid_cipher_suite(u32 cipher) cipher == WLAN_CIPHER_SUITE_WEP104 || cipher == WLAN_CIPHER_SUITE_TKIP || cipher == WLAN_CIPHER_SUITE_CCMP || - cipher == WLAN_CIPHER_SUITE_AES_CMAC; + cipher == WLAN_CIPHER_SUITE_AES_CMAC || + cipher == WLAN_CIPHER_SUITE_SMS4; } diff --git a/net/wireless/util.c b/net/wireless/util.c index be75a3a0424..22cf49a59b5 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -199,6 +199,10 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, if (params->key_len != WLAN_KEY_LEN_AES_CMAC) return -EINVAL; break; + case WLAN_CIPHER_SUITE_SMS4: + if (params->key_len != WLAN_KEY_LEN_SMS4) + return -EINVAL; + break; default: /* * We don't know anything about this algorithm, @@ -222,6 +226,10 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, if (params->seq_len != 6) return -EINVAL; break; + case WLAN_CIPHER_SUITE_SMS4: + if (params->seq_len != 16) + return -EINVAL; + break; } } diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 0bf169bb770..ac23c31c82c 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -214,6 +214,11 @@ int cfg80211_wext_giwrange(struct net_device *dev, range->encoding_size[range->num_encoding_sizes++] = WLAN_KEY_LEN_WEP104; break; + + case WLAN_CIPHER_SUITE_SMS4: + range->enc_capa |= (IW_ENC_CAPA_CIPHER_SMS4 | + IW_ENC_CAPA_WAPI); + break; } } @@ -699,6 +704,9 @@ int cfg80211_wext_siwencodeext(struct net_device *dev, case IW_ENCODE_ALG_AES_CMAC: cipher = WLAN_CIPHER_SUITE_AES_CMAC; break; + case IW_ENCODE_ALG_SMS4: + cipher = WLAN_CIPHER_SUITE_SMS4; + break; default: return -EOPNOTSUPP; } @@ -959,17 +967,21 @@ static int cfg80211_set_wpa_version(struct wireless_dev *wdev, u32 wpa_versions) { if (wpa_versions & ~(IW_AUTH_WPA_VERSION_WPA | IW_AUTH_WPA_VERSION_WPA2| + IW_AUTH_WPA_VERSION_WAPI| IW_AUTH_WPA_VERSION_DISABLED)) return -EINVAL; if ((wpa_versions & IW_AUTH_WPA_VERSION_DISABLED) && (wpa_versions & (IW_AUTH_WPA_VERSION_WPA| - IW_AUTH_WPA_VERSION_WPA2))) + IW_AUTH_WPA_VERSION_WPA2| + IW_AUTH_WPA_VERSION_WAPI))) return -EINVAL; if (wpa_versions & IW_AUTH_WPA_VERSION_DISABLED) wdev->wext.connect.crypto.wpa_versions &= - ~(NL80211_WPA_VERSION_1|NL80211_WPA_VERSION_2); + ~(NL80211_WPA_VERSION_1| + NL80211_WPA_VERSION_2| + NL80211_WAPI_VERSION_1); if (wpa_versions & IW_AUTH_WPA_VERSION_WPA) wdev->wext.connect.crypto.wpa_versions |= @@ -979,6 +991,10 @@ static int cfg80211_set_wpa_version(struct wireless_dev *wdev, u32 wpa_versions) wdev->wext.connect.crypto.wpa_versions |= NL80211_WPA_VERSION_2; + if (wpa_versions & IW_AUTH_WPA_VERSION_WAPI) + wdev->wext.connect.crypto.wpa_versions |= + NL80211_WAPI_VERSION_1; + return 0; } @@ -1001,6 +1017,9 @@ static int cfg80211_set_cipher_group(struct wireless_dev *wdev, u32 cipher) WLAN_CIPHER_SUITE_AES_CMAC; else if (cipher & IW_AUTH_CIPHER_NONE) wdev->wext.connect.crypto.cipher_group = 0; + else if (cipher & IW_AUTH_CIPHER_SMS4) + wdev->wext.connect.crypto.cipher_group = + WLAN_CIPHER_SUITE_SMS4; else return -EINVAL; @@ -1037,7 +1056,12 @@ static int cfg80211_set_cipher_pairwise(struct wireless_dev *wdev, u32 cipher) nr_ciphers++; } - BUILD_BUG_ON(NL80211_MAX_NR_CIPHER_SUITES < 5); + if (cipher & IW_AUTH_CIPHER_SMS4) { + ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_SMS4; + nr_ciphers++; + } + + BUILD_BUG_ON(NL80211_MAX_NR_CIPHER_SUITES < 6); wdev->wext.connect.crypto.n_ciphers_pairwise = nr_ciphers; @@ -1050,7 +1074,8 @@ static int cfg80211_set_key_mgt(struct wireless_dev *wdev, u32 key_mgt) int nr_akm_suites = 0; if (key_mgt & ~(IW_AUTH_KEY_MGMT_802_1X | - IW_AUTH_KEY_MGMT_PSK)) + IW_AUTH_KEY_MGMT_PSK | + IW_AUTH_KEY_MGMT_WAPI_PSK)) return -EINVAL; if (key_mgt & IW_AUTH_KEY_MGMT_802_1X) { @@ -1065,6 +1090,12 @@ static int cfg80211_set_key_mgt(struct wireless_dev *wdev, u32 key_mgt) nr_akm_suites++; } + if (key_mgt & IW_AUTH_KEY_MGMT_WAPI_PSK) { + wdev->wext.connect.crypto.akm_suites[nr_akm_suites] = + WLAN_AKM_SUITE_WAPI_PSK; + nr_akm_suites++; + } + wdev->wext.connect.crypto.n_akm_suites = nr_akm_suites; return 0; -- cgit v1.2.3 From 3a9bb5126c26fe5a7a27f132233543e386cec3cb 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 c0b09cfe017392e0697e205c4b6726999479064b 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 06c9081d596..72263563aa2 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -150,4 +150,6 @@ source "drivers/staging/mei/Kconfig" source "drivers/staging/nvec/Kconfig" +source "drivers/staging/cw1200/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index f3c5e33bb26..44d819dba23 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -66,3 +66,4 @@ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/ obj-$(CONFIG_DRM_PSB) += gma500/ obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_MFD_NVEC) += nvec/ +obj-$(CONFIG_CW1200) += cw1200/ -- cgit v1.2.3