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=7fcbb799bf9a428bb83ebd9efd57b30af343a176;hp=ba3268ea81fe57563c9f0f95a293773c21f68ffe;hb=5a31571ca0a6d63dc9084cfea76f06b02580c458;hpb=d6748066ad0e8b2514545998f8367ebb3906f299 diff --git a/drivers/net/wireless/wl1251/main.c b/drivers/net/wireless/wl1251/main.c index ba3268ea81fe..7fcbb799bf9a 100644 --- a/drivers/net/wireless/wl1251/main.c +++ b/drivers/net/wireless/wl1251/main.c @@ -212,12 +212,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); @@ -308,6 +307,22 @@ static void wl1251_irq_work(struct work_struct *work) wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_INIT_COMPLETE"); + while (skb_queue_len(&wl->tx_queue) > 0 + && wl1251_tx_path_status(wl) == 0) { + + struct sk_buff *skb = skb_dequeue(&wl->tx_queue); + if (skb == NULL) + goto out_sleep; + + ret = wl1251_tx_frame(wl, skb); + if (ret == -EBUSY) { + skb_queue_head(&wl->tx_queue, skb); + break; + } else if (ret < 0) { + dev_kfree_skb(skb); + } + } + if (--ctr == 0) break; @@ -320,6 +335,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, @@ -334,6 +359,12 @@ static int wl1251_join(struct wl1251 *wl, u8 bss_type, u8 channel, if (ret < 0) goto out; + /* + * Join command applies filters, and if we are not associated, + * BSSID filter must be disabled for association to work. + */ + if (is_zero_ether_addr(wl->bssid)) + wl->rx_config &= ~CFG_BSSID_FILTER_EN; ret = wl1251_cmd_join(wl, bss_type, channel, beacon_interval, dtim_period); @@ -348,33 +379,6 @@ out: 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 void wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct wl1251 *wl = hw->priv; @@ -478,7 +482,8 @@ 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); + cancel_delayed_work_sync(&wl->ps_work); + cancel_delayed_work_sync(&wl->elp_work); mutex_lock(&wl->mutex); @@ -582,6 +587,92 @@ 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; + } + } + + need_ps = wl->psm_requested && !wl->bss_lost; + have_ps = wl->station_mode == STATION_POWER_SAVE_MODE; + + 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_io_jiffies; + if (diff < msecs_to_jiffies(150)) { + //wl1251_info("ps: postponed psm, j %ld", diff); + wait = msecs_to_jiffies(150) - diff + 1; + } + + 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, j %ld, d %ld", need_ps, + // jiffies - wl->last_io_jiffies); + +out_sleep: + wl1251_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); +} + static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) { struct wl1251 *wl = hw->priv; @@ -617,26 +708,14 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) 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; + ieee80211_queue_delayed_work(wl->hw, &wl->ps_work, 0); } else if (!(conf->flags & IEEE80211_CONF_PS) && 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) { @@ -648,6 +727,11 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE); if (ret < 0) goto out_sleep; + + ret = wl1251_event_wait(wl, PS_REPORT_EVENT_ID, 100); + if (ret < 0) + wl1251_error("error waiting for wakeup"); + ret = wl1251_join(wl, wl->bss_type, wl->channel, wl->beacon_int, wl->dtim_period); if (ret < 0) @@ -677,13 +761,15 @@ 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"); @@ -694,7 +780,7 @@ 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; @@ -717,15 +803,25 @@ static void wl1251_op_configure_filter(struct ieee80211_hw *hw, } if (*total & FIF_CONTROL) wl->rx_filter |= CFG_RX_CTL_EN; - if (*total & FIF_OTHER_BSS) - wl->rx_filter &= ~CFG_BSSID_FILTER_EN; + if (*total & FIF_OTHER_BSS || is_zero_ether_addr(wl->bssid)) + wl->rx_config &= ~CFG_BSSID_FILTER_EN; + if (*total & FIF_PROBE_REQ) + wl->rx_filter |= CFG_RX_PREQ_EN; - /* - * 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); */ + if (wl->state == WL1251_STATE_OFF) + goto out; + + ret = wl1251_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + /* send filters to firmware */ + wl1251_acx_rx_config(wl, wl->rx_config, wl->rx_filter); + + wl1251_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); } /* HW encryption */ @@ -964,6 +1060,7 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, { struct wl1251 *wl = hw->priv; struct sk_buff *beacon, *skb; + bool enable; int ret; wl1251_debug(DEBUG_MAC80211, "mac80211 bss info changed"); @@ -1062,6 +1159,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) @@ -1302,6 +1410,34 @@ static int wl1251_read_eeprom_mac(struct wl1251 *wl) return 0; } +/* temporary (?) hack for EEPROM dumping + * (it seems this can only be done before fw is running) */ +static int wl1251_dump_eeprom(struct wl1251 *wl) +{ + int ret; + + wl1251_set_partition(wl, 0, 0, REGISTERS_BASE, REGISTERS_DOWN_SIZE); + + wl->eeprom_dump = kzalloc(1024, GFP_KERNEL); + if (wl->eeprom_dump == NULL) { + ret = -ENOMEM; + goto out; + } + + ret = wl1251_read_eeprom(wl, 0, wl->eeprom_dump, 1024); + if (ret != 0) { + wl1251_error("eeprom dump failed: %d", ret); + kfree(wl->eeprom_dump); + wl->eeprom_dump = NULL; + goto out; + } + + wl1251_info("eeprom dumped."); + +out: + return ret; +} + static int wl1251_register_hw(struct wl1251 *wl) { int ret; @@ -1351,6 +1487,8 @@ int wl1251_init_ieee80211(struct wl1251 *wl) if (wl->use_eeprom) wl1251_read_eeprom_mac(wl); + if (wl->dump_eeprom) + wl1251_dump_eeprom(wl); ret = wl1251_register_hw(wl); if (ret) @@ -1388,7 +1526,7 @@ 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->ps_work, wl1251_ps_work); INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work); wl->channel = WL1251_DEFAULT_CHANNEL; wl->scanning = false; @@ -1460,6 +1598,9 @@ int wl1251_free_hw(struct wl1251 *wl) ieee80211_free_hw(wl->hw); + if (wl->eeprom_dump != NULL) + kfree(wl->eeprom_dump); + return 0; } EXPORT_SYMBOL_GPL(wl1251_free_hw);