#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);
mutex_unlock(&wl->mutex);
}
+/* Atomic irq handler */
+void wl1251_irq(struct wl1251 *wl)
+{
+ unsigned long flags;
+
+ 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);
+
+ 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;
+
+ /*
+ * If we have no associated BSSID, the filter must be disabled
+ * or else association will break
+ */
+ if (is_zero_ether_addr(wl->bssid))
+ wl->rx_config &= ~CFG_BSSID_FILTER_EN;
+
+ wl1251_debug(DEBUG_FILTERS, "rx_config %04x, rx_filter %04x",
+ wl->rx_config, wl->rx_filter);
+}
+
static int wl1251_join(struct wl1251 *wl, u8 bss_type, u8 channel,
u16 beacon_interval, u8 dtim_period)
{
if (ret < 0)
goto out;
+ /* Join uses filters, update them */
+ wl1251_convert_filters(wl, wl->mac80211_filters);
ret = wl1251_cmd_join(wl, bss_type, channel, beacon_interval,
dtim_period);
return ret;
}
-static void wl1251_filter_work(struct work_struct *work)
-{
- struct wl1251 *wl =
- container_of(work, struct wl1251, filter_work);
- int ret;
-
- mutex_lock(&wl->mutex);
-
- if (wl->state == WL1251_STATE_OFF)
- goto out;
-
- ret = wl1251_ps_elp_wakeup(wl);
- if (ret < 0)
- goto out;
-
- ret = wl1251_join(wl, wl->bss_type, wl->channel, wl->beacon_int,
- wl->dtim_period);
- if (ret < 0)
- goto out_sleep;
-
-out_sleep:
- wl1251_ps_elp_sleep(wl);
-
-out:
- mutex_unlock(&wl->mutex);
-}
-
static int wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct wl1251 *wl = hw->priv;
cancel_work_sync(&wl->irq_work);
cancel_work_sync(&wl->tx_work);
- cancel_work_sync(&wl->filter_work);
mutex_lock(&wl->mutex);
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;
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;
}
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;
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;
/* 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 */
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;
}
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;
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;
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;