summaryrefslogtreecommitdiff
path: root/drivers/staging/cw1200/sta.c
diff options
context:
space:
mode:
authorDmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>2010-06-05 23:26:14 +0200
committerRobert Marklund <robert.marklund@stericsson.com>2011-10-05 11:16:50 +0200
commit82b8089e8fca45e08ffa99cf3612c0f078899efe (patch)
tree0dd22acb11b496e914d2fab206926f413cb3ff86 /drivers/staging/cw1200/sta.c
parentf46a6bba76d040633ff59100e42aabd0784cb593 (diff)
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 <dmitry.tarnyagin@stericsson.com>
Diffstat (limited to 'drivers/staging/cw1200/sta.c')
-rw-r--r--drivers/staging/cw1200/sta.c1026
1 files changed, 1026 insertions, 0 deletions
diff --git a/drivers/staging/cw1200/sta.c b/drivers/staging/cw1200/sta.c
new file mode 100644
index 00000000000..3bb17b1e616
--- /dev/null
+++ b/drivers/staging/cw1200/sta.c
@@ -0,0 +1,1026 @@
+/*
+ * Mac80211 STA API for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>
+ *
+ * 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 <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+
+#include "cw1200.h"
+#include "sta.h"
+#include "fwio.h"
+#include "bh.h"
+
+#ifdef CW1200_DEBUG_ENABLE_STA_LOGS
+#define sta_printk(...) printk(__VA_ARGS__)
+#else
+#define sta_printk(...)
+#endif
+
+
+static int cw1200_cancel_scan(struct cw1200_common *priv);
+
+static inline void __cw1200_free_event_queue(struct list_head *list)
+{
+ while (!list_empty(list)) {
+ struct cw1200_wsm_event *event =
+ list_first_entry(list, struct cw1200_wsm_event,
+ link);
+ list_del(&event->link);
+ kfree(event);
+ }
+}
+
+/* ******************************************************************** */
+/* STA API */
+
+int cw1200_start(struct ieee80211_hw *dev)
+{
+ struct cw1200_common *priv = dev->priv;
+ int ret = 0;
+
+ mutex_lock(&priv->conf_mutex);
+
+ /* default ECDA */
+ WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47);
+ WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94);
+ WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0);
+ WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0);
+ ret = wsm_set_edca_params(priv, &priv->edca);
+ if (WARN_ON(ret))
+ goto out;
+
+ memset(priv->bssid, ~0, ETH_ALEN);
+ memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN);
+ priv->mode = NL80211_IFTYPE_MONITOR;
+ priv->softled_state = 0;
+ priv->wep_default_key_id = -1;
+
+ priv->cqm_link_loss_count = 60;
+ priv->cqm_beacon_loss_count = 20;
+
+ ret = cw1200_setup_mac(priv);
+ if (WARN_ON(ret))
+ goto out;
+
+ /* err = cw1200_set_leds(priv); */
+
+out:
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+void cw1200_stop(struct ieee80211_hw *dev)
+{
+ struct cw1200_common *priv = dev->priv;
+ unsigned long flags;
+ LIST_HEAD(list);
+ int i;
+
+ struct wsm_reset reset = {
+ .reset_statistics = true,
+ };
+
+ wsm_lock_tx(priv);
+
+ while (down_trylock(&priv->scan.lock)) {
+ /* Scan is in progress. Force it to stop. */
+ priv->scan.req = NULL;
+ schedule();
+ }
+ up(&priv->scan.lock);
+
+ mutex_lock(&priv->conf_mutex);
+ cw1200_free_keys(priv);
+ priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+ mutex_unlock(&priv->conf_mutex);
+
+ cancel_delayed_work_sync(&priv->scan.probe_work);
+ cancel_delayed_work_sync(&priv->scan.timeout);
+ cancel_delayed_work_sync(&priv->join_timeout);
+ cancel_delayed_work_sync(&priv->bss_loss_work);
+ cancel_delayed_work_sync(&priv->connection_loss_work);
+#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE
+ cancel_delayed_work_sync(&priv->keep_alive_work);
+#endif
+ switch (priv->join_status) {
+ case CW1200_JOIN_STATUS_STA:
+ queue_work(priv->workqueue, &priv->unjoin_work);
+ break;
+ case CW1200_JOIN_STATUS_AP:
+ /* If you see this warning please change the code to iterate
+ * through the map and reset each link separately. */
+ WARN_ON(priv->link_id_map);
+ priv->sta_asleep_mask = 0;
+ priv->suspend_multicast = false;
+ wsm_reset(priv, &reset);
+ wsm_unlock_tx(priv);
+ break;
+ default:
+ wsm_unlock_tx(priv);
+ }
+ flush_workqueue(priv->workqueue);
+ mutex_lock(&priv->conf_mutex);
+
+ priv->softled_state = 0;
+ /* cw1200_set_leds(priv); */
+
+ spin_lock_irqsave(&priv->event_queue_lock, flags);
+ list_splice_init(&priv->event_queue, &list);
+ spin_unlock_irqrestore(&priv->event_queue_lock, flags);
+ __cw1200_free_event_queue(&list);
+
+ priv->delayed_link_loss = 0;
+
+ priv->link_id_map = 0;
+ priv->join_status = CW1200_JOIN_STATUS_MONITOR;
+
+ /* TODO: Complete deinitialization */
+
+ for (i = 0; i < 4; i++)
+ cw1200_queue_clear(&priv->tx_queue[i]);
+
+ /* HACK! */
+ if (atomic_xchg(&priv->tx_lock, 1) != 1)
+ sta_printk(KERN_DEBUG "[STA] TX is force-unlocked due to stop " \
+ "request.\n");
+
+ wsm_unlock_tx(priv);
+
+ mutex_unlock(&priv->conf_mutex);
+}
+
+int cw1200_add_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
+{
+ int ret;
+ struct cw1200_common *priv = dev->priv;
+ /* __le32 auto_calibration_mode = __cpu_to_le32(1); */
+
+ mutex_lock(&priv->conf_mutex);
+
+ if (priv->mode != NL80211_IFTYPE_MONITOR) {
+ mutex_unlock(&priv->conf_mutex);
+ return -EOPNOTSUPP;
+ }
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_AP:
+ priv->mode = vif->type;
+ break;
+ default:
+ mutex_unlock(&priv->conf_mutex);
+ return -EOPNOTSUPP;
+ }
+
+ priv->vif = vif;
+ memcpy(priv->mac_addr, vif->addr, ETH_ALEN);
+
+ ret = WARN_ON(cw1200_setup_mac(priv));
+ /* Enable auto-calibration */
+ /* Exception in subsequent channel switch; disabled.
+ WARN_ON(wsm_write_mib(priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE,
+ &auto_calibration_mode, sizeof(auto_calibration_mode)));
+ */
+
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+void cw1200_remove_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
+{
+ struct cw1200_common *priv = dev->priv;
+
+ struct wsm_reset reset = {
+ .reset_statistics = true,
+ };
+
+ mutex_lock(&priv->conf_mutex);
+
+ priv->vif = NULL;
+ priv->mode = NL80211_IFTYPE_MONITOR;
+ memset(priv->mac_addr, 0, ETH_ALEN);
+ memset(priv->bssid, 0, ETH_ALEN);
+ WARN_ON(wsm_reset(priv, &reset));
+ cw1200_free_keys(priv);
+ cw1200_setup_mac(priv);
+
+ mutex_unlock(&priv->conf_mutex);
+}
+
+int cw1200_config(struct ieee80211_hw *dev, u32 changed)
+{
+ int ret = 0;
+ struct cw1200_common *priv = dev->priv;
+ struct ieee80211_conf *conf = &dev->conf;
+
+ mutex_lock(&priv->conf_mutex);
+ /* TODO: IEEE80211_CONF_CHANGE_QOS */
+ if (changed & IEEE80211_CONF_CHANGE_POWER) {
+ priv->output_power = conf->power_level;
+ sta_printk(KERN_DEBUG "[STA] TX power: %d\n", priv->output_power);
+ WARN_ON(wsm_set_output_power(priv, priv->output_power * 10));
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) {
+ /* TODO: Not sure. Needs to be verified. */
+ /* TODO: DTIM skipping */
+ int dtim_interval = conf->ps_dtim_period;
+ int listen_interval = conf->listen_interval;
+ if (dtim_interval < 1)
+ dtim_interval = 1;
+ if (listen_interval < dtim_interval)
+ listen_interval = 0;
+ /* TODO: max_sleep_period is not supported
+ * and silently skipped. */
+ sta_printk(KERN_DEBUG "[STA] DTIM %d, listen %d\n",
+ dtim_interval, listen_interval);
+ WARN_ON(wsm_set_beacon_wakeup_period(priv,
+ dtim_interval, listen_interval));
+ }
+
+
+ if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) &&
+ (priv->channel != conf->channel)) {
+ struct wsm_switch_channel channel = {
+ .newChannelNumber = conf->channel->hw_value
+ };
+ cw1200_cancel_scan(priv);
+ sta_printk(KERN_DEBUG "[STA] Freq %d (wsm ch: %d).\n",
+ conf->channel->center_freq, conf->channel->hw_value);
+ WARN_ON(wait_event_interruptible_timeout(
+ priv->channel_switch_done,
+ !priv->channel_switch_in_progress, 3 * HZ) <= 0);
+ WARN_ON(wsm_switch_channel(priv, &channel));
+ priv->channel = conf->channel;
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_PS) {
+ priv->powersave_mode.pmMode =
+ (conf->flags & IEEE80211_CONF_PS) ?
+ WSM_PSM_PS : WSM_PSM_ACTIVE;
+ WARN_ON(wsm_set_pm(priv, &priv->powersave_mode));
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+ /* TBD: It looks like it's transparent
+ * there's a monitor interface present -- use this
+ * to determine for example whether to calculate
+ * timestamps for packets or not, do not use instead
+ * of filter flags! */
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_IDLE) {
+ struct wsm_operational_mode mode = {
+ /* TODO: wsm_power_mode_quiescent is more efficient,
+ * but it requires AI to get device on again. */
+ .power_mode = (conf->flags & IEEE80211_CONF_IDLE) ?
+ wsm_power_mode_doze : wsm_power_mode_active
+ };
+ WARN_ON(wsm_set_operational_mode(priv, &mode));
+ }
+
+ if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
+ unsigned long flags;
+ sta_printk(KERN_DEBUG "[STA] Retry limits: %d (long), " \
+ "%d (short).\n",
+ conf->long_frame_max_tx_count,
+ conf->short_frame_max_tx_count);
+ spin_lock_irqsave(&priv->tx_policy_cache.lock, flags);
+ priv->long_frame_max_tx_count = conf->long_frame_max_tx_count;
+ priv->short_frame_max_tx_count =
+ (conf->short_frame_max_tx_count < 0x0F) ?
+ conf->short_frame_max_tx_count : 0x0F;
+ priv->hw->max_rate_tries = priv->short_frame_max_tx_count;
+ spin_unlock_irqrestore(&priv->tx_policy_cache.lock, flags);
+ /* TBD: I think we don't need tx_policy_force_upload().
+ * Outdated policies will leave cache in a normal way. */
+ /* WARN_ON(tx_policy_force_upload(priv)); */
+ }
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+void cw1200_configure_filter(struct ieee80211_hw *dev,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ u64 multicast)
+{
+#if 0
+ struct cw1200_common *priv = dev->priv;
+ struct wsm_rx_filter filter = {
+ .promiscuous = (*total_flags & FIF_PROMISC_IN_BSS) ? 1 : 0,
+ .bssid = (*total_flags & FIF_OTHER_BSS) ? 1 : 0,
+ .fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0,
+ };
+ struct wsm_beacon_filter_control bf_control = {
+ .enabled = 0,
+ .bcn_count = (*total_flags &
+ (FIF_BCN_PRBRESP_PROMISC | FIF_PROMISC_IN_BSS)) ?
+ 1 : 0,
+ };
+#endif
+
+ *total_flags &= FIF_PROMISC_IN_BSS |
+ FIF_OTHER_BSS |
+ FIF_FCSFAIL |
+ FIF_BCN_PRBRESP_PROMISC;
+
+#if 0
+ /* FIXME: FW behaves strange if promiscuous mode is enabled. */
+ WARN_ON(wsm_set_rx_filter(priv, &filter));
+ WARN_ON(wsm_beacon_filter_control(priv, &bf_control));
+#endif
+}
+
+int cw1200_conf_tx(struct ieee80211_hw *dev, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct cw1200_common *priv = dev->priv;
+ int ret = 0;
+
+ mutex_lock(&priv->conf_mutex);
+
+ if (queue < dev->queues) {
+ WSM_EDCA_SET(&priv->edca, queue, params->aifs,
+ params->cw_min, params->cw_max, params->txop);
+ ret = wsm_set_edca_params(priv, &priv->edca);
+ } else
+ ret = -EINVAL;
+
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+int cw1200_get_stats(struct ieee80211_hw *dev,
+ struct ieee80211_low_level_stats *stats)
+{
+ struct cw1200_common *priv = dev->priv;
+
+ memcpy(stats, &priv->stats, sizeof(*stats));
+ return 0;
+}
+
+/*
+int cw1200_get_tx_stats(struct ieee80211_hw *dev,
+ struct ieee80211_tx_queue_stats *stats)
+{
+ int i;
+ struct cw1200_common *priv = dev->priv;
+
+ for (i = 0; i < dev->queues; ++i)
+ cw1200_queue_get_stats(&priv->tx_queue[i], &stats[i]);
+
+ return 0;
+}
+*/
+
+int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+ int ret = -EOPNOTSUPP;
+ struct cw1200_common *priv = dev->priv;
+
+ mutex_lock(&priv->conf_mutex);
+
+ if (cmd == SET_KEY) {
+ u8 *peer_addr = NULL;
+ int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
+ 1 : 0;
+ int idx = cw1200_alloc_key(priv);
+ struct wsm_add_key *wsm_key = &priv->keys[idx];
+
+ if (idx < 0) {
+ ret = -EINVAL;
+ goto finally;
+ }
+
+ BUG_ON(pairwise && !sta);
+ if (sta)
+ peer_addr = sta->addr;
+
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ if (key->keylen > 16) {
+ cw1200_free_key(priv, idx);
+ ret = -EINVAL;
+ goto finally;
+ }
+
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE;
+ memcpy(wsm_key->wepPairwiseKey.peerAddress,
+ peer_addr, ETH_ALEN);
+ memcpy(wsm_key->wepPairwiseKey.keyData,
+ &key->key[0], key->keylen);
+ wsm_key->wepPairwiseKey.keyLength = key->keylen;
+ } else {
+ wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT;
+ memcpy(wsm_key->wepGroupKey.keyData,
+ &key->key[0], key->keylen);
+ wsm_key->wepGroupKey.keyLength = key->keylen;
+ wsm_key->wepGroupKey.keyId = key->keyidx;
+ }
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE;
+ memcpy(wsm_key->tkipPairwiseKey.peerAddress,
+ peer_addr, ETH_ALEN);
+ memcpy(wsm_key->tkipPairwiseKey.tkipKeyData,
+ &key->key[0], 16);
+ memcpy(wsm_key->tkipPairwiseKey.txMicKey,
+ &key->key[16], 8);
+ memcpy(wsm_key->tkipPairwiseKey.rxMicKey,
+ &key->key[24], 8);
+ } else {
+ size_t mic_offset =
+ (priv->mode == NL80211_IFTYPE_AP) ?
+ 16 : 24;
+ wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP;
+ memcpy(wsm_key->tkipGroupKey.tkipKeyData,
+ &key->key[0], 16);
+ memcpy(wsm_key->tkipGroupKey.rxMicKey,
+ &key->key[mic_offset], 8);
+
+ /* TODO: Where can I find TKIP SEQ? */
+ memset(wsm_key->tkipGroupKey.rxSeqCounter,
+ 0, 8);
+ wsm_key->tkipGroupKey.keyId = key->keyidx;
+
+ print_hex_dump_bytes("TKIP: ", DUMP_PREFIX_NONE,
+ key->key, key->keylen);
+ }
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE;
+ memcpy(wsm_key->aesPairwiseKey.peerAddress,
+ peer_addr, ETH_ALEN);
+ memcpy(wsm_key->aesPairwiseKey.aesKeyData,
+ &key->key[0], 16);
+ } else {
+ wsm_key->type = WSM_KEY_TYPE_AES_GROUP;
+ memcpy(wsm_key->aesGroupKey.aesKeyData,
+ &key->key[0], 16);
+ /* TODO: Where can I find AES SEQ? */
+ memset(wsm_key->aesGroupKey.rxSeqCounter,
+ 0, 8);
+ wsm_key->aesGroupKey.keyId = key->keyidx;
+ }
+ break;
+#if 0
+ case WLAN_CIPHER_SUITE_WAPI:
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE;
+ memcpy(wsm_key->wapiPairwiseKey.peerAddress,
+ peer_addr, ETH_ALEN);
+ memcpy(wsm_key->wapiPairwiseKey.wapiKeyData,
+ &key->key[0], 16);
+ memcpy(wsm_key->wapiPairwiseKey.micKeyData,
+ &key->key[16], 16);
+ wsm_key->wapiPairwiseKey.keyId = key->keyidx;
+ } else {
+ wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP;
+ memcpy(wsm_key->wapiGroupKey.wapiKeyData,
+ &key->key[0], 16);
+ memcpy(wsm_key->wapiGroupKey.micKeyData,
+ &key->key[16], 16);
+ wsm_key->wapiGroupKey.keyId = key->keyidx;
+ }
+ break;
+#endif
+ default:
+ WARN_ON(1);
+ cw1200_free_key(priv, idx);
+ ret = -EOPNOTSUPP;
+ goto finally;
+ }
+ ret = WARN_ON(wsm_add_key(priv, wsm_key));
+ if (!ret)
+ key->hw_key_idx = idx;
+ else
+ cw1200_free_key(priv, idx);
+ } else if (cmd == DISABLE_KEY) {
+ struct wsm_remove_key wsm_key = {
+ .entryIndex = key->hw_key_idx,
+ };
+
+ if (wsm_key.entryIndex > WSM_KEY_MAX_INDEX) {
+ ret = -EINVAL;
+ goto finally;
+ }
+
+ cw1200_free_key(priv, wsm_key.entryIndex);
+ ret = wsm_remove_key(priv, &wsm_key);
+ } else {
+ BUG_ON("Unsupported command");
+ }
+
+finally:
+ mutex_unlock(&priv->conf_mutex);
+ return ret;
+}
+
+void cw1200_wep_key_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, wep_key_work);
+ __le32 wep_default_key_id = __cpu_to_le32(
+ priv->wep_default_key_id);
+
+ sta_printk(KERN_DEBUG "[STA] Setting default WEP key: %d\n",
+ priv->wep_default_key_id);
+ wsm_flush_tx(priv);
+ WARN_ON(wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID,
+ &wep_default_key_id, sizeof(wep_default_key_id)));
+ wsm_unlock_tx(priv);
+}
+
+int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+ int ret;
+ __le32 val32;
+
+ if (value != (u32) -1)
+ val32 = __cpu_to_le32(value);
+ else
+ val32 = 0; /* disabled */
+
+ /* mutex_lock(&priv->conf_mutex); */
+ ret = WARN_ON(wsm_write_mib(hw->priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD,
+ &val32, sizeof(val32)));
+ /* mutex_unlock(&priv->conf_mutex); */
+ return ret;
+}
+
+/* ******************************************************************** */
+/* WSM callbacks */
+
+/* TODO: move to txrx.c */
+void cw1200_rx_cb(struct cw1200_common *priv,
+ struct wsm_rx *arg,
+ struct sk_buff **skb_p)
+{
+ struct sk_buff *skb = *skb_p;
+ struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb);
+ hdr->flag = 0;
+
+ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) {
+ /* STA is stopped. */
+ return;
+ }
+
+ if (unlikely(arg->status)) {
+ if (arg->status == WSM_STATUS_MICFAILURE) {
+ sta_printk(KERN_DEBUG "[RX] MIC failure.\n");
+ hdr->flag |= RX_FLAG_MMIC_ERROR;
+ } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) {
+ sta_printk(KERN_DEBUG "[RX] No key found.\n");
+ return;
+ } else {
+ sta_printk(KERN_DEBUG "[RX] Receive failure: %d.\n",
+ arg->status);
+ return;
+ }
+ }
+
+ hdr->mactime = 0; /* Not supported by WSM */
+ hdr->freq = ieee80211_channel_to_frequency(arg->channelNumber);
+ hdr->band = (hdr->freq >= 5000) ?
+ IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ;
+ hdr->rate_idx = arg->rxedRate;
+ if (hdr->rate_idx >= 4) /* TODO: Use common convert function. */
+ hdr->rate_idx -= 2;
+ hdr->signal = (s8)arg->rcpiRssi;
+ hdr->antenna = 0;
+
+ if (arg->flags & 0x07)
+ hdr->flag |= RX_FLAG_DECRYPTED;
+ if (arg->flags & BIT(14))
+ hdr->flag |= RX_FLAG_HT;
+#if 0
+ /* Wrong: ACK could be disable for this ACL */
+ if (arg->flags & BIT(16))
+ priv->last_activity_time = jiffies;
+#endif
+
+#if 0
+ print_hex_dump_bytes("RX: ", DUMP_PREFIX_NONE,
+ skb->data, skb->len);
+#endif
+
+ /* Not that we really need _irqsafe variant here,
+ * but it offloads realtime bh thread and improve
+ * system performance. */
+ ieee80211_rx_irqsafe(priv->hw, skb);
+ *skb_p = NULL;
+}
+
+void cw1200_free_event_queue(struct cw1200_common *priv)
+{
+ unsigned long flags;
+ LIST_HEAD(list);
+
+ spin_lock_irqsave(&priv->event_queue_lock, flags);
+ list_splice_init(&priv->event_queue, &list);
+ spin_unlock_irqrestore(&priv->event_queue_lock, flags);
+
+ __cw1200_free_event_queue(&list);
+}
+
+void cw1200_event_handler(struct work_struct *work)
+{
+ unsigned long flags;
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, event_handler);
+ struct cw1200_wsm_event *event;
+ LIST_HEAD(list);
+
+ spin_lock_irqsave(&priv->event_queue_lock, flags);
+ list_splice_init(&priv->event_queue, &list);
+ spin_unlock_irqrestore(&priv->event_queue_lock, flags);
+
+ list_for_each_entry(event, &list, link) {
+ switch (event->evt.eventId) {
+ case WSM_EVENT_ERROR:
+ /* I even don't know what is it about.. */
+ STUB();
+ break;
+ case WSM_EVENT_BSS_LOST:
+ {
+ sta_printk(KERN_DEBUG "[CQM] BSS lost.\n");
+ cancel_delayed_work_sync(&priv->bss_loss_work);
+ cancel_delayed_work_sync(&priv->connection_loss_work);
+ if (!down_trylock(&priv->scan.lock)) {
+ up(&priv->scan.lock);
+ priv->delayed_link_loss = 0;
+ queue_delayed_work(priv->workqueue,
+ &priv->bss_loss_work, 0);
+ } else {
+ /* Scan is in progress. Delay reporting. */
+ /* Scan complete will trigger bss_loss_work */
+ priv->delayed_link_loss = 1;
+ /* Also we're starting watchdog. */
+ queue_delayed_work(priv->workqueue,
+ &priv->bss_loss_work, 10 * HZ);
+ }
+ break;
+ }
+ case WSM_EVENT_BSS_REGAINED:
+ {
+ sta_printk(KERN_DEBUG "[CQM] BSS regained.\n");
+ priv->delayed_link_loss = 0;
+ cancel_delayed_work_sync(&priv->bss_loss_work);
+ cancel_delayed_work_sync(&priv->connection_loss_work);
+ break;
+ }
+ case WSM_EVENT_RADAR_DETECTED:
+ STUB();
+ break;
+ case WSM_EVENT_RCPI_RSSI:
+ {
+ int rssi = (int)(s8)(event->evt.eventData & 0xFF);
+ int cqm_evt = (rssi <= priv->cqm_rssi_thold) ?
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
+ sta_printk(KERN_DEBUG "[CQM] RSSI event: %d", rssi);
+ ieee80211_cqm_rssi_notify(priv->vif, cqm_evt,
+ GFP_KERNEL);
+ break;
+ }
+ case WSM_EVENT_BT_INACTIVE:
+ STUB();
+ break;
+ case WSM_EVENT_BT_ACTIVE:
+ STUB();
+ break;
+ }
+ }
+ __cw1200_free_event_queue(&list);
+}
+
+void cw1200_bss_loss_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, bss_loss_work.work);
+ int timeout; /* in beacons */
+
+ timeout = priv->cqm_link_loss_count -
+ priv->cqm_beacon_loss_count;
+
+ if (priv->cqm_beacon_loss_count) {
+ sta_printk(KERN_DEBUG "[CQM] Beacon loss.\n");
+ if (timeout <= 0)
+ timeout = 0;
+#ifdef USE_STE_EXTENSIONS
+ ieee80211_cqm_beacon_miss_notify(priv->vif, GFP_KERNEL);
+#endif
+ } else {
+ timeout = 0;
+ }
+
+ cancel_delayed_work_sync(&priv->connection_loss_work);
+ queue_delayed_work(priv->workqueue,
+ &priv->connection_loss_work,
+ timeout * HZ / 10);
+}
+
+void cw1200_connection_loss_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common,
+ connection_loss_work.work);
+ sta_printk(KERN_DEBUG "[CQM] Reporting connection loss.\n");
+ ieee80211_connection_loss(priv->vif);
+}
+
+#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE
+void cw1200_keep_alive_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, keep_alive_work.work);
+ unsigned long now = jiffies;
+ unsigned long delta = now - priv->last_activity_time;
+ unsigned long tmo = 30 * HZ;
+
+ if (delta >= tmo) {
+ sta_printk(KERN_DEBUG "[CQM] Keep-alive ping.\n");
+ STUB();
+ /* TODO: Do a keep-alive ping :) */
+ priv->last_activity_time = now;
+ } else {
+ tmo -= delta;
+ }
+ queue_delayed_work(priv->workqueue,
+ &priv->keep_alive_work, tmo);
+}
+#endif
+
+void cw1200_tx_failure_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, tx_failure_work);
+ sta_printk(KERN_DEBUG "[CQM] Reporting TX failure.\n");
+#ifdef USE_STE_EXTENSIONS
+ ieee80211_cqm_tx_fail_notify(priv->vif, GFP_KERNEL);
+#else
+ (void)priv;
+#endif
+}
+
+/* ******************************************************************** */
+/* Internal API */
+
+int cw1200_setup_mac(struct cw1200_common *priv)
+{
+ /* TBD: Do you know how to assing MAC address without
+ * annoying uploading RX data? */
+ u8 prev_mac[ETH_ALEN];
+
+ /* NOTE: There is a bug in FW: it reports signal
+ * as RSSI if RSSI subscription is enabled.
+ * It's not enough to set WSM_RCPI_RSSI_USE_RSSI. */
+ struct wsm_rcpi_rssi_threshold threshold = {
+ .rssiRcpiMode = WSM_RCPI_RSSI_USE_RSSI |
+ WSM_RCPI_RSSI_THRESHOLD_ENABLE |
+ WSM_RCPI_RSSI_DONT_USE_UPPER |
+ WSM_RCPI_RSSI_DONT_USE_LOWER,
+ .rollingAverageCount = 16,
+ };
+ int ret = 0;
+
+ if (wsm_get_station_id(priv, &prev_mac[0])
+ || memcmp(prev_mac, priv->mac_addr, ETH_ALEN)) {
+ const char *sdd_path = NULL;
+ const struct firmware *firmware = NULL;
+ struct wsm_configuration cfg = {
+ .dot11StationId = &priv->mac_addr[0],
+ };
+
+ switch (priv->hw_revision) {
+ case CW1200_HW_REV_CUT10:
+ sdd_path = SDD_FILE_10;
+ break;
+ case CW1200_HW_REV_CUT11:
+ sdd_path = SDD_FILE_11;
+ break;
+ case CW1200_HW_REV_CUT20:
+ sdd_path = SDD_FILE_20;
+ break;
+ default:
+ BUG_ON(1);
+ }
+
+ ret = request_firmware(&firmware,
+ sdd_path, priv->pdev);
+
+ if (unlikely(ret)) {
+ cw1200_dbg(CW1200_DBG_ERROR,
+ "%s: can't load sdd file %s.\n",
+ __func__, sdd_path);
+ return ret;
+ }
+
+ cfg.dpdData = firmware->data;
+ cfg.dpdData_size = firmware->size;
+ ret = WARN_ON(wsm_configuration(priv, &cfg));
+
+ release_firmware(firmware);
+ }
+ if (ret)
+ return ret;
+
+ /* Configure RSSI/SCPI reporting as RSSI. */
+ WARN_ON(wsm_set_rcpi_rssi_threshold(priv, &threshold));
+
+ /* TODO: */
+ switch (priv->mode) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_AP:
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ /* TODO: Not verified yet. */
+ STUB();
+ break;
+ }
+
+ return 0;
+}
+
+void cw1200_join_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, join_work);
+ const struct wsm_tx *wsm = priv->join_pending_frame;
+ const u8 *frame = (u8 *)&wsm[1];
+ const u8 *bssid = &frame[4]; /* AP SSID in a 802.11 frame */
+ struct cfg80211_bss *bss;
+ const u8 *ssidie;
+ const u8 *dtimie;
+ const struct ieee80211_tim_ie *tim = NULL;
+ u8 queueId = wsm_queue_id_to_linux(wsm->queueId);
+
+ cancel_delayed_work_sync(&priv->join_timeout);
+
+ bss = cfg80211_get_bss(priv->hw->wiphy, NULL, bssid, NULL, 0, 0, 0);
+ if (!bss) {
+ priv->join_pending_frame = NULL;
+ cw1200_queue_remove(&priv->tx_queue[queueId],
+ priv, __le32_to_cpu(wsm->packetID));
+ return;
+ }
+ ssidie = cfg80211_find_ie(WLAN_EID_SSID,
+ bss->information_elements,
+ bss->len_information_elements);
+ dtimie = cfg80211_find_ie(WLAN_EID_TIM,
+ bss->information_elements,
+ bss->len_information_elements);
+ if (dtimie)
+ tim = (struct ieee80211_tim_ie *)&dtimie[2];
+
+ mutex_lock(&priv->conf_mutex);
+ {
+ struct wsm_join join = {
+ .mode = (bss->capability & WLAN_CAPABILITY_IBSS) ?
+ WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS,
+ .preambleType = WSM_JOIN_PREAMBLE_SHORT,
+ .probeForJoin = 1,
+ /* dtimPeriod will be updated after association */
+ .dtimPeriod = 1,
+ .beaconInterval = bss->beacon_interval,
+ /* basicRateSet will be updated after association */
+ .basicRateSet = 7,
+ };
+
+ if (tim && tim->dtim_period > 1) {
+ join.dtimPeriod = tim->dtim_period;
+ priv->join_dtim_period = tim->dtim_period;
+ sta_printk(KERN_DEBUG "[STA] Join DTIM: %d\n",
+ join.dtimPeriod);
+ }
+
+ priv->join_pending_frame = NULL;
+ BUG_ON(!wsm);
+ BUG_ON(!priv->channel);
+
+ join.channelNumber = priv->channel->hw_value;
+ join.band = (priv->channel->band == IEEE80211_BAND_5GHZ) ?
+ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
+
+ memcpy(&join.bssid[0], bssid, sizeof(join.bssid));
+ memcpy(&priv->join_bssid[0], bssid, sizeof(priv->join_bssid));
+
+ if (ssidie) {
+ join.ssidLength = ssidie[1];
+ if (WARN_ON(join.ssidLength > sizeof(join.ssid)))
+ join.ssidLength = sizeof(join.ssid);
+ memcpy(&join.ssid[0], &ssidie[2], join.ssidLength);
+ }
+
+ wsm_flush_tx(priv);
+
+ /* TX block_ack can use only 3 TX buffers,
+ * which is /slightly/ :) unefficient => disabled.
+ * RX block ACK is enabled for everything but voice.
+ * TODO: Verify video lags, change 0x3F -> 0x0F
+ * if necessary. */
+ WARN_ON(wsm_set_block_ack_policy(priv, 0x00, 0x3F));
+
+#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE
+ priv->last_activity_time = jiffies;
+ /* Queue keep-alive ping avery 30 sec. */
+ queue_delayed_work(priv->workqueue,
+ &priv->keep_alive_work, 30 * HZ);
+#endif
+ /* Queue unjoin if not associated in 3 sec. */
+ queue_delayed_work(priv->workqueue,
+ &priv->join_timeout, 3 * HZ);
+
+ if (wsm_join(priv, &join)) {
+ memset(&priv->join_bssid[0],
+ 0, sizeof(priv->join_bssid));
+ cw1200_queue_remove(&priv->tx_queue[queueId],
+ priv, __le32_to_cpu(wsm->packetID));
+ cancel_delayed_work_sync(&priv->join_timeout);
+#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE
+ cancel_delayed_work_sync(&priv->keep_alive_work);
+#endif
+ } else {
+ WARN_ON(cw1200_upload_keys(priv));
+#ifndef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE
+ WARN_ON(wsm_keep_alive_period(priv, 30 /* sec */));
+#endif
+ cw1200_queue_requeue(&priv->tx_queue[queueId],
+ __le32_to_cpu(wsm->packetID));
+ }
+ }
+ mutex_unlock(&priv->conf_mutex);
+ cfg80211_put_bss(bss);
+}
+
+void cw1200_join_timeout(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, join_timeout.work);
+ sta_printk(KERN_DEBUG "[WSM] Issue unjoin command (TMO).\n");
+ wsm_lock_tx(priv);
+ cw1200_unjoin_work(&priv->unjoin_work);
+}
+
+void cw1200_unjoin_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, unjoin_work);
+
+ struct wsm_reset reset = {
+ .reset_statistics = true,
+ };
+
+ mutex_lock(&priv->conf_mutex);
+ BUG_ON(priv->join_status &&
+ priv->join_status != CW1200_JOIN_STATUS_STA);
+ if (priv->join_status == CW1200_JOIN_STATUS_STA) {
+ memset(&priv->join_bssid[0], 0, sizeof(priv->join_bssid));
+ priv->join_status = CW1200_JOIN_STATUS_MONITOR;
+
+ /* Unjoin is a reset. */
+ wsm_flush_tx(priv);
+ WARN_ON(wsm_reset(priv, &reset));
+ priv->join_dtim_period = 0;
+ WARN_ON(cw1200_setup_mac(priv));
+ cw1200_free_event_queue(priv);
+ cancel_work_sync(&priv->event_handler);
+ cancel_delayed_work_sync(&priv->connection_loss_work);
+#ifdef CW1200_FIRMWARE_DOES_NOT_SUPPORT_KEEPALIVE
+ cancel_delayed_work_sync(&priv->keep_alive_work);
+#endif
+ sta_printk(KERN_DEBUG "[STA] Unjoin.\n");
+ }
+ mutex_unlock(&priv->conf_mutex);
+ wsm_unlock_tx(priv);
+}
+
+/* ******************************************************************** */
+/* STA privates */
+
+static int cw1200_cancel_scan(struct cw1200_common *priv)
+{
+ /* STUB(); */
+ return 0;
+}