X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?p=pandora-kernel.git;a=blobdiff_plain;f=drivers%2Fnet%2Fwireless%2Fwl1251%2Fmain.c;h=b012867ad6c55b8151a753cdb5c47c1a9b467aba;hp=84a5bc989a9e324301b6ce46214cc9de4ed814fd;hb=e466b34681a0a07960cbf43791da3b8e042a05e0;hpb=ce8f8fd62a8b1b0987d1acb4803c2f91e46acec9 diff --git a/drivers/net/wireless/wl1251/main.c b/drivers/net/wireless/wl1251/main.c index 84a5bc989a9e..b012867ad6c5 100644 --- a/drivers/net/wireless/wl1251/main.c +++ b/drivers/net/wireless/wl1251/main.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "wl1251.h" #include "wl12xx_80211.h" @@ -42,6 +43,13 @@ #include "debugfs.h" #include "boot.h" +static bool use_fw_ps = true; +module_param(use_fw_ps, bool, 0644); +MODULE_PARM_DESC(use_fw_ps, "Enable powersave once and leave it for chip's " + "firmware to manage. When disabled, mac80211 " + "will toggle powersave on tx activity instead. " + "Default: y/Y/1"); + void wl1251_enable_interrupts(struct wl1251 *wl) { wl->if_ops->enable_irq(wl); @@ -212,12 +220,11 @@ out: return ret; } -#define WL1251_IRQ_LOOP_COUNT 10 -static void wl1251_irq_work(struct work_struct *work) +#define WL1251_IRQ_LOOP_COUNT 100 +irqreturn_t wl1251_irq(int irq, void *cookie) { u32 intr, ctr = WL1251_IRQ_LOOP_COUNT; - struct wl1251 *wl = - container_of(work, struct wl1251, irq_work); + struct wl1251 *wl = cookie; int ret; mutex_lock(&wl->mutex); @@ -279,9 +286,18 @@ static void wl1251_irq_work(struct work_struct *work) goto out_sleep; } + if (intr & WL1251_ACX_INTR_TX_RESULT) { + wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT"); + wl1251_tx_complete(wl); + } + if (intr & WL1251_ACX_INTR_RX0_DATA) { wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA"); wl1251_rx(wl); + + if ((intr & WL1251_ACX_INTR_RX1_DATA) + && skb_queue_len(&wl->tx_queue) > 0) + wl1251_tx_work_unlocked(wl, false); } if (intr & WL1251_ACX_INTR_RX1_DATA) { @@ -289,11 +305,6 @@ static void wl1251_irq_work(struct work_struct *work) wl1251_rx(wl); } - if (intr & WL1251_ACX_INTR_TX_RESULT) { - wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT"); - wl1251_tx_complete(wl); - } - if (intr & WL1251_ACX_INTR_EVENT_A) { wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT_A"); wl1251_event_handle(wl, 0); @@ -308,6 +319,9 @@ static void wl1251_irq_work(struct work_struct *work) wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_INIT_COMPLETE"); + if (skb_queue_len(&wl->tx_queue) > 0) + wl1251_tx_work_unlocked(wl, false); + if (--ctr == 0) break; @@ -320,6 +334,16 @@ out_sleep: out: mutex_unlock(&wl->mutex); + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(wl1251_irq); + +static void wl1251_irq_work(struct work_struct *work) +{ + struct wl1251 *wl = + container_of(work, struct wl1251, irq_work); + + wl1251_irq(0, wl); } static int wl1251_join(struct wl1251 *wl, u8 bss_type, u8 channel, @@ -350,6 +374,9 @@ static int wl1251_join(struct wl1251 *wl, u8 bss_type, u8 channel, if (ret < 0) wl1251_warning("join timeout"); + wl1251_no_ps_event(wl); + wl->bss_lost = 0; + out: return ret; } @@ -457,6 +484,7 @@ static void wl1251_op_stop(struct ieee80211_hw *hw) cancel_work_sync(&wl->irq_work); cancel_work_sync(&wl->tx_work); + cancel_delayed_work_sync(&wl->ps_work); cancel_delayed_work_sync(&wl->elp_work); mutex_lock(&wl->mutex); @@ -481,6 +509,9 @@ static void wl1251_op_stop(struct ieee80211_hw *hw) wl->power_level = WL1251_DEFAULT_POWER_LEVEL; wl->rssi_thold = 0; wl->channel = WL1251_DEFAULT_CHANNEL; + wl->monitor_present = false; + wl->joined = false; + wl->long_doze_mode_set = false; wl1251_debugfs_reset(wl); @@ -537,9 +568,40 @@ static void wl1251_op_remove_interface(struct ieee80211_hw *hw, mutex_lock(&wl->mutex); wl1251_debug(DEBUG_MAC80211, "mac80211 remove interface"); wl->vif = NULL; + memset(wl->bssid, 0, ETH_ALEN); mutex_unlock(&wl->mutex); } +static int wl1251_build_null_data(struct wl1251 *wl) +{ + struct sk_buff *skb = NULL; + int size; + void *ptr; + int ret = -ENOMEM; + + + if (wl->bss_type == BSS_TYPE_IBSS) { + size = sizeof(struct wl12xx_null_data_template); + ptr = NULL; + } else { + skb = ieee80211_nullfunc_get(wl->hw, wl->vif); + if (!skb) + goto out; + size = skb->len; + ptr = skb->data; + } + + ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, ptr, size); + +out: + dev_kfree_skb(skb); + if (ret) + wl1251_warning("cmd buld null data failed %d", ret); + + return ret; + +} + static int wl1251_build_qos_null_data(struct wl1251 *wl) { struct ieee80211_qos_hdr template; @@ -561,6 +623,99 @@ static int wl1251_build_qos_null_data(struct wl1251 *wl) sizeof(template)); } +static void wl1251_ps_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wl1251 *wl; + unsigned long diff, wait; + bool need_ps; + bool have_ps; + int ret; + int i; + + dwork = container_of(work, struct delayed_work, work); + wl = container_of(dwork, struct wl1251, ps_work); + + mutex_lock(&wl->mutex); + + /* don't change PS modes while still transitioning, to avoid possbile + * fw bugs (it normally takes ~130ms to enable and ~10ms to disable) */ + if (wl->ps_transitioning) { + diff = jiffies - wl->ps_change_jiffies; + if (diff > msecs_to_jiffies(500)) { + wl1251_error("PS change taking too long: %lu", diff); + wl->ps_transitioning = false; + } else { + //wl1251_error("PS still transitioning"); + ieee80211_queue_delayed_work(wl->hw, &wl->ps_work, + msecs_to_jiffies(50)); + goto out; + } + } + + have_ps = wl->station_mode == STATION_POWER_SAVE_MODE; + need_ps = wl->psm_requested && !wl->bss_lost + && wl->rate < wl->ps_rate_threshold; + + if (need_ps == have_ps) { + //wl1251_info("ps: already in mode %d", have_ps); + goto out; + } + + /* don't enter PS if there was recent activity */ + if (need_ps) { + wait = 0; + + diff = jiffies - wl->last_no_ps_jiffies[1]; + if (diff < msecs_to_jiffies(1000)) + wait = msecs_to_jiffies(1000) - diff + 1; + + diff = jiffies - wl->last_no_ps_jiffies[0]; + if (diff < msecs_to_jiffies(3000)) + wait += msecs_to_jiffies(1000); + + for (i = 0; i < ARRAY_SIZE(wl->tx_frames); i++) { + if (wl->tx_frames[i] != NULL) { + //wl1251_error(" frm %d busy", i); + if (wait < msecs_to_jiffies(50)) + wait = msecs_to_jiffies(50); + break; + } + } + + if (wait > 0) { + ieee80211_queue_delayed_work(wl->hw, &wl->ps_work, wait); + goto out; + } + } + + ret = wl1251_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + if (need_ps) { + wl1251_acx_wr_tbtt_and_dtim(wl, wl->beacon_int, + wl->dtim_period); + } + ret = wl1251_ps_set_mode(wl, + need_ps ? STATION_POWER_SAVE_MODE : STATION_ACTIVE_MODE); + if (ret < 0) + goto out_sleep; + + // wl1251_info("psm %d, r %u", need_ps, wl->rate); + +out_sleep: + wl1251_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); +} + +static bool wl1251_can_do_pm(struct ieee80211_conf *conf, struct wl1251 *wl) +{ + return (conf->flags & IEEE80211_CONF_PS) && !wl->monitor_present; +} + static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) { struct wl1251 *wl = hw->priv; @@ -569,8 +724,10 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) channel = ieee80211_frequency_to_channel(conf->channel->center_freq); - wl1251_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d", + wl1251_debug(DEBUG_MAC80211, + "mac80211 config ch %d monitor %s psm %s power %d", channel, + conf->flags & IEEE80211_CONF_MONITOR ? "on" : "off", conf->flags & IEEE80211_CONF_PS ? "on" : "off", conf->power_level); @@ -580,42 +737,62 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) if (ret < 0) goto out; + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + u32 mode; + + if (conf->flags & IEEE80211_CONF_MONITOR) { + wl->monitor_present = true; + mode = DF_SNIFF_MODE_ENABLE | DF_ENCRYPTION_DISABLE; + } else { + wl->monitor_present = false; + mode = 0; + } + + ret = wl1251_acx_feature_cfg(wl, mode); + if (ret < 0) + goto out_sleep; + } + 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. + * + * Note that bss_type must be BSS_TYPE_STA_BSS, also at least + * one join has to be performed before CMD_ENABLE_RX can + * properly switch channels (join will be done by CONF_IDLE). + */ + if (wl->vif == NULL) { + wl->bss_type = BSS_TYPE_STA_BSS; + wl->joined = false; + 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; } - if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) { + if (wl1251_can_do_pm(conf, wl) && !wl->psm_requested) { wl1251_debug(DEBUG_PSM, "psm enabled"); wl->psm_requested = true; wl->dtim_period = conf->ps_dtim_period; - ret = wl1251_acx_wr_tbtt_and_dtim(wl, wl->beacon_int, - wl->dtim_period); - - /* - * mac80211 enables PSM only if we're already associated. - */ - ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE); - if (ret < 0) - goto out_sleep; - } else if (!(conf->flags & IEEE80211_CONF_PS) && - wl->psm_requested) { + ieee80211_queue_delayed_work(wl->hw, &wl->ps_work, 0); + } else if (!wl1251_can_do_pm(conf, wl) && wl->psm_requested) { wl1251_debug(DEBUG_PSM, "psm disabled"); wl->psm_requested = false; - if (wl->station_mode != STATION_ACTIVE_MODE) { - ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE); - if (ret < 0) - goto out_sleep; - } + ieee80211_queue_delayed_work(wl->hw, &wl->ps_work, 0); } if (changed & IEEE80211_CONF_CHANGE_IDLE) { @@ -656,6 +833,44 @@ out: return ret; } +struct wl1251_filter_params { + bool enabled; + int mc_list_length; + u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN]; +}; + +static u64 wl1251_op_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + struct wl1251_filter_params *fp; + struct netdev_hw_addr *ha; + struct wl1251 *wl = hw->priv; + + if (unlikely(wl->state == WL1251_STATE_OFF)) + return 0; + + fp = kzalloc(sizeof(*fp), GFP_ATOMIC); + if (!fp) { + wl1251_error("Out of memory setting filters."); + return 0; + } + + /* update multicast filtering parameters */ + fp->mc_list_length = 0; + if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) { + fp->enabled = false; + } else { + fp->enabled = true; + netdev_hw_addr_list_for_each(ha, mc_list) { + memcpy(fp->mc_list[fp->mc_list_length], + ha->addr, ETH_ALEN); + fp->mc_list_length++; + } + } + + return (u64)(unsigned long)fp; +} + #define WL1251_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \ FIF_ALLMULTI | \ FIF_FCSFAIL | \ @@ -666,8 +881,9 @@ out: static void wl1251_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, - unsigned int *total,u64 multicast) + unsigned int *total, u64 multicast) { + struct wl1251_filter_params *fp = (void *)(unsigned long)multicast; struct wl1251 *wl = hw->priv; int ret; @@ -676,9 +892,11 @@ static void wl1251_op_configure_filter(struct ieee80211_hw *hw, *total &= WL1251_SUPPORTED_FILTERS; changed &= WL1251_SUPPORTED_FILTERS; - if (changed == 0) + if (changed == 0) { /* no filters which we support changed */ + kfree(fp); return; + } mutex_lock(&wl->mutex); @@ -715,6 +933,15 @@ static void wl1251_op_configure_filter(struct ieee80211_hw *hw, if (ret < 0) goto out; + if (*total & FIF_ALLMULTI || *total & FIF_PROMISC_IN_BSS) + ret = wl1251_acx_group_address_tbl(wl, false, NULL, 0); + else if (fp) + ret = wl1251_acx_group_address_tbl(wl, fp->enabled, + fp->mc_list, + fp->mc_list_length); + if (ret < 0) + goto out; + /* send filters to firmware */ wl1251_acx_rx_config(wl, wl->rx_config, wl->rx_filter); @@ -722,6 +949,7 @@ static void wl1251_op_configure_filter(struct ieee80211_hw *hw, out: mutex_unlock(&wl->mutex); + kfree(fp); } /* HW encryption */ @@ -801,12 +1029,12 @@ static int wl1251_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, mutex_lock(&wl->mutex); - ret = wl1251_ps_elp_wakeup(wl); - if (ret < 0) - goto out_unlock; - switch (cmd) { case SET_KEY: + if (wl->monitor_present) { + ret = -EOPNOTSUPP; + goto out_unlock; + } wl_cmd->key_action = KEY_ADD_OR_REPLACE; break; case DISABLE_KEY: @@ -817,6 +1045,10 @@ static int wl1251_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, break; } + ret = wl1251_ps_elp_wakeup(wl); + if (ret < 0) + goto out_unlock; + ret = wl1251_set_key_type(wl, wl_cmd, cmd, key, addr); if (ret < 0) { wl1251_error("Set KEY type failed"); @@ -917,6 +1149,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; } @@ -960,6 +1193,8 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, { struct wl1251 *wl = hw->priv; struct sk_buff *beacon, *skb; + bool do_join = false; + bool enable; int ret; wl1251_debug(DEBUG_MAC80211, "mac80211 bss info changed"); @@ -980,28 +1215,20 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, wl->rssi_thold = bss_conf->cqm_rssi_thold; } - if (changed & BSS_CHANGED_BSSID) { + if ((changed & BSS_CHANGED_BSSID) && + memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) { memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); - skb = ieee80211_nullfunc_get(wl->hw, wl->vif); - if (!skb) - goto out_sleep; - - ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, - skb->data, skb->len); - dev_kfree_skb(skb); - if (ret < 0) - goto out_sleep; - - ret = wl1251_build_qos_null_data(wl); - if (ret < 0) - goto out; + if (!is_zero_ether_addr(wl->bssid)) { + ret = wl1251_build_null_data(wl); + if (ret < 0) + goto out_sleep; - if (wl->bss_type != BSS_TYPE_IBSS) { - ret = wl1251_join(wl, wl->bss_type, wl->channel, - wl->beacon_int, wl->dtim_period); + ret = wl1251_build_qos_null_data(wl); if (ret < 0) goto out_sleep; + + do_join = true; } } @@ -1058,6 +1285,17 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, } } + if (changed & BSS_CHANGED_ARP_FILTER) { + __be32 addr = bss_conf->arp_addr_list[0]; + WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS); + + enable = bss_conf->arp_addr_cnt == 1 && bss_conf->assoc; + wl1251_acx_arp_ip_filter(wl, enable, addr); + + if (ret < 0) + goto out_sleep; + } + if (changed & BSS_CHANGED_BEACON) { beacon = ieee80211_beacon_get(hw, vif); if (!beacon) @@ -1079,9 +1317,12 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; - ret = wl1251_join(wl, wl->bss_type, wl->beacon_int, - wl->channel, wl->dtim_period); + do_join = true; + } + if (do_join) { + ret = wl1251_join(wl, wl->bss_type, wl->channel, + wl->beacon_int, wl->dtim_period); if (ret < 0) goto out_sleep; } @@ -1228,6 +1469,7 @@ static const struct ieee80211_ops wl1251_ops = { .add_interface = wl1251_op_add_interface, .remove_interface = wl1251_op_remove_interface, .config = wl1251_op_config, + .prepare_multicast = wl1251_op_prepare_multicast, .configure_filter = wl1251_op_configure_filter, .tx = wl1251_op_tx, .set_key = wl1251_op_set_key, @@ -1366,6 +1608,9 @@ int wl1251_init_ieee80211(struct wl1251 *wl) IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_SUPPORTS_CQM_RSSI; + if (use_fw_ps) + wl->hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_PS; + wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); wl->hw->wiphy->max_scan_ssids = 1; @@ -1414,9 +1659,13 @@ struct ieee80211_hw *wl1251_alloc_hw(void) skb_queue_head_init(&wl->tx_queue); + INIT_DELAYED_WORK(&wl->ps_work, wl1251_ps_work); INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work); wl->channel = WL1251_DEFAULT_CHANNEL; + wl->monitor_present = false; + wl->joined = false; wl->scanning = false; + wl->bss_type = MAX_BSS_TYPE; wl->default_key = 0; wl->listen_int = 1; wl->rx_counter = 0; @@ -1434,6 +1683,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void) wl->beacon_int = WL1251_DEFAULT_BEACON_INT; wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD; wl->vif = NULL; + wl->ps_rate_threshold = 100000; for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) wl->tx_frames[i] = NULL;