From 32ec78901ddef7cd53342b2dbd96bc0b25ba7bd0 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 21 Oct 2011 10:35:01 +0200 Subject: WLAN: Initial commit of the STM CW1200 WLAN driver. LL IO API is defined and implemented. TX queue is designed and implemented. WSM API subset is defined. mac80211 API is implemented partly. FW downloading is working. BH is _not_ implemented. RX BH is implemented. 1. RX WSM API is defined and stubs are created. 2. RX BH is implemented (not verifiyed yet). Reference WSM API implementation. wsm_configuration / wsm_configuration_confirm. Not veryfied yet. BH and WSM TX, basic WSM notification. Device startup is finished. TODO at mac80211 layer: - start - add_interface - config - config_tx - config_filter - hw_scan - remove_interface - stop TODO in general: - proper (random?) MAC address TODO in WSM: - datapath Basic mac80211 STA interface is implemented (not verified yet, but should be working). Random MAC address is implemented. Defined and implemented: - cw1200_start - cw1200_stop - cw1200_add_interface - cw1200_remove_interface - cw1200_config - cw1200_conf_tx FW code is moved to fwio.[hc] STA code is moved to sta.[hc] AP code is moved to ap.[hc] TODO: - configure_filter() - hw_scan() - datapath Some bugfix. Piggyback RX-ing. Bugbix: wrong buf_id_rx calculation. Finaly! RX datapath is implemented. TX datapath is implemented. HW scan is implemented. TODO: - hw_scan: little refactoring. I hate gotos. - Join - Unjoin - SKB pool - BH monitor / chip restarter Development. + Proper handling of scan IEs Join. TX/RX should be working now. Development. + Proper handling of scan IEs + Ping is working!!! (with some hacks) Development. + TX rate policy cache is implemented. + TX rate policy table uploading is implemented. + TX queues stop/resume. + WSM STA API (except MIBs) is 100% finished. + Direct TX-ing of probe requests is converted to start-scan WSM cmd (workaround against FW "feature"). + All work moved to an own workqueue. + Scan timeout is implemented (workaround against a known bug in FW). + set_key is implemented. TODO: - Full WSM API support. - Join refactoring - Assoc status updates. Verification and bugfixing. + Direct probe is verified. Some bugs fixed. + TX timeout is implemented... and rolled back: no way to cancel TX. + TX hang is fixed: FW hangs if tx->more is set but no more data available. + TX rate table uploading is moved into a separate workitem: can't schedule in datapath. + TX status reporting is enabled. + Removed hacks from Join: should be able to connect to any AP now. + TX rate cache bugfix: incorrect handling of policies like 100000000008900000000000. + TX rate cache optimization: upload only changed policies. + Unjoin is implemented for TX and RX. + Join is triggered for auth. frames only. + Join timeout is implemented. + wsm_set_bss_params is called from assoc notification. + wsm_set_association_mode is called from assoc notification. + set_beacon_wakeup_period is called from assoc notification. + HT information is now available in priv->ht_cap, priv->association_mode + CTS protection is set. + RTS threshold is set and verified. + Warning cleanup. + TODOs for compat-wireless-2010-07-16 API change. + Very basic RSSI subscription. However reporting is NIY. + Some bugfix for minestrel. F.e. policy like 10:9 11:9 0:1 0:1 -1:0 should be handled ok. + MaxTxCount (short/long) is implemented and verified. + Channel switch is protected now. + OverrideInternalTxRate is implemented.. and rolled back: internal rate controlselection is better. TODO: - Security is not validated yet - Set HT parameters of TX data according to priv->ht_cap, priv->association_mode. - Strange: set_beacon_wakeup_period is called with DTIM 1 always. Why??? - RSSI subscription events. - How to do unjoin? Style changes. Modifications to make the code compatible with checkpath. Not finished yet. 1. // -> /* */ 2. whitespaces 3. formatting 4. warning- & error fix No functional changes in the commit. Verification. + RSSI reporting is working. + WEP is working. Dynamic WEP key switching is implemented to select TX WEP key on-fly + WPA/CCMP and WPA2/CCMP are working. + WPA/TKIP and WPA2/TKIP are working. + MIC failure event is implemented. + Unjoin is implemented as a reset. Why not? + wsm_flush_tx() added to flush TX port after wsm_lock_tx_async(). + race condition in wsm_lock_tx()/wsm_flush_tx() is fixed. + RSSI subscription is under research. + Event reporting is implemented. + Beacon loss event is prepared (but not sent) + Link loss is implemented. TODO: On target.. + Bugfix: DMA from non-DMAble memory in FW downloading. + RSSI subscription is finished. + RSSI event reporting is implemented. + Beacon loss event is reimplemented with respect to scan. + Memory leak in WSM event processing is fixed. + Code is verified on target. Set CW1200_U8500_PLATFORM to build for target. + Scan turns on powersave to prevent AP from TXing. + Spinning of direct_probe work is fixed. + Workaround is implemented for "FIXME: we can't use 'more' at all: we don't know future." TODO: Verification on device. + Correct deinitialization of async work. + New WSM API. + WMM is verified and fixed (queue prioritization). + Bug in TX is fixed (freezing of TCP traffic). + STE CQM extensions are implemented. + HW revision detection is implemented. + A crash/hangs in scan is fixed. + 802.11 slot time is set + 11n: first steps: Mixed mode is working. + 11n: Block ACK config. + Keep-alive work is implemented (but it is not doing anything yet). TODO: - Copyright statements and COPYING - STE API for power-save - Need to deinit bt_thread, isn't it? Copyright statements and bt_thread deinitialization. + Copyright statements + bt_thread deinitialization (NOT TESTED ON DEVICE AT ALL!!!) Cumulative TODO: - COPYING - STE API for power-save - Set HT parameters of TX data. 11n verification. - SKB pool - reuse previously allocated SKBs - BH monitor / chip restarter - IBSS - AP mode - U-APSD configuration - Memory leak verification, coverity, checkpatch = It looks like that's it. Mainline to compat-wireless-20010-12-03 No functional changes in this commit. + Changes in compat-wireless API. + Changes in kerner API. + Changes in kernel header structures. TODO: - New code in cw1200_bss_info_changed() needs to be verified. - ht_operation_mode needs to be cached in priv. Some HT refactoring. + ht_operation_mode is cached in priv + channel_type is cached in priv + New header for HT-related code: ht.h + Greenfield mode is used for TX if possible. + HT flags are reported back to rate control correctly. TODO: - Verify A-MPDU factor settings in cw1200_band_2ghz. SKB cache is implemented. Firmware loading refactoring. + CUT 2.0 detection is implemented. + Standard firmware loading API is used. + Firmware loading path is changed to /lib/firmware/cw1200/ + Firmware is put to the project: ./firmware/ TODO: - Firmware copying at build time. Bringup after mainlining. + FW downloading is working now. TODO: - BH thread can't read data block from device. Init: removing GPIO toggling. Not needed anymore. WLAN: Fix for missing GPIO_HIGH, GPIO_LOW The patch defines CW1200_GPIO_{HIGH|LOW} as replacement of GPIO_{HIGH|LOW}, which might be absent on the target platform. Also it allows build without compat wireless tree. Added cw1200 to drivers/staging Kconfig and Makefile. Added two CONFIG_ flags: + CONFIG_CW1200 - Enables the generic cw1200 module + CONFIG_CW1200_U8500_PLATFORM - U8500 platform integration Removed binary firmware files, they do not belong in the kernel tree. Added TODO file for cw1200 staging driver. Added more help text to Kconfig Removed checkpatch warnings. There is still one left. Removed all checkpatch warnings for cw1200.h Removed checkpatch warnings from cw1200_stdio.c. There is still one left. Removed checkpatch warnings for fwio.h Removed checkpatch warning for hwio.c Removed checkpatch warnings for main.c There are still some warninges left. Removed checkpatch warnings from queue.c There are still some warnings left Removed checkpatch warnings for queue.h Removed checkpatch warnings for sbus.h Removed checkpatch warnings for scan.c Removed checkpatch warnings for scan.h Removed checkpatch warnings for sta.c Removed checkpatch warnings for txrc.c Removed checkpatch warnings for txrx.h Removed checkpatch warnings from wsm.c Lots of "line over 80 char..." warnings left. Minor checkpatch warning cleanup on wsm.c WLAN: CW1200: Development: SoftAP mode WLAN: CW1200: AP development. + Basic open AP is woking (prototyping done). TODO: - Check AP scan & assoc with sniffer - WSM API: TxPowerLevel in scan requests. - WSM API: Frame in TX and RX indication. - WSM API: Flags in set_bss_params. - WSM API: IGTKGroupKey in key info. - WSM API: MultiTX confirm - WSM API: Config block ACK - WSM API: BA timeout indication. CW1200: WLAN: Fix for a merge error. This commit fixes build error introduced by WLAN: CW1200: AP development. WLAN: CW1200: Fix for a warning in net/mac80211/rx.c WLAN: CW1200: Add / remove key refactoring for AP mode. Keys are cached in the driver and loaded after wsm_start() is called. Verified (AP mode): + Open security: OK + WEP40: OK + WEP104: FAIL (WSM_STATUS_DECRYPTFAILURE) + CCMP: FAIL (MIC failure detected by STA. STA->AP decrypted OK) + TKIP: FAIL (MIC failure detected by STA. STA->AP decrypted OK) WLAN: CW1200: Proper handling of stop request in the AP mode. WLAN: CW1200: Firmware-driven keep-alive is implemented. CW1200: SDIO card detection is implemented. Removed dependency to u8500_sdio_detect_card(). New moule parameter "device" is implemented to point to the SDIO interface device is connected to. WLAN: CW1200: Checkpatch cleanup. + wsm.c is cleared. + sta.c is cleared. + ap.c is cleared. + bh.c is cleared. + cw1200_sdio.c is cleared. + main.c is cleared. + queue.c is cleared. + scan.c is cleared. + txrx.c is cleared. + cw1200.h is cleared. + hwio.h is cleared. + ht.h is cleared. + wsm_get_tx is (finally!) redesigned. TODO: - wsm.h WLAN: CW1200: Checkpatch cleanup. + wsm.h is cleared. Code is ready for submission. WLAN: CW1200: Module parameter for MAC address. New module parameter for cw1200_core.ko: "macaddr". Usage: modprobe cw1200_core macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 Random MAC address by default. WLAN: CW1200: Fix for a crash in skb_release_data(). WLAN: CW1200: Fix for a too short WSM timeout in stressed condition. WLAN: CW1200: Bugfix after regression testing. Datapath is verified (several gigabytes of downloaded data). WLAN: STA mode development. + AP API sta_add/sta_semove is implemented. + wsm_suspend_resume_indication is implemented. + Unique link ID is allocated for STAs in AP mode as WSM requires. + wsm_map_link is implemented. + wsm_reset is modified to support multiple link IDs. + cw1200_suspend_resume handler is implemented in the ap.c. + no more hardcoded SSIDs + Debug logs functionality is exposed to the kernel config. + Suspend / resume is supported by queue. + TKIP multicast TX (AP mode) is fixed. TODO: - AP mode with powersave is not verified. WLAN: Support for non-power-of-two SDIO transfer. WLAN: AP mode verification and bugfixing. + Fix for broken broadcasts. + Redesign of PS implementation in AP mode. + set_tim() is implemented. + sta_notify() is implemented. WLAN: IV/ICV injection fixed. Fixed: skb was modified prior to sanity check. There is also a workaround against a bug in WSM_A21.05.0288 firmware in this commit. In AP mode FW calculates FCS incorrectly when DA is FF:FF:FF:FF:FF:FF (was seen with TKIP and CCMP security). Workaround checks DA and replaces FF:FF:FF:FF:FF:FF with multicast address 01:00:5E:00:00:16. Workaround is disabled by #if 0 (can be used only for verification, should not be enabled in production). WLAN: Disabling debug printouts (STA/AP). WLAN: Updating u8500_defconfig with CW1200 driver. WLAN: AP mode fine-tuning. + Explicitly set broadcast bit in TIM when broadcasts are available. + Split handling of management frames (link ID = 0) and buffered multicasts (link ID = CW1200_LINK_ID_AFTER_DTIM) Signed-off-by: Dmitry Tarnyagin --- drivers/staging/cw1200/Makefile | 45 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 drivers/staging/cw1200/Makefile (limited to 'drivers/staging/cw1200/Makefile') 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 -- cgit v1.2.3 From 79cda77dc61307241153a37d07430e74138121dc Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Tue, 3 May 2011 22:25:16 +0200 Subject: WLAN: CW1200: Support for HREF/HREFV60/Snowball. 1. Added support for HREF/HREFV60/Snowball platforms. 2. Configuration cleanup. Signed-off-by: Dmitry Tarnyagin --- drivers/staging/cw1200/Kconfig | 13 ------- drivers/staging/cw1200/Makefile | 40 --------------------- drivers/staging/cw1200/ap.c | 14 ++++---- drivers/staging/cw1200/bh.c | 22 ++++++------ drivers/staging/cw1200/cw1200.h | 20 ++--------- drivers/staging/cw1200/cw1200_sdio.c | 69 +++++++++++++++++++++++------------- drivers/staging/cw1200/fwio.c | 2 -- drivers/staging/cw1200/hwio.h | 2 -- drivers/staging/cw1200/main.c | 8 ++--- drivers/staging/cw1200/sta.c | 36 +++++++++---------- drivers/staging/cw1200/txrx.c | 6 ++-- drivers/staging/cw1200/wsm.c | 2 +- 12 files changed, 92 insertions(+), 142 deletions(-) (limited to 'drivers/staging/cw1200/Makefile') 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 9872c88ff6979c5b1454bf2bcd96c5ec7a4ff76d Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Thu, 26 May 2011 17:49:37 +0200 Subject: cw1200: Expose driver internals on DebugFS. Change-Id: I8dc24f029e78c9c2026dbe03f4a7111746316d70 Signed-off-by: Dmitry Tarnyagin Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/25613 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/Kconfig | 3 + drivers/staging/cw1200/Makefile | 21 ++- drivers/staging/cw1200/cw1200.h | 4 +- drivers/staging/cw1200/debug.c | 307 ++++++++++++++++++++++++++++++++++++++++ drivers/staging/cw1200/debug.h | 24 ++++ drivers/staging/cw1200/main.c | 7 +- 6 files changed, 360 insertions(+), 6 deletions(-) create mode 100644 drivers/staging/cw1200/debug.c create mode 100644 drivers/staging/cw1200/debug.h (limited to 'drivers/staging/cw1200/Makefile') 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 c419f3df8e70242bd7dd45eeb6d4a62de95601ec Mon Sep 17 00:00:00 2001 From: Dmitry Tarnyagin Date: Wed, 13 Jul 2011 23:03:17 +0200 Subject: cw1200: Wakeup on wireless (WoW) is implemented. + Core and SDIO power management is implemented as defined in wireless-next v3.0-rc4. + SDD is cached to avoid disk access at suspend/resume time. TODO: - WoW conditions and filtering are not yet implemented. - BUG: Late interrupts (coming after suspend() callback but before the actual suspend) are not detected as wakeup sources. Universal (Android/GLK) way of waking up halfly-suspended system to be found. Signed-off-by: Dmitry Tarnyagin Change-Id: Ib4931a261e592f2927455e988055cd673250ec81 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27297 Tested-by: Bartosz MARKOWSKI Reviewed-by: Bartosz MARKOWSKI Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33484 Reviewed-by: Philippe LANGLAIS --- drivers/staging/cw1200/Makefile | 3 +- drivers/staging/cw1200/bh.c | 70 +++++++++++++++-- drivers/staging/cw1200/bh.h | 2 + drivers/staging/cw1200/cw1200.h | 17 +++-- drivers/staging/cw1200/cw1200_plat.h | 5 ++ drivers/staging/cw1200/cw1200_sdio.c | 45 +++++++---- drivers/staging/cw1200/main.c | 25 ++++-- drivers/staging/cw1200/pm.c | 142 +++++++++++++++++++++++++++++++++++ drivers/staging/cw1200/pm.h | 23 ++++++ drivers/staging/cw1200/sbus.h | 1 + drivers/staging/cw1200/sta.c | 58 +++++++------- drivers/staging/cw1200/sta.h | 1 + drivers/staging/cw1200/wsm.c | 4 +- 13 files changed, 329 insertions(+), 67 deletions(-) create mode 100644 drivers/staging/cw1200/pm.c create mode 100644 drivers/staging/cw1200/pm.h (limited to 'drivers/staging/cw1200/Makefile') 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 e9ae53167bf730792813618295e2f5654ee0d91c Mon Sep 17 00:00:00 2001 From: Lukasz Kucharczyk Date: Wed, 29 Feb 2012 15:15:31 +0100 Subject: cw1200: ITP Commands: 1,,: Start RX test 2: Show RX statistics 3: Stop RX/TX test 4,,,,, ,,, ,: Start TX test. 5: Get chip ID. 6: Get firmware ID. Return codes: 0[,...] command executed succesfully 1,,,, ,: response to commands 2, 3. 2,: response to commands 5, 6 -,: Error ST-Ericsson ID: 359166 Signed-off-by: Dmitry Tarnyagin Signed-off-by: Lukasz Kucharczyk Change-Id: I97b58b6e662f6f28fdd24241bb6b1dc431189b20 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/47731 Reviewed-by: QABUILD Reviewed-by: Bartosz MARKOWSKI Tested-by: Bartosz MARKOWSKI --- drivers/staging/cw1200/Kconfig | 14 + drivers/staging/cw1200/Makefile | 1 + drivers/staging/cw1200/debug.c | 8 +- drivers/staging/cw1200/debug.h | 11 + drivers/staging/cw1200/itp.c | 739 ++++++++++++++++++++++++++++++++++++++++ drivers/staging/cw1200/itp.h | 151 ++++++++ drivers/staging/cw1200/txrx.c | 10 +- drivers/staging/cw1200/wsm.c | 5 + 8 files changed, 936 insertions(+), 3 deletions(-) create mode 100644 drivers/staging/cw1200/itp.c create mode 100644 drivers/staging/cw1200/itp.h (limited to 'drivers/staging/cw1200/Makefile') diff --git a/drivers/staging/cw1200/Kconfig b/drivers/staging/cw1200/Kconfig index 3a4fc9813c6..044f7e6585a 100644 --- a/drivers/staging/cw1200/Kconfig +++ b/drivers/staging/cw1200/Kconfig @@ -42,6 +42,13 @@ config CW1200_WAPI_SUPPORT Say Y if your compat-wireless support WAPI. If unsure, say N. +config CW1200_USE_STE_EXTENSIONS + bool "STE extensions" + depends on CW1200 + help + Say Y if you want to include STE extensions. + If unsure, say N. + config CW1200_DISABLE_BEACON_HINTS bool "Disable 11d beacon hints" depends on CW1200 @@ -82,4 +89,11 @@ config CW1200_STA_DEBUG endmenu +config CW1200_ITP + bool "Enable ITP DebugFS" + depends on CW1200 + help + Say Y if you want to include ITP code. + If unsure, say N. + endif diff --git a/drivers/staging/cw1200/Makefile b/drivers/staging/cw1200/Makefile index c0e88bd0f11..67d7867c1b5 100644 --- a/drivers/staging/cw1200/Makefile +++ b/drivers/staging/cw1200/Makefile @@ -10,6 +10,7 @@ cw1200_core-y := \ ap.o \ scan.o cw1200_core-$(CONFIG_CW1200_DEBUGFS) += debug.o +cw1200_core-$(CONFIG_CW1200_ITP) += itp.o cw1200_core-$(CONFIG_PM) += pm.o cw1200_wlan-y := cw1200_sdio.o diff --git a/drivers/staging/cw1200/debug.c b/drivers/staging/cw1200/debug.c index 43b2c257c57..3a8b79f7556 100644 --- a/drivers/staging/cw1200/debug.c +++ b/drivers/staging/cw1200/debug.c @@ -470,11 +470,12 @@ static const struct file_operations fops_hang = { int cw1200_debug_init(struct cw1200_common *priv) { + int ret = -ENOMEM; struct cw1200_debug_priv *d = kzalloc(sizeof(struct cw1200_debug_priv), GFP_KERNEL); priv->debug = d; if (!d) - return -ENOMEM; + return ret; d->debugfs_phy = debugfs_create_dir("cw1200", priv->hw->wiphy->debugfsdir); @@ -511,6 +512,11 @@ err: void cw1200_debug_release(struct cw1200_common *priv) { struct cw1200_debug_priv *d = priv->debug; + if (d) { + cw1200_itp_release(priv); + priv->debug = NULL; + kfree(d); + } priv->debug = NULL; if (d) { diff --git a/drivers/staging/cw1200/debug.h b/drivers/staging/cw1200/debug.h index c901a6a64e9..72b827f296b 100644 --- a/drivers/staging/cw1200/debug.h +++ b/drivers/staging/cw1200/debug.h @@ -12,6 +12,8 @@ #ifndef CW1200_DEBUG_H_INCLUDED #define CW1200_DEBUG_H_INCLUDED +#include "itp.h" + struct cw200_common; #ifdef CONFIG_CW1200_DEBUGFS @@ -31,6 +33,9 @@ struct cw1200_debug_priv { int rx_burst; int ba_cnt; int ba_acc; +#ifdef CONFIG_CW1200_ITP + struct cw1200_itp itp; +#endif /* CONFIG_CW1200_ITP */ }; int cw1200_debug_init(struct cw1200_common *priv); @@ -95,6 +100,8 @@ static inline void cw1200_debug_ba(struct cw1200_common *priv, priv->debug->ba_acc = ba_acc; } +int cw1200_print_fw_version(struct cw1200_common *priv, u8 *buf, size_t len); + #else /* CONFIG_CW1200_DEBUGFS */ static inline int cw1200_debug_init(struct cw1200_common *priv) @@ -152,6 +159,10 @@ static inline void cw1200_debug_ba(struct cw1200_common *priv, { } +int cw1200_print_fw_version(struct cw1200_common *priv, u8 *buf, size_t len) +{ +} + #endif /* CONFIG_CW1200_DEBUGFS */ #endif /* CW1200_DEBUG_H_INCLUDED */ diff --git a/drivers/staging/cw1200/itp.c b/drivers/staging/cw1200/itp.c new file mode 100644 index 00000000000..eb7e53bf096 --- /dev/null +++ b/drivers/staging/cw1200/itp.c @@ -0,0 +1,739 @@ +/* + * mac80211 glue code for mac80211 ST-Ericsson CW1200 drivers + * ITP code + * + * Copyright (c) 2010, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include "cw1200.h" +#include "debug.h" +#include "itp.h" +#include "sta.h" + +static int __cw1200_itp_open(struct cw1200_common *priv); +static int __cw1200_itp_close(struct cw1200_common *priv); +static void cw1200_itp_rx_start(struct cw1200_common *priv); +static void cw1200_itp_rx_stop(struct cw1200_common *priv); +static void cw1200_itp_rx_stats(struct cw1200_common *priv); +static void cw1200_itp_rx_reset(struct cw1200_common *priv); +static void cw1200_itp_tx_stop(struct cw1200_common *priv); +static void cw1200_itp_handle(struct cw1200_common *priv, + struct sk_buff *skb); +static void cw1200_itp_err(struct cw1200_common *priv, + int err, + int arg); +static void __cw1200_itp_tx_stop(struct cw1200_common *priv); + +static ssize_t cw1200_itp_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct cw1200_common *priv = file->private_data; + struct cw1200_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + int ret; + + if (skb_queue_empty(&itp->log_queue)) + return 0; + + skb = skb_dequeue(&itp->log_queue); + ret = copy_to_user(user_buf, skb->data, skb->len); + *ppos += skb->len; + skb->data[skb->len] = 0; + itp_printk(KERN_DEBUG "[ITP] >>> %s", skb->data); + consume_skb(skb); + + return skb->len - ret; +} + +static ssize_t cw1200_itp_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct cw1200_common *priv = file->private_data; + struct sk_buff *skb; + + if (!count || count > 1024) + return -EINVAL; + skb = dev_alloc_skb(count + 1); + if (!skb) + return -ENOMEM; + skb_trim(skb, 0); + skb_put(skb, count + 1); + if (copy_from_user(skb->data, user_buf, count)) { + kfree_skb(skb); + return -EFAULT; + } + skb->data[count] = 0; + + cw1200_itp_handle(priv, skb); + consume_skb(skb); + return count; +} + +static unsigned int cw1200_itp_poll(struct file *file, poll_table *wait) +{ + struct cw1200_common *priv = file->private_data; + struct cw1200_itp *itp = &priv->debug->itp; + unsigned int mask = 0; + + poll_wait(file, &itp->read_wait, wait); + + if (!skb_queue_empty(&itp->log_queue)) + mask |= POLLIN | POLLRDNORM; + + mask |= POLLOUT | POLLWRNORM; + + return mask; +} + +static int cw1200_itp_open(struct inode *inode, struct file *file) +{ + struct cw1200_common *priv = inode->i_private; + struct cw1200_itp *itp = &priv->debug->itp; + int ret = 0; + + file->private_data = priv; + if (atomic_inc_return(&itp->open_count) == 1) { + ret = __cw1200_itp_open(priv); + if (ret && !atomic_dec_return(&itp->open_count)) + __cw1200_itp_close(priv); + } else { + atomic_dec(&itp->open_count); + ret = -EBUSY; + } + + return ret; +} + +static int cw1200_itp_close(struct inode *inode, struct file *file) +{ + struct cw1200_common *priv = file->private_data; + struct cw1200_itp *itp = &priv->debug->itp; + if (!atomic_dec_return(&itp->open_count)) { + __cw1200_itp_close(priv); + wake_up(&itp->close_wait); + } + return 0; +} + +static const struct file_operations fops_itp = { + .open = cw1200_itp_open, + .read = cw1200_itp_read, + .write = cw1200_itp_write, + .poll = cw1200_itp_poll, + .release = cw1200_itp_close, + .llseek = default_llseek, + .owner = THIS_MODULE, +}; + +static void cw1200_itp_fill_pattern(u8 *data, int size, + enum cw1200_itp_data_modes mode) +{ + u8 *p = data; + + if (size <= 0) + return; + + switch (mode) { + default: + case ITP_DATA_ZEROS: + memset(data, 0x0, size); + break; + case ITP_DATA_ONES: + memset(data, 0xff, size); + break; + case ITP_DATA_ZERONES: + memset(data, 0x55, size); + break; + case ITP_DATA_RANDOM: + while (p < data+size-sizeof(u32)) { + (*(u32 *)p) = random32(); + p += sizeof(u32); + } + while (p < data+size) { + (*p) = random32() & 0xFF; + p++; + } + break; + } + return; +} + +static void cw1200_itp_tx_work(struct work_struct *work) +{ + struct cw1200_itp *itp = container_of(work, struct cw1200_itp, + tx_work.work); + struct cw1200_common *priv = itp->priv; + atomic_set(&priv->bh_tx, 1); + wake_up(&priv->bh_wq); +} + +static void cw1200_itp_tx_finish(struct work_struct *work) +{ + struct cw1200_itp *itp = container_of(work, struct cw1200_itp, + tx_finish.work); + __cw1200_itp_tx_stop(itp->priv); +} + +int cw1200_itp_init(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + + itp->priv = priv; + atomic_set(&itp->open_count, 0); + atomic_set(&itp->stop_tx, 0); + atomic_set(&itp->awaiting_confirm, 0); + skb_queue_head_init(&itp->log_queue); + spin_lock_init(&itp->tx_lock); + init_waitqueue_head(&itp->read_wait); + init_waitqueue_head(&itp->write_wait); + init_waitqueue_head(&itp->close_wait); + INIT_DELAYED_WORK(&itp->tx_work, cw1200_itp_tx_work); + INIT_DELAYED_WORK(&itp->tx_finish, cw1200_itp_tx_finish); + itp->data = NULL; + itp->hdr_len = WSM_TX_EXTRA_HEADROOM + + sizeof(struct ieee80211_hdr_3addr); + + if (!debugfs_create_file("itp", S_IRUSR | S_IWUSR, + priv->debug->debugfs_phy, priv, &fops_itp)) + return -ENOMEM; + + return 0; +} + +void cw1200_itp_release(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + + wait_event_interruptible(itp->close_wait, + !atomic_read(&itp->open_count)); + + WARN_ON(atomic_read(&itp->open_count)); + + skb_queue_purge(&itp->log_queue); + cw1200_itp_tx_stop(priv); +} + +static int __cw1200_itp_open(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + + if (!priv->vif) + return -EINVAL; + if (priv->join_status) + return -EINVAL; + itp->saved_channel = priv->channel; + if (!priv->channel) + priv->channel = &priv->hw-> + wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; + wsm_set_bssid_filtering(priv, false); + cw1200_itp_rx_reset(priv); + return 0; +} + +static int __cw1200_itp_close(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) + cw1200_itp_rx_stop(priv); + cw1200_itp_tx_stop(priv); + cw1200_disable_listening(priv); + cw1200_update_filtering(priv); + priv->channel = itp->saved_channel; + return 0; +} + +bool cw1200_is_itp(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + return atomic_read(&itp->open_count) != 0; +} + +static void cw1200_itp_rx_reset(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + itp->rx_cnt = 0; + itp->rx_rssi = 0; + itp->rx_rssi_max = -1000; + itp->rx_rssi_min = 1000; +} + +static void cw1200_itp_rx_start(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + + itp_printk(KERN_DEBUG "[ITP] RX start, band = %d, ch = %d\n", + itp->band, itp->ch); + atomic_set(&itp->test_mode, TEST_MODE_RX_TEST); + cw1200_update_listening(priv, false); + priv->channel = &priv->hw-> + wiphy->bands[itp->band]->channels[itp->ch]; + cw1200_update_listening(priv, true); + wsm_set_bssid_filtering(priv, false); +} + +static void cw1200_itp_rx_stop(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + itp_printk(KERN_DEBUG "[ITP] RX stop\n"); + atomic_set(&itp->test_mode, TEST_MODE_NO_TEST); + cw1200_itp_rx_reset(priv); +} + +static void cw1200_itp_rx_stats(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + char buf[128]; + int len, ret; + struct wsm_counters_table counters; + + ret = wsm_get_counters_table(priv, &counters); + + if (ret) + cw1200_itp_err(priv, -EBUSY, 20); + + if (!itp->rx_cnt) + len = snprintf(buf, sizeof(buf), "1,0,0,0,0,%d\n", + counters.countRxPacketErrors); + else + len = snprintf(buf, sizeof(buf), "1,%d,%ld,%d,%d,%d\n", + itp->rx_cnt, + itp->rx_cnt ? itp->rx_rssi / itp->rx_cnt : 0, + itp->rx_rssi_min, itp->rx_rssi_max, + counters.countRxPacketErrors); + + if (len <= 0) { + cw1200_itp_err(priv, -EBUSY, 21); + return; + } + + skb = dev_alloc_skb(len); + if (!skb) { + cw1200_itp_err(priv, -ENOMEM, 22); + return; + } + + itp->rx_cnt = 0; + itp->rx_rssi = 0; + itp->rx_rssi_max = -1000; + itp->rx_rssi_min = 1000; + + skb_trim(skb, 0); + skb_put(skb, len); + + memcpy(skb->data, buf, len); + skb_queue_tail(&itp->log_queue, skb); + wake_up(&itp->read_wait); +} + +static void cw1200_itp_tx_start(struct cw1200_common *priv) +{ + struct wsm_tx *tx; + struct ieee80211_hdr_3addr *hdr; + struct cw1200_itp *itp = &priv->debug->itp; + struct wsm_association_mode assoc_mode = { + .flags = WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE, + .preambleType = itp->preamble, + }; + int len; + u8 da_addr[6] = ITP_DEFAULT_DA_ADDR; + + /* Rates index 4 and 5 are not supported */ + if (itp->rate > 3) + itp->rate += 2; + + itp_printk(KERN_DEBUG "[ITP] TX start: band = %d, ch = %d, rate = %d," + " preamble = %d, number = %d, data_mode = %d," + " interval = %d, power = %d, data_len = %d\n", + itp->band, itp->ch, itp->rate, itp->preamble, + itp->number, itp->data_mode, itp->interval_us, + itp->power, itp->data_len); + + len = itp->hdr_len + itp->data_len; + + itp->data = kmalloc(len, GFP_KERNEL); + tx = (struct wsm_tx *)itp->data; + tx->hdr.len = itp->data_len + itp->hdr_len; + tx->hdr.id = __cpu_to_le16(0x0004 | 1 << 6); + tx->maxTxRate = itp->rate; + tx->queueId = 3; + tx->more = 0; + tx->flags = 0xc; + tx->packetID = 0x55ff55; + tx->reserved = 0; + tx->expireTime = 1; + + if (itp->preamble == ITP_PREAMBLE_GREENFIELD) + tx->htTxParameters = WSM_HT_TX_GREENFIELD; + else if (itp->preamble == ITP_PREAMBLE_MIXED) + tx->htTxParameters = WSM_HT_TX_MIXED; + + hdr = (struct ieee80211_hdr_3addr *)&itp->data[sizeof(struct wsm_tx)]; + memset(hdr, 0, sizeof(*hdr)); + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_FCTL_TODS); + memcpy(hdr->addr1, da_addr, ETH_ALEN); + memcpy(hdr->addr2, priv->vif->addr, ETH_ALEN); + memcpy(hdr->addr3, da_addr, ETH_ALEN); + + cw1200_itp_fill_pattern(&itp->data[itp->hdr_len], + itp->data_len, itp->data_mode); + + cw1200_update_listening(priv, false); + priv->channel = &priv->hw-> + wiphy->bands[itp->band]->channels[itp->ch]; + WARN_ON(wsm_set_output_power(priv, itp->power)); + if (itp->preamble == ITP_PREAMBLE_SHORT || + itp->preamble == ITP_PREAMBLE_LONG) + WARN_ON(wsm_set_association_mode(priv, + &assoc_mode)); + wsm_set_bssid_filtering(priv, false); + cw1200_update_listening(priv, true); + + spin_lock_bh(&itp->tx_lock); + atomic_set(&itp->test_mode, TEST_MODE_TX_TEST); + atomic_set(&itp->awaiting_confirm, 0); + atomic_set(&itp->stop_tx, 0); + atomic_set(&priv->bh_tx, 1); + ktime_get_ts(&itp->last_sent); + wake_up(&priv->bh_wq); + spin_unlock_bh(&itp->tx_lock); +} + +void __cw1200_itp_tx_stop(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + spin_lock_bh(&itp->tx_lock); + kfree(itp->data); + itp->data = NULL; + atomic_set(&itp->test_mode, TEST_MODE_NO_TEST); + spin_unlock_bh(&itp->tx_lock); +} + +static void cw1200_itp_tx_stop(struct cw1200_common *priv) +{ + struct cw1200_itp *itp = &priv->debug->itp; + itp_printk(KERN_DEBUG "[ITP] TX stop\n"); + atomic_set(&itp->stop_tx, 1); + flush_workqueue(priv->workqueue); + + /* time for FW to confirm all tx requests */ + msleep(500); + + __cw1200_itp_tx_stop(priv); +} + +static void cw1200_itp_get_version(struct cw1200_common *priv, + enum cw1200_itp_version_type type) +{ + struct cw1200_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + char buf[ITP_BUF_SIZE]; + size_t size = 0; + int len; + itp_printk(KERN_DEBUG "[ITP] print %s version\n", type == ITP_CHIP_ID ? + "chip" : "firmware"); + + len = snprintf(buf, ITP_BUF_SIZE, "2,"); + if (len <= 0) { + cw1200_itp_err(priv, -EINVAL, 40); + return; + } + size += len; + + switch (type) { + case ITP_CHIP_ID: + len = cw1200_print_fw_version(priv, buf+size, + ITP_BUF_SIZE - size); + + if (len <= 0) { + cw1200_itp_err(priv, -EINVAL, 41); + return; + } + size += len; + break; + case ITP_FW_VER: + len = snprintf(buf+size, ITP_BUF_SIZE - size, + "%d.%d", priv->wsm_caps.hardwareId, + priv->wsm_caps.hardwareSubId); + if (len <= 0) { + cw1200_itp_err(priv, -EINVAL, 42); + return; + } + size += len; + break; + default: + cw1200_itp_err(priv, -EINVAL, 43); + break; + } + + len = snprintf(buf+size, ITP_BUF_SIZE-size, "\n"); + if (len <= 0) { + cw1200_itp_err(priv, -EINVAL, 44); + return; + } + size += len; + + skb = dev_alloc_skb(size); + if (!skb) { + cw1200_itp_err(priv, -ENOMEM, 45); + return; + } + + skb_trim(skb, 0); + skb_put(skb, size); + + memcpy(skb->data, buf, size); + skb_queue_tail(&itp->log_queue, skb); + wake_up(&itp->read_wait); +} + +int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data, + size_t *tx_len, int *burst) +{ + struct cw1200_itp *itp; + struct timespec now; + int time_left_us; + + if (!priv->debug) + return 0; + + itp = &priv->debug->itp; + + if (!itp) + return 0; + + spin_lock_bh(&itp->tx_lock); + if (atomic_read(&itp->test_mode) != TEST_MODE_TX_TEST) + goto out; + + if (atomic_read(&itp->stop_tx)) + goto out; + + if (itp->number == 0) { + atomic_set(&itp->stop_tx, 1); + queue_delayed_work(priv->workqueue, &itp->tx_finish, + HZ/10); + goto out; + } + + if (!itp->data) + goto out; + + if (priv->hw_bufs_used >= 2) { + if (!atomic_read(&priv->bh_rx)) + atomic_set(&priv->bh_rx, 1); + atomic_set(&priv->bh_tx, 1); + goto out; + } + + ktime_get_ts(&now); + time_left_us = (itp->last_sent.tv_sec - + now.tv_sec)*1000000 + + (itp->last_sent.tv_nsec - now.tv_nsec)/1000 + + itp->interval_us; + + if (time_left_us > ITP_TIME_THRES_US) { + queue_delayed_work(priv->workqueue, &itp->tx_work, + ITP_US_TO_MS(time_left_us)*HZ/1000); + goto out; + } + + if (time_left_us > 50) + udelay(time_left_us); + + if (itp->number > 0) + itp->number--; + + *data = itp->data; + *tx_len = itp->data_len + itp->hdr_len; + + if (itp->data_mode == ITP_DATA_RANDOM) + cw1200_itp_fill_pattern(&itp->data[itp->hdr_len], + itp->data_len, itp->data_mode); + *burst = 2; + atomic_set(&priv->bh_tx, 1); + ktime_get_ts(&itp->last_sent); + atomic_add(1, &itp->awaiting_confirm); + spin_unlock_bh(&itp->tx_lock); + return 1; + +out: + spin_unlock_bh(&itp->tx_lock); + return 0; +} + +bool cw1200_itp_rxed(struct cw1200_common *priv, struct sk_buff *skb) +{ + struct cw1200_itp *itp = &priv->debug->itp; + struct ieee80211_rx_status *rx = IEEE80211_SKB_RXCB(skb); + int signal; + + if (atomic_read(&itp->test_mode) != TEST_MODE_RX_TEST) + return cw1200_is_itp(priv); + if (rx->freq != priv->channel->center_freq) + return true; + + signal = rx->signal; + itp->rx_cnt++; + itp->rx_rssi += signal; + if (itp->rx_rssi_min > rx->signal) + itp->rx_rssi_min = rx->signal; + if (itp->rx_rssi_max < rx->signal) + itp->rx_rssi_max = rx->signal; + + return true; +} + +void cw1200_itp_wake_up_tx(struct cw1200_common *priv) +{ + wake_up(&priv->debug->itp.write_wait); +} + +bool cw1200_itp_tx_running(struct cw1200_common *priv) +{ + if (atomic_read(&priv->debug->itp.awaiting_confirm) || + atomic_read(&priv->debug->itp.test_mode) == + TEST_MODE_TX_TEST) { + atomic_sub(1, &priv->debug->itp.awaiting_confirm); + return true; + } + return false; +} + +static void cw1200_itp_handle(struct cw1200_common *priv, + struct sk_buff *skb) +{ + struct cw1200_itp *itp = &priv->debug->itp; + const struct wiphy *wiphy = priv->hw->wiphy; + int cmd; + int ret; + + itp_printk(KERN_DEBUG "[ITP] <<< %s", skb->data); + if (sscanf(skb->data, "%d", &cmd) != 1) { + cw1200_itp_err(priv, -EINVAL, 1); + return; + } + + switch (cmd) { + case 1: /* RX test */ + if (atomic_read(&itp->test_mode)) { + cw1200_itp_err(priv, -EBUSY, 0); + return; + } + ret = sscanf(skb->data, "%d,%d,%d", + &cmd, &itp->band, &itp->ch); + if (ret != 3) { + cw1200_itp_err(priv, -EINVAL, ret + 1); + return; + } + if (itp->band >= 2) + cw1200_itp_err(priv, -EINVAL, 2); + else if (!wiphy->bands[itp->band]) + cw1200_itp_err(priv, -EINVAL, 2); + else if (itp->ch >= + wiphy->bands[itp->band]->n_channels) + cw1200_itp_err(priv, -EINVAL, 3); + else { + cw1200_itp_rx_stats(priv); + cw1200_itp_rx_start(priv); + } + break; + case 2: /* RX stat */ + cw1200_itp_rx_stats(priv); + break; + case 3: /* RX/TX stop */ + if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) { + cw1200_itp_rx_stats(priv); + cw1200_itp_rx_stop(priv); + } else if (atomic_read(&itp->test_mode) == TEST_MODE_TX_TEST) { + cw1200_itp_tx_stop(priv); + } else + cw1200_itp_err(priv, -EBUSY, 0); + break; + case 4: /* TX start */ + if (atomic_read(&itp->test_mode) != TEST_MODE_NO_TEST) { + cw1200_itp_err(priv, -EBUSY, 0); + return; + } + ret = sscanf(skb->data, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + &cmd, &itp->band, &itp->ch, &itp->rate, + &itp->preamble, &itp->number, &itp->data_mode, + &itp->interval_us, &itp->power, &itp->data_len); + if (ret != 10) { + cw1200_itp_err(priv, -EINVAL, ret + 1); + return; + } + if (itp->band >= 2) + cw1200_itp_err(priv, -EINVAL, 2); + else if (!wiphy->bands[itp->band]) + cw1200_itp_err(priv, -EINVAL, 2); + else if (itp->ch >= + wiphy->bands[itp->band]->n_channels) + cw1200_itp_err(priv, -EINVAL, 3); + else if (itp->rate >= 20) + cw1200_itp_err(priv, -EINVAL, 4); + else if (itp->preamble >= ITP_PREAMBLE_MAX) + cw1200_itp_err(priv, -EINVAL, 5); + else if (itp->data_mode >= ITP_DATA_MAX_MODE) + cw1200_itp_err(priv, -EINVAL, 7); + else if (itp->data_len < ITP_MIN_DATA_SIZE || + itp->data_len > priv->wsm_caps.sizeInpChBuf - + itp->hdr_len) + cw1200_itp_err(priv, -EINVAL, 8); + else { + cw1200_itp_tx_start(priv); + } + break; + case 5: + cw1200_itp_get_version(priv, ITP_CHIP_ID); + break; + case 6: + cw1200_itp_get_version(priv, ITP_FW_VER); + break; + + } +} + +static void cw1200_itp_err(struct cw1200_common *priv, + int err, int arg) +{ + struct cw1200_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + static char buf[255]; + int len; + + len = snprintf(buf, sizeof(buf), "%d,%d\n", + err, arg); + if (len <= 0) + return; + + skb = dev_alloc_skb(len); + if (!skb) + return; + + skb_trim(skb, 0); + skb_put(skb, len); + + memcpy(skb->data, buf, len); + skb_queue_tail(&itp->log_queue, skb); + wake_up(&itp->read_wait); + + len = sprint_symbol(buf, + (unsigned long)__builtin_return_address(0)); + if (len <= 0) + return; + itp_printk(KERN_DEBUG "[ITP] error %d,%d from %s\n", + err, arg, buf); +} diff --git a/drivers/staging/cw1200/itp.h b/drivers/staging/cw1200/itp.h new file mode 100644 index 00000000000..635e7f85ff9 --- /dev/null +++ b/drivers/staging/cw1200/itp.h @@ -0,0 +1,151 @@ +/* + * ITP code for ST-Ericsson CW1200 mac80211 driver + * + * Copyright (c) 2011, ST-Ericsson + * Author: Dmitry Tarnyagin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef CW1200_ITP_H_INCLUDED +#define CW1200_ITP_H_INCLUDED + +struct cw200_common; +struct wsm_tx_confirm; +struct dentry; + +#ifdef CONFIG_CW1200_ITP + +/*extern*/ struct ieee80211_channel; + +#define TEST_MODE_NO_TEST (0) +#define TEST_MODE_RX_TEST (1) +#define TEST_MODE_TX_TEST (2) + +#define itp_printk(...) printk(__VA_ARGS__) +#define ITP_DEFAULT_DA_ADDR {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} +#define ITP_MIN_DATA_SIZE 6 +#define ITP_MAX_DATA_SIZE 1600 +#define ITP_TIME_THRES_US 10000 +#define ITP_US_TO_MS(x) ((x)/1000) +#define ITP_MS_TO_US(x) ((x)*1000) +#if ((ITP_US_TO_MS(ITP_TIME_THRES_US))*HZ/1000) < 1 +#warning not able to achieve non-busywaiting ITP_TIME_THRES_US\ +precision with current HZ value ! +#endif +#define ITP_BUF_SIZE 255 + + +enum cw1200_itp_data_modes { + ITP_DATA_ZEROS, + ITP_DATA_ONES, + ITP_DATA_ZERONES, + ITP_DATA_RANDOM, + ITP_DATA_MAX_MODE, +}; + +enum cw1200_itp_version_type { + ITP_CHIP_ID, + ITP_FW_VER, +}; + +enum cw1200_itp_preamble_type { + ITP_PREAMBLE_LONG, + ITP_PREAMBLE_SHORT, + ITP_PREAMBLE_OFDM, + ITP_PREAMBLE_MIXED, + ITP_PREAMBLE_GREENFIELD, + ITP_PREAMBLE_MAX, +}; + + +struct cw1200_itp { + struct cw1200_common *priv; + atomic_t open_count; + atomic_t awaiting_confirm; + struct sk_buff_head log_queue; + wait_queue_head_t read_wait; + wait_queue_head_t write_wait; + wait_queue_head_t close_wait; + struct ieee80211_channel *saved_channel; + atomic_t stop_tx; + struct delayed_work tx_work; + struct delayed_work tx_finish; + spinlock_t tx_lock; + struct timespec last_sent; + atomic_t test_mode; + int rx_cnt; + long rx_rssi; + int rx_rssi_max; + int rx_rssi_min; + unsigned band; + unsigned ch; + unsigned rate; + unsigned preamble; + unsigned int number; + unsigned data_mode; + int interval_us; + int power; + u8 *data; + int hdr_len; + int data_len; +}; + +int cw1200_itp_init(struct cw1200_common *priv); +void cw1200_itp_release(struct cw1200_common *priv); + +bool cw1200_is_itp(struct cw1200_common *priv); +bool cw1200_itp_rxed(struct cw1200_common *priv, struct sk_buff *skb); +void cw1200_itp_wake_up_tx(struct cw1200_common *priv); +int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data, + size_t *tx_len, int *burst); +bool cw1200_itp_tx_running(struct cw1200_common *priv); + +#else /* CONFIG_CW1200_ITP */ + +static inline int +cw1200_itp_init(struct cw1200_common *priv) +{ + return 0; +} + +static inline void cw1200_itp_release(struct cw1200_common *priv) +{ +} + +static inline bool cw1200_is_itp(struct cw1200_common *priv) +{ + return false; +} + +static inline bool cw1200_itp_rxed(struct cw1200_common *priv, + struct sk_buff *skb) +{ + return false; +} + + +static inline void cw1200_itp_consume_txed(struct cw1200_common *priv) +{ +} + +static inline void cw1200_itp_wake_up_tx(struct cw1200_common *priv) +{ +} + +static inline int cw1200_itp_get_tx(struct cw1200_common *priv, u8 **data, + size_t *tx_len, int *burst) +{ + return 0; +} + +static inline bool cw1200_itp_tx_running(struct cw1200_common *priv) +{ + return false; +} + +#endif /* CONFIG_CW1200_ITP */ + +#endif /* CW1200_ITP_H_INCLUDED */ diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c index f568c6275c3..7c0fa0b0f2a 100644 --- a/drivers/staging/cw1200/txrx.c +++ b/drivers/staging/cw1200/txrx.c @@ -940,6 +940,9 @@ void cw1200_tx_confirm_cb(struct cw1200_common *priv, txrx_printk(KERN_DEBUG "[TX] TX confirm: %d, %d.\n", arg->status, arg->ackFailures); + if (unlikely(cw1200_itp_tx_running(priv))) + return; + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { /* STA is stopped. */ return; @@ -1085,7 +1088,8 @@ void cw1200_skb_dtor(struct cw1200_common *priv, txpriv->raw_link_id, txpriv->tid); tx_policy_put(priv, txpriv->rate_id); } - ieee80211_tx_status(priv->hw, skb); + if (likely(!cw1200_is_itp(priv))) + ieee80211_tx_status(priv->hw, skb); } void cw1200_rx_cb(struct cw1200_common *priv, @@ -1262,7 +1266,9 @@ void cw1200_rx_cb(struct cw1200_common *priv, grace_period = 1 * HZ; cw1200_pm_stay_awake(&priv->pm_state, grace_period); - if (unlikely(early_data)) { + if (unlikely(cw1200_itp_rxed(priv, skb))) + consume_skb(skb); + else if (unlikely(early_data)) { spin_lock_bh(&priv->ps_state_lock); /* Double-check status with lock held */ if (entry->status == CW1200_LINK_SOFT) diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c index 085868b532e..834524666a7 100644 --- a/drivers/staging/cw1200/wsm.c +++ b/drivers/staging/cw1200/wsm.c @@ -21,6 +21,7 @@ #include "wsm.h" #include "bh.h" #include "debug.h" +#include "itp.h" #if defined(CONFIG_CW1200_WSM_DEBUG) #define wsm_printk(...) printk(__VA_ARGS__) @@ -1644,6 +1645,10 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data, /* More is used only for broadcasts. */ bool more = false; + count = cw1200_itp_get_tx(priv, data, tx_len, burst); + if (count) + return count; + if (priv->wsm_cmd.ptr) { ++count; spin_lock(&priv->wsm_cmd.lock); -- cgit v1.2.3