wl1251: Prepare for idle mode support
[pandora-wifi.git] / drivers / net / wireless / wl12xx / wl1251_main.c
index c113e7a..8ca7901 100644 (file)
 #include "wl1251_debugfs.h"
 #include "wl1251_boot.h"
 
+/* HACK */
+#ifndef FIF_PROBE_REQ
+#define FIF_PROBE_REQ 0
+#endif
+
 void wl1251_enable_interrupts(struct wl1251 *wl)
 {
        wl->if_ops->enable_irq(wl);
@@ -126,7 +131,7 @@ static int wl1251_fetch_nvs(struct wl1251 *wl)
        }
 
        wl->nvs_len = fw->size;
-       wl->nvs = kmalloc(wl->nvs_len, GFP_KERNEL);
+       wl->nvs = kmemdup(fw->data, wl->nvs_len, GFP_KERNEL);
 
        if (!wl->nvs) {
                wl1251_error("could not allocate memory for the nvs file");
@@ -134,8 +139,6 @@ static int wl1251_fetch_nvs(struct wl1251 *wl)
                goto out;
        }
 
-       memcpy(wl->nvs, fw->data, wl->nvs_len);
-
        ret = 0;
 
 out:
@@ -297,14 +300,14 @@ static void wl1251_irq_work(struct work_struct *work)
                        wl1251_tx_complete(wl);
                }
 
-               if (intr & (WL1251_ACX_INTR_EVENT_A |
-                           WL1251_ACX_INTR_EVENT_B)) {
-                       wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT (0x%x)",
-                                    intr);
-                       if (intr & WL1251_ACX_INTR_EVENT_A)
-                               wl1251_event_handle(wl, 0);
-                       else
-                               wl1251_event_handle(wl, 1);
+               if (intr & WL1251_ACX_INTR_EVENT_A) {
+                       wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT_A");
+                       wl1251_event_handle(wl, 0);
+               }
+
+               if (intr & WL1251_ACX_INTR_EVENT_B) {
+                       wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT_B");
+                       wl1251_event_handle(wl, 1);
                }
 
                if (intr & WL1251_ACX_INTR_INIT_COMPLETE)
@@ -325,64 +328,97 @@ out:
        mutex_unlock(&wl->mutex);
 }
 
-static int wl1251_join(struct wl1251 *wl, u8 bss_type, u8 channel,
-                      u16 beacon_interval, u8 dtim_period)
+/* Atomic irq handler */
+void wl1251_irq(struct wl1251 *wl)
 {
-       int ret;
+       unsigned long flags;
 
-       ret = wl1251_acx_frame_rates(wl, DEFAULT_HW_GEN_TX_RATE,
-                                    DEFAULT_HW_GEN_MODULATION_TYPE,
-                                    wl->tx_mgmt_frm_rate,
-                                    wl->tx_mgmt_frm_mod);
-       if (ret < 0)
-               goto out;
+       wl1251_debug(DEBUG_IRQ, "IRQ");
 
+       /* complete the ELP completion */
+       spin_lock_irqsave(&wl->wl_lock, flags);
+       if (wl->elp_compl) {
+               complete(wl->elp_compl);
+               wl->elp_compl = NULL;
+       }
+       spin_unlock_irqrestore(&wl->wl_lock, flags);
 
-       ret = wl1251_cmd_join(wl, bss_type, channel, beacon_interval,
-                             dtim_period);
-       if (ret < 0)
-               goto out;
+       ieee80211_queue_work(wl->hw, &wl->irq_work);
+}
+EXPORT_SYMBOL_GPL(wl1251_irq);
+
+static void wl1251_convert_filters(struct wl1251 *wl,
+                                 unsigned int mac80211_filters)
+{
+       wl->rx_config = WL1251_DEFAULT_RX_CONFIG;
+       wl->rx_filter = WL1251_DEFAULT_RX_FILTER;
+
+       if (mac80211_filters & FIF_PROMISC_IN_BSS) {
+               wl->rx_config |= CFG_BSSID_FILTER_EN;
+               wl->rx_config &= ~CFG_UNI_FILTER_EN;
+       }
+       if (mac80211_filters & FIF_ALLMULTI)
+               /*
+                * CFG_MC_FILTER_EN in rx_config needs to be 0 to receive
+                * all multicast frames
+                */
+               wl->rx_config &= ~CFG_MC_FILTER_EN;
+       if (mac80211_filters & FIF_FCSFAIL)
+               wl->rx_filter |= CFG_RX_FCS_ERROR;
+       if (mac80211_filters & FIF_BCN_PRBRESP_PROMISC) {
+               wl->rx_config &= ~CFG_BSSID_FILTER_EN;
+               wl->rx_config &= ~CFG_SSID_FILTER_EN;
+       }
+       if (mac80211_filters & FIF_CONTROL)
+               wl->rx_filter |= CFG_RX_CTL_EN;
+       if (mac80211_filters & FIF_OTHER_BSS)
+               wl->rx_config &= ~CFG_BSSID_FILTER_EN;
+       if (mac80211_filters & FIF_PROBE_REQ)
+               wl->rx_filter |= CFG_RX_PREQ_EN;
 
        /*
-        * FIXME: we should wait for JOIN_EVENT_COMPLETE_ID but to simplify
-        * locking we just sleep instead, for now
+        * If we have no associated BSSID, the filter must be disabled
+        * or else association will break
         */
-       msleep(10);
+       if (is_zero_ether_addr(wl->bssid))
+               wl->rx_config &= ~CFG_BSSID_FILTER_EN;
 
-out:
-       return ret;
+       wl1251_debug(DEBUG_FILTERS, "rx_config %04x, rx_filter %04x",
+               wl->rx_config, wl->rx_filter);
 }
 
-static void wl1251_filter_work(struct work_struct *work)
+static int wl1251_join(struct wl1251 *wl, u8 bss_type, u8 channel,
+                      u16 beacon_interval, u8 dtim_period)
 {
-       struct wl1251 *wl =
-               container_of(work, struct wl1251, filter_work);
        int ret;
 
-       mutex_lock(&wl->mutex);
-
-       if (wl->state == WL1251_STATE_OFF)
+       ret = wl1251_acx_frame_rates(wl, DEFAULT_HW_GEN_TX_RATE,
+                                    DEFAULT_HW_GEN_MODULATION_TYPE,
+                                    wl->tx_mgmt_frm_rate,
+                                    wl->tx_mgmt_frm_mod);
+       if (ret < 0)
                goto out;
 
-       ret = wl1251_ps_elp_wakeup(wl);
+       /* Join uses filters, update them */
+       wl1251_convert_filters(wl, wl->mac80211_filters);
+
+       ret = wl1251_cmd_join(wl, bss_type, channel, beacon_interval,
+                             dtim_period);
        if (ret < 0)
                goto out;
 
-       ret = wl1251_join(wl, wl->bss_type, wl->channel, wl->beacon_int,
-                         wl->dtim_period);
+       ret = wl1251_event_wait(wl, JOIN_EVENT_COMPLETE_ID, 100);
        if (ret < 0)
-               goto out_sleep;
-
-out_sleep:
-       wl1251_ps_elp_sleep(wl);
+               wl1251_warning("join timeout");
 
 out:
-       mutex_unlock(&wl->mutex);
+       return ret;
 }
 
 static int wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 {
        struct wl1251 *wl = hw->priv;
+       unsigned long flags;
 
        skb_queue_tail(&wl->tx_queue, skb);
 
@@ -397,16 +433,13 @@ static int wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
         * The workqueue is slow to process the tx_queue and we need stop
         * the queue here, otherwise the queue will get too long.
         */
-       if (skb_queue_len(&wl->tx_queue) >= WL1251_TX_QUEUE_MAX_LENGTH) {
+       if (skb_queue_len(&wl->tx_queue) >= WL1251_TX_QUEUE_HIGH_WATERMARK) {
                wl1251_debug(DEBUG_TX, "op_tx: tx_queue full, stop queues");
-               ieee80211_stop_queues(wl->hw);
 
-               /*
-                * FIXME: this is racy, the variable is not properly
-                * protected. Maybe fix this by removing the stupid
-                * variable altogether and checking the real queue state?
-                */
+               spin_lock_irqsave(&wl->wl_lock, flags);
+               ieee80211_stop_queues(wl->hw);
                wl->tx_queue_stopped = true;
+               spin_unlock_irqrestore(&wl->wl_lock, flags);
        }
 
        return NETDEV_TX_OK;
@@ -415,6 +448,7 @@ static int wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
 static int wl1251_op_start(struct ieee80211_hw *hw)
 {
        struct wl1251 *wl = hw->priv;
+       struct wiphy *wiphy = hw->wiphy;
        int ret = 0;
 
        wl1251_debug(DEBUG_MAC80211, "mac80211 start");
@@ -448,6 +482,10 @@ static int wl1251_op_start(struct ieee80211_hw *hw)
 
        wl1251_info("firmware booted (%s)", wl->fw_ver);
 
+       /* update hw/fw version info in wiphy struct */
+       wiphy->hw_version = wl->chip_id;
+       strncpy(wiphy->fw_version, wl->fw_ver, sizeof(wiphy->fw_version));
+
 out:
        if (ret < 0)
                wl1251_power_off(wl);
@@ -484,7 +522,6 @@ static void wl1251_op_stop(struct ieee80211_hw *hw)
 
        cancel_work_sync(&wl->irq_work);
        cancel_work_sync(&wl->tx_work);
-       cancel_work_sync(&wl->filter_work);
 
        mutex_lock(&wl->mutex);
 
@@ -503,7 +540,7 @@ static void wl1251_op_stop(struct ieee80211_hw *hw)
        wl->rx_last_id = 0;
        wl->next_tx_complete = 0;
        wl->elp = false;
-       wl->psm = 0;
+       wl->station_mode = STATION_ACTIVE_MODE;
        wl->tx_queue_stopped = false;
        wl->power_level = WL1251_DEFAULT_POWER_LEVEL;
        wl->channel = WL1251_DEFAULT_CHANNEL;
@@ -609,8 +646,19 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
        if (channel != wl->channel) {
                wl->channel = channel;
 
-               ret = wl1251_join(wl, wl->bss_type, wl->channel,
-                                 wl->beacon_int, wl->dtim_period);
+               /*
+                * Use ENABLE_RX command for channel switching when no
+                * interface is present (monitor mode only).
+                * This leaves the tx path disabled in firmware, whereas
+                * the usual JOIN command seems to transmit some frames
+                * at firmware level.
+                */
+               if (wl->vif == NULL) {
+                       ret = wl1251_cmd_data_path_rx(wl, wl->channel, 1);
+               } else {
+                       ret = wl1251_join(wl, wl->bss_type, wl->channel,
+                                         wl->beacon_int, wl->dtim_period);
+               }
                if (ret < 0)
                        goto out_sleep;
        }
@@ -637,7 +685,7 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
 
                wl->psm_requested = false;
 
-               if (wl->psm) {
+               if (wl->station_mode != STATION_ACTIVE_MODE) {
                        ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE);
                        if (ret < 0)
                                goto out_sleep;
@@ -666,15 +714,18 @@ out:
                                  FIF_FCSFAIL | \
                                  FIF_BCN_PRBRESP_PROMISC | \
                                  FIF_CONTROL | \
-                                 FIF_OTHER_BSS)
+                                 FIF_OTHER_BSS | \
+                                 FIF_PROBE_REQ)
 
 static void wl1251_op_configure_filter(struct ieee80211_hw *hw,
                                       unsigned int changed,
                                       unsigned int *total,u64 multicast)
 {
        struct wl1251 *wl = hw->priv;
+       int ret;
 
        wl1251_debug(DEBUG_MAC80211, "mac80211 configure filter");
+       wl1251_debug(DEBUG_FILTERS, "configure filter %x %x", *total, changed);
 
        *total &= WL1251_SUPPORTED_FILTERS;
        changed &= WL1251_SUPPORTED_FILTERS;
@@ -683,38 +734,25 @@ static void wl1251_op_configure_filter(struct ieee80211_hw *hw,
                /* no filters which we support changed */
                return;
 
-       /* FIXME: wl->rx_config and wl->rx_filter are not protected */
+       mutex_lock(&wl->mutex);
 
-       wl->rx_config = WL1251_DEFAULT_RX_CONFIG;
-       wl->rx_filter = WL1251_DEFAULT_RX_FILTER;
+       wl->mac80211_filters = *total;
+       wl1251_convert_filters(wl, *total);
 
-       if (*total & FIF_PROMISC_IN_BSS) {
-               wl->rx_config |= CFG_BSSID_FILTER_EN;
-               wl->rx_config |= CFG_RX_ALL_GOOD;
-       }
-       if (*total & FIF_ALLMULTI)
-               /*
-                * CFG_MC_FILTER_EN in rx_config needs to be 0 to receive
-                * all multicast frames
-                */
-               wl->rx_config &= ~CFG_MC_FILTER_EN;
-       if (*total & FIF_FCSFAIL)
-               wl->rx_filter |= CFG_RX_FCS_ERROR;
-       if (*total & FIF_BCN_PRBRESP_PROMISC) {
-               wl->rx_config &= ~CFG_BSSID_FILTER_EN;
-               wl->rx_config &= ~CFG_SSID_FILTER_EN;
-       }
-       if (*total & FIF_CONTROL)
-               wl->rx_filter |= CFG_RX_CTL_EN;
-       if (*total & FIF_OTHER_BSS)
-               wl->rx_filter &= ~CFG_BSSID_FILTER_EN;
+       if (wl->state == WL1251_STATE_OFF)
+               goto out;
 
-       /*
-        * FIXME: workqueues need to be properly cancelled on stop(), for
-        * now let's just disable changing the filter settings. They will
-        * be updated any on config().
-        */
-       /* schedule_work(&wl->filter_work); */
+       ret = wl1251_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       /* apply configured filters */
+       wl1251_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
+
+       wl1251_ps_elp_sleep(wl);
+
+out:
+       mutex_unlock(&wl->mutex);
 }
 
 /* HW encryption */
@@ -908,6 +946,7 @@ static int wl1251_op_hw_scan(struct ieee80211_hw *hw,
        ret = wl1251_cmd_scan(wl, ssid, ssid_len, req->channels,
                              req->n_channels, WL1251_SCAN_NUM_PROBES);
        if (ret < 0) {
+               wl1251_debug(DEBUG_SCAN, "scan failed %d", ret);
                wl->scanning = false;
                goto out_sleep;
        }
@@ -1041,6 +1080,9 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
 
        if (changed & BSS_CHANGED_BEACON) {
                beacon = ieee80211_beacon_get(hw, vif);
+               if (!beacon)
+                       goto out_sleep;
+
                ret = wl1251_cmd_template_set(wl, CMD_BEACON, beacon->data,
                                              beacon->len);
 
@@ -1298,7 +1340,8 @@ int wl1251_init_ieee80211(struct wl1251 *wl)
                IEEE80211_HW_BEACON_FILTER |
                IEEE80211_HW_SUPPORTS_UAPSD;
 
-       wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+       wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+                                        BIT(NL80211_IFTYPE_ADHOC);
        wl->hw->wiphy->max_scan_ssids = 1;
        wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1251_band_2ghz;
 
@@ -1343,7 +1386,6 @@ struct ieee80211_hw *wl1251_alloc_hw(void)
 
        skb_queue_head_init(&wl->tx_queue);
 
-       INIT_WORK(&wl->filter_work, wl1251_filter_work);
        INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work);
        wl->channel = WL1251_DEFAULT_CHANNEL;
        wl->scanning = false;
@@ -1356,7 +1398,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void)
        wl->rx_config = WL1251_DEFAULT_RX_CONFIG;
        wl->rx_filter = WL1251_DEFAULT_RX_FILTER;
        wl->elp = false;
-       wl->psm = 0;
+       wl->station_mode = STATION_ACTIVE_MODE;
        wl->psm_requested = false;
        wl->tx_queue_stopped = false;
        wl->power_level = WL1251_DEFAULT_POWER_LEVEL;