diff options
Diffstat (limited to 'drivers/net/wireless/digiPiper/digiPs.c')
-rw-r--r-- | drivers/net/wireless/digiPiper/digiPs.c | 1164 |
1 files changed, 1164 insertions, 0 deletions
diff --git a/drivers/net/wireless/digiPiper/digiPs.c b/drivers/net/wireless/digiPiper/digiPs.c new file mode 100644 index 000000000000..c6ae6b0b656a --- /dev/null +++ b/drivers/net/wireless/digiPiper/digiPs.c @@ -0,0 +1,1164 @@ +/* + * digiPs.c + * + * Copyright (C) 2009 by Digi International Inc. + * All rights reserved. + * + * 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. + */ + +/* + * This file contains the routines that are related to transmitting + * frames. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/kthread.h> + +#include "pipermain.h" +#include "mac.h" +#include "phy.h" +#include "airoha.h" +#include "digiPs.h" + +/* + * Macro converts milliseconds to HZ. + * + * TODO: Look for standard Linux version of this. + */ +#define MILLS_TO_JIFFIES(x) ((((x)*HZ) + 500) / 1000) + + + + +#define MINIMUM_SLEEP_PERIOD (20) + +/* + * Amount of time before shutdown deadline to start the shutdown process. This + * should allow enough time to get one last frame out, which will be the + * null-data frame notifying the AP that we are shutting down. + */ +#define PS_TRANSMITTER_SHUTDOWN_MS (10) + +/* + * Amount of time we will wake up before the next beacon. We try to wake up before + * the next beacon so that we don't miss it. + */ +#define PS_WAKE_BEFORE_BEACON_MS (20) + +/* + * Minimum amount of time we we keep awake for. + */ +#define PS_MINUMUM_POWER_UP_PERIOD_MS (10) + +/* + * Minimum amount of time we will sleep. If we will end up sleeping + * for less than this, then don't go to sleep. + */ +#define PS_MINIMUM_SLEEP_TIME (10) + +/* + * Length of one tick of the transmit clean up timer in ms. This is + * the minimum amount of time we will sleep for. + */ +#define PS_TRANSMIT_TIMER_TICK_MS ((1000/HZ) ? (1000/HZ) : 1) + +/* + * Length of time we will wait past the expected arrival of a beacon before assuming + * that we missed it. + */ +#define PS_BEACON_TIMEOUT_MS (40) + + +/* + * Minimum beacon interval we will support for duty cycling. There is so much overhead + * in duty cycling that it doesn't make sense to do it for short beacon intervals. + */ +#define PS_MINIMUM_BEACON_INT (100) + +/* + * Amount of time we will pause duty cycling for after receiving an event that suggests + * wpa_supplicant is attempting to reassociate with an AP. + */ +#define PS_SCAN_DELAY (5000) + + +// Powersave register index +#define INDX_GEN_CONTROL 0 // General control +#define INDX_GEN_STATUS 1 // General status +#define INDX_RSSI_AES 2 // RSSI and AES status +#define INDX_INTR_MASK 3 // Interrupt mask +#define INDX_SPI_CTRL 4 // RF SPI control +#define INDX_CONF1 5 // Configuration 1 +#define INDX_CONF2 6 // Configuration 2 +#define INDX_AES_MODE 7 // ARS mode +#define INDX_OUT_CTRL 8 // Output control +#define INDX_MAC_CONTROL 9 // MAC control +#define INDX_STAID_1 10 // first part of stations ID +#define INDX_STAID_2 11 // 2nd part of station ID +#define INDX_BSSID_1 12 // 1st part of BSS ID +#define INDX_BSSID_2 13 // 2nd part of BSS ID +#define INDX_BRS_SSID 14 // BRS mask and SSID length +#define INDX_BACKOFF 15 // backoff +#define INDX_DTIM_LISTEN 16 // DTIM perido and listen interval +#define INDX_BEACON_INT 17 // beacon interval +#define INDX_MAC_CTL 18 // MAC control register +#define INDX_BEACON_MASK 19 // beacon mask and backoff +#define INDX_TOTAL 20 + +static u32 savedRegs[INDX_TOTAL]; // Used to save registers for sleep mode + + +/* + * TODO: Delete this. + */ +struct ps_stats { + unsigned int modeStart; + unsigned int cycleStart; + unsigned int receivedBeacons; + unsigned int missedBeacons; + unsigned int jiffiesOn; + unsigned int jiffiesOff; +} stats; + + + +static void ps_free_frame(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct piper_priv *piperp = hw->priv; + + if (skb) { + dev_kfree_skb(skb); + piperp->ps.frames_pending--; + } +} + + + + + +#define ACK_SIZE (14) /* length of ACK in bytes */ + +// Length (in usecs) of a MAC frame of bytes at rate (in 500kbps units) +// not including SIFS and PLCP preamble/header +#define CCK_DURATION(bytes, rate) ((16*(bytes)+(rate)-1)/(rate)) + +#define USE_SHORTPREAMBLE(is_1_Mbit, use_short_preamble) ((!is_1_Mbit) & use_short_preamble) + +// Length (in usecs) of SIFS and PLCP preamble/header. +#define PRE_LEN(is_1_Mbit, use_short_preamble) (USE_SHORTPREAMBLE(is_1_Mbit, use_short_preamble) ? 106 : 202) + +// Duration (in usecs) of an OFDM frame at rate (in 500kbps units) +// including SIFS and PLCP preamble/header +#define OFDM_DURATION(bytes, rate) (36 + 4*((4*(bytes)+(rate)+10)/(rate))) + +static unsigned int getRateIndex(struct piper_priv *piperp) +{ + unsigned int rates = piperp->ac->rd_reg(piperp, MAC_SSID_LEN); + unsigned int result = AIROHA_LOWEST_OFDM_RATE_INDEX; + + if (piperp->rf->getBand(piperp->channel) == IEEE80211_BAND_2GHZ) { + if ((rates & MAC_PSK_BRS_MASK) != 0) { + result = AIROHA_LOWEST_PSK_RATE_INDEX; + } + } + + return result; +} + +static int getAckDuration(struct piper_priv *piperp) +{ + bool is_1_Mbit = (getRateIndex(piperp) == AIROHA_LOWEST_PSK_RATE_INDEX); + int duration = 0; + + if (is_1_Mbit) { + duration = CCK_DURATION(ACK_SIZE, 1); + } else { + duration = OFDM_DURATION(ACK_SIZE, 6); + } + + duration += PRE_LEN(is_1_Mbit, piperp->use_short_preamble); + + return duration; +} + + +/* + * This function is used to notify the AP about the current state of + * power save. One of the bits in the 802.11 header field is set to + * indicate the status of power save. This bit is actually set appropriately + * for all frames sent, we just send a null data frame just to make + * sure something is sent to the AP in a reasonable amount of time. + */ +/* + * Possible values for is_power_management_on argument + */ +#define POWERING_DOWN (true) +#define POWERED_UP (false) +void piper_sendNullDataFrame(struct piper_priv *piperp, bool is_power_management_on) +{ + struct sk_buff *skb = NULL; + _80211HeaderType *header; + struct ieee80211_tx_info *tx_info; + + skb = + __dev_alloc_skb(sizeof(_80211HeaderType) + + piperp->hw->extra_tx_headroom, GFP_ATOMIC); + if (skb == NULL) + goto piper_sendNullDataFrame_Exit; + + tx_info = (struct ieee80211_tx_info *) skb->cb; + + skb_reserve(skb, piperp->hw->extra_tx_headroom); + header = (_80211HeaderType *) skb_put(skb, sizeof(_80211HeaderType)); + memset(header, 0, sizeof(*header)); + header->fc.type = TYPE_NULL_DATA; + header->fc.pwrMgt = is_power_management_on; + header->duration = getAckDuration(piperp); + memcpy(header->addr1, piperp->bssid, sizeof(header->addr1)); + memcpy(header->addr2, piperp->pdata->macaddr, sizeof(header->addr2)); + memcpy(header->addr3, piperp->bssid, sizeof(header->addr3)); + + tx_info->flags = IEEE80211_TX_CTL_ASSIGN_SEQ | IEEE80211_TX_CTL_FIRST_FRAGMENT; + tx_info->band = piperp->rf->getBand(piperp->channel); + tx_info->antenna_sel_tx = 1; /* actually this is ignored for now */ + tx_info->control.rates[0].idx = 0; + tx_info->control.rates[0].count = 2; /* no retries. Don't tie us up waiting for an ACK */ + tx_info->control.rates[0].flags = 0; + tx_info->control.rates[1].idx = -1; + tx_info->control.rts_cts_rate_idx = -1; + piperp->ps.frames_pending++; + + if (piper_hw_tx_private(piperp->hw, skb, ps_free_frame) != 0) { + /* printk(KERN_ERR + "piper_hw_tx() failed unexpectedly when sending null data frame.\n"); */ + ps_free_frame(piperp->hw, skb); + } + +piper_sendNullDataFrame_Exit: + return; +} + + + + +#define RESET_PIPER (1) /* must be set to 1 (0 case is only for debugging)*/ +#define PS_DONT_FORCE (false) /* set force to this value if we want to be safe */ +#define PS_FORCE_POWER_DOWN (true) /* set force to this value to shut down the H/W reguardless*/ +/* + * This routine shuts down Piper and the Airoha transceiver. First we check to + * make sure the driver and H/W are idle. Then we save the state of the H/W. + * Then we shut down the Airoha and place piper into reset. + */ +int piper_MacEnterSleepMode(struct piper_priv *piperp, bool force) +{ + /* + * Interrupts are already disabled when we get here. + */ + + if (piperp->ps.poweredDown) + return 0; + + savedRegs[INDX_INTR_MASK] = piperp->ac->rd_reg(piperp, BB_IRQ_MASK); + piperp->ac->wr_reg(piperp, BB_IRQ_MASK, 0, op_write); + + if (!force) { + if ( (piperp->ps.rxTaskletRunning) + || ((piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY)) + || ( (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) + & BB_GENERAL_CTL_TX_FIFO_EMPTY) == 0) + || (piperp->tx_tasklet_running) + || ( (piperp->ac->rd_reg(piperp, BB_GENERAL_STAT) + & BB_GENERAL_STAT_RX_FIFO_EMPTY) == 0) + || (piperp->ac->rd_reg(piperp, BB_IRQ_STAT) & savedRegs[INDX_INTR_MASK])) { + + piperp->ac->wr_reg(piperp, BB_IRQ_MASK, savedRegs[INDX_INTR_MASK], op_write); + return -1; + } + } + + disable_irq(piperp->irq); + + savedRegs[INDX_GEN_CONTROL] = piperp->ac->rd_reg(piperp, BB_GENERAL_CTL); + savedRegs[INDX_GEN_STATUS] = piperp->ac->rd_reg(piperp, BB_GENERAL_STAT); + savedRegs[INDX_RSSI_AES] = piperp->ac->rd_reg(piperp, BB_RSSI) & ~BB_RSSI_EAS_BUSY; + savedRegs[INDX_SPI_CTRL] = piperp->ac->rd_reg(piperp, BB_SPI_CTRL); + savedRegs[INDX_CONF1] = piperp->ac->rd_reg(piperp, BB_TRACK_CONTROL); + savedRegs[INDX_CONF2] = piperp->ac->rd_reg(piperp, BB_CONF_2); + savedRegs[INDX_OUT_CTRL] = piperp->ac->rd_reg(piperp, BB_OUTPUT_CONTROL); + savedRegs[INDX_MAC_CONTROL] = piperp->ac->rd_reg(piperp, MAC_CTL); + + savedRegs[INDX_STAID_1] = piperp->ac->rd_reg(piperp, MAC_STA_ID0); + savedRegs[INDX_STAID_2] = piperp->ac->rd_reg(piperp, MAC_STA_ID1); + savedRegs[INDX_BSSID_1] = piperp->ac->rd_reg(piperp, MAC_BSS_ID0); + savedRegs[INDX_BSSID_2] = piperp->ac->rd_reg(piperp, MAC_BSS_ID1); + savedRegs[INDX_BRS_SSID] = piperp->ac->rd_reg(piperp, MAC_SSID_LEN); + savedRegs[INDX_BACKOFF] = piperp->ac->rd_reg(piperp, MAC_BACKOFF); + savedRegs[INDX_DTIM_LISTEN] = piperp->ac->rd_reg(piperp, MAC_DTIM_PERIOD); + savedRegs[INDX_BEACON_INT] = piperp->ac->rd_reg(piperp, MAC_CFP_ATIM); + savedRegs[INDX_MAC_CTL] = piperp->ac->rd_reg(piperp, MAC_CTL); + savedRegs[INDX_BEACON_MASK] = piperp->ac->rd_reg(piperp, MAC_BEACON_FILT); + + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_RX_EN, op_and); //disable receiving + piperp->ac->wr_reg(piperp, MAC_CTL, 0, op_write); + piperp->ac->wr_reg(piperp, BB_IRQ_MASK, 0, op_write); + +#if RESET_PIPER + // Power down the airoha transceiver + piperp->rf->power_on(piperp->hw, false); + udelay(10); + // hold the transceiver in reset mode + if (piperp->pdata->reset) + piperp->pdata->reset(piperp, 1); +#endif + stats.jiffiesOn += jiffies - stats.cycleStart; + stats.cycleStart = jiffies; + piperp->ps.poweredDown = true; + + return 0; +} + + +#define PS_NO_SPIKE_SUPPRESSION (false) /* want_spike_suppression = don't want spike suppression*/ +#define PS_WANT_SPIKE_SUPPRESSION (true) /* want_spike_suppression = do spike suppression*/ +/* + * Turn the H/W back on after it was shutdown with piper_MacEnterSleepMode. + * + * 1. Power up the hardware. + * 2. Perform the spike suppression routine if desired. The spike suppression + * routine resyncs the clocks in Piper in order to prevent us from generating + * noise spikes at 1 and 2 MHz. Unfortunately it takes an indeterminate amount + * of time, so we normally don't do it and just make sure we do not send at + * at those data rates while duty cycling. + * 3. Set the channel. The Airoha was shut off so we have to set the channel + * again. + * 4. Restore the state of piper registers. + * 5. Zero out and reset the transmitter FIFO. Mike Schaffner claims this should + * not be necessary, but we seem to run into trouble when we don't do it. + * 6. Restore the interrupts. + */ +void piper_MacEnterActiveMode(struct piper_priv *piperp, bool want_spike_suppression) +{ + int i; +// #define WANT_DEBUG +#ifdef WANT_DEBUG + static unsigned int run = 0; +#endif + +#if RESET_PIPER + + if (piperp->pdata->reset) { +#ifdef WANT_DEBUG + if (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_TX_FIFO_FULL) { + printk(KERN_ERR "**** While in reset, run = %d\n", run); + digiWifiDumpRegisters(piperp, MAIN_REGS); + while(1); + } + if (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) { + printk(KERN_ERR "**** While in reset AES busy set, run = %d\n", run); + digiWifiDumpRegisters(piperp, MAIN_REGS); + while(1); + } +#endif + piperp->pdata->reset(piperp, 0); + udelay(10); + +#ifdef WANT_DEBUG + if (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_TX_FIFO_FULL) { + printk(KERN_ERR "**** After reset, run = %d\n", run); + digiWifiDumpRegisters(piperp, MAIN_REGS); + while(1); + } + if (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) { + printk(KERN_ERR "**** After reset AES busy set, run = %d\n", run); + digiWifiDumpRegisters(piperp, MAIN_REGS); + while(1); + } + run++; +#endif + piperp->rf->power_on(piperp->hw, true); + mdelay(1); + piper_spike_suppression(piperp, want_spike_suppression); + } +#endif + + piperp->ac->wr_reg(piperp, BB_IRQ_STAT, 0xff, op_write); + + +#if RESET_PIPER + piperp->rf->set_chan_no_rx(piperp->hw, piperp->channel); +#endif + + // store the registers back + + piperp->ac->wr_reg(piperp, BB_GENERAL_STAT, 0x30000000, op_write); + piperp->ac->wr_reg(piperp, BB_RSSI, savedRegs[INDX_RSSI_AES] & ~BB_RSSI_EAS_BUSY, op_write); + +// piperp->ac->wr_reg(piperp, BB_IRQ_MASK, savedRegs[INDX_INTR_MASK], op_write); + piperp->ac->wr_reg(piperp, BB_SPI_CTRL, savedRegs[INDX_SPI_CTRL], op_write); + piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, savedRegs[INDX_CONF1], op_write); + piperp->ac->wr_reg(piperp, BB_CONF_2, savedRegs[INDX_CONF2], op_write); + piperp->ac->wr_reg(piperp, BB_AES_CTL, 0, op_write); + piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, savedRegs[INDX_OUT_CTRL], op_write); + piperp->ac->wr_reg(piperp, MAC_CTL, savedRegs[INDX_MAC_CONTROL], op_write); + + piperp->ac->wr_reg(piperp, MAC_STA_ID0, savedRegs[INDX_STAID_1], op_write); + piperp->ac->wr_reg(piperp, MAC_STA_ID1, savedRegs[INDX_STAID_2], op_write); + piperp->ac->wr_reg(piperp, MAC_BSS_ID0, savedRegs[INDX_BSSID_1], op_write); + piperp->ac->wr_reg(piperp, MAC_BSS_ID1, savedRegs[INDX_BSSID_2], op_write); + piperp->ac->wr_reg(piperp, MAC_SSID_LEN, savedRegs[INDX_BRS_SSID], op_write); + piperp->ac->wr_reg(piperp, MAC_BACKOFF, savedRegs[INDX_BACKOFF], op_write); + piperp->ac->wr_reg(piperp, MAC_DTIM_PERIOD, savedRegs[INDX_DTIM_LISTEN], op_write); + piperp->ac->wr_reg(piperp, MAC_CFP_ATIM, savedRegs[INDX_BEACON_INT], op_write); + piperp->ac->wr_reg(piperp, MAC_CTL, savedRegs[INDX_MAC_CTL], op_write); + piperp->ac->wr_reg(piperp, MAC_BEACON_FILT, savedRegs[INDX_BEACON_MASK], op_write); + +//**** + // set bit-11 in the general control register to a 1 to start the processors + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, + BB_GENERAL_CTL_MAC_ASSIST_ENABLE, op_or); + + // set the TX-hold bit + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720080, op_write); + + // clear the TX-FIFO memory + for (i = 0; i < 448; i++) + piperp->ac->wr_reg(piperp, BB_DATA_FIFO, 0, op_write); + + // clear RX-FIFO memory + for (i = 0; i < 512; i++) + piperp->ac->rd_reg(piperp, BB_DATA_FIFO); + + // reset the TX-FIFO and RX-FIFO + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x377200E0, op_write); + + + // release the TX-hold and reset + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720000, op_write); + + +//*** + /* + * Reset the interrupt mask. We could have been receiving a frame when we + * powered down. This could cause us to store the wrong mask, so we want + * to make sure we enabe the RX interrupts. However, we should not have the + * TX interrupts enabled when we come out of power save mode. + */ + udelay(50); + piperp->ac->wr_reg(piperp, BB_IRQ_STAT, 0xff, op_write); + piperp->clear_irq_mask_bit(piperp, 0xffffffff); + piperp->set_irq_mask_bit(piperp, BB_IRQ_MASK_RX_OVERRUN | BB_IRQ_MASK_RX_FIFO); + piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, + (savedRegs[INDX_GEN_CONTROL] | 0x37000000 | + BB_GENERAL_CTL_RX_EN), op_write); + + stats.jiffiesOff += jiffies - stats.cycleStart; + stats.cycleStart = jiffies; + + /* TODO, this is a temporary workaround and should be better analyzed in future. + * The problem is that the general power save code is not synchronized with the + * dynamic PW and this is causing that, the following line, unbalances the + * piper wireless irq */ + if (piperp->ps.poweredDown != false) + enable_irq(piperp->irq); + + piperp->ps.poweredDown = false; +} + + + + +/* + * So what crazy thing are we doing here? Well, Piper has a bug where it + * can send noise spikes at 1 Mbps and 2 Mbps if it is powered up without + * running a special spike suppression routine. The spike suppression code + * takes an average of 30 ms, and I have timed it taking as long as 300 ms. + * This is not something you want to use for duty cycling. The solution is + * to avoid sending at those two rates. After the transmit routine determines + * the rate mac80211 specified, it will call us and we will decide whether + * we like that rate. If it is one of our bad rates, then we will bump it + * up to a good rate. + */ +struct ieee80211_rate *piper_ps_check_rate(struct piper_priv *piperp, + struct ieee80211_rate *rate) +{ +#define BAD_RATE_1MBPS (10) +#define BAD_RATE_2MBPS (20) + if ((piperp->ps.mode == PS_MODE_LOW_POWER) && (rate != NULL)) { + if ((rate->bitrate == BAD_RATE_1MBPS) + || (rate->bitrate == BAD_RATE_2MBPS)) { + rate = + (struct ieee80211_rate *) piperp->rf-> + getRate(AIROHA_55_MBPS_RATE_INDEX); + } + } + + return rate; +} + +EXPORT_SYMBOL_GPL(piper_ps_check_rate); + + + +/* + * This routine restarts the transmitter after powering back + * up, or failing to power down. + * + * 1) Clear the power management bit so that frames will be + * sent indicating that we are poweredup. + * 2) Set the flag to allow transmits again. + * 3) If we stopped the mac80211 transmitter queue, then start + * it back up again. + * 4) Notify the AP that we are awake by sending a null-data frame + * with the power management bit clear. + */ +static void ps_resume_transmits(struct piper_priv *piperp) +{ + piperp->ps.power_management = POWERED_UP; + piperp->ps.allowTransmits = true; + piperp->ps.stopped_tx_queues = false; + piper_sendNullDataFrame(piperp, POWERED_UP); + ieee80211_wake_queues(piperp->hw); +} + + + + + + +/* + * This routine sets an event timer. The ps_state_machine routine + * will be executed when the event timer expires. + */ +static void ps_set_timer_event(struct piper_priv *piperp, + enum piper_ps_event next_event, + unsigned int timeout_ms) +{ + unsigned int delay_in_jiffies = MILLS_TO_JIFFIES(timeout_ms); + + if (delay_in_jiffies == 0) + delay_in_jiffies++; + + del_timer_sync(&piperp->ps.timer); + piperp->ps.this_event = next_event; + piperp->ps.timer.expires = delay_in_jiffies + jiffies; + add_timer(&piperp->ps.timer); +} + + +/* + * This routine cancels an event timer set by ps_set_timer_event. + */ +static void ps_cancel_timer_event(struct piper_priv *piperp) +{ + del_timer_sync(&piperp->ps.timer); +} + + +#define WANT_STATE_MACHINE_DEBUG (0) +#if WANT_STATE_MACHINE_DEBUG + +struct event_record { + enum piper_ps_event event; + enum piper_ps_state state; +}; + +#define MAX_EVENTS_RECORDED (15) +static unsigned int debug_events_index = 0; +static struct event_record debug_events[MAX_EVENTS_RECORDED]; + +void debug_track_event(enum piper_ps_event event, enum piper_ps_state state) +{ + debug_events[debug_events_index].event = event; + debug_events[debug_events_index].state = state; + if (debug_events_index == MAX_EVENTS_RECORDED) { + unsigned int i; + + printk(KERN_ERR); + for (i = 0; i < MAX_EVENTS_RECORDED; i++) { + printk("(%d,%d)", debug_events[i].event, + debug_events[i].state); + } + printk("\n"); + debug_events_index = 0; + } else { + debug_events_index++; + } +} + + +#else + #define debug_track_event(event, state) +#endif + +/* + * This is the entry point into the duty cycle state machine. It may be called + * by: + * + * 1) piper_ps_handle_beacon when a beacon frame is received. + * 2) the receiver task when we receive the ACK for the last pending frame + * when we are waiting to power down. + * 3) by ps_timer for many timer events. + */ +static void ps_state_machine(struct piper_priv *piperp, enum piper_ps_event event) +{ + unsigned long flags; + + debug_track_event(event, piperp->ps.state); + + spin_lock_irqsave(&piperp->ps.lock, flags); + + + switch (event) { + case PS_EVENT_BEACON_RECEIVED: + /* + * We just received a beacon. This is really the driving event in this state + * machine. Everything else is synchronized from it. + * + * We know we are powered up because we just received the beacon. The normal + * control path is to set a timer which will expire when we need to start + * preparations for powering down again. + */ + ps_cancel_timer_event(piperp); /* cancel missed beacon timer*/ + stats.receivedBeacons++; + if ( (!piperp->areWeAssociated) + || (piperp->ps.beacon_int < PS_MINIMUM_BEACON_INT) + || (piperp->ps.scan_timer != 0)) { + /* + * Don't duty cyle while we are trying to associate. + */ + piperp->ps.state = PS_STATE_WAIT_FOR_BEACON; + if (piperp->ps.scan_timer) { + piperp->ps.scan_timer--; + } + break; + } + if (piperp->ps.state == PS_STATE_WAIT_FOR_BEACON) { + int timeout; + /* + * Calculate amount of time to sleep. + */ + piperp->ps.sleep_time = (piperp->ps.beacon_int * (100 - piperp->power_duty)) / 100; + + /* + * Now figure out how long we have before it's time to go to sleep. We + * have to wake up at least PS_WAKE_BEFORE_BEACON_MS before we expect to + * receive the next beacon, and we need to start powering down at least + * PS_TRANSMITTER_SHUTDOWN_MS ahead of time. + */ + timeout = piperp->ps.beacon_int - (piperp->ps.sleep_time + PS_TRANSMITTER_SHUTDOWN_MS + PS_WAKE_BEFORE_BEACON_MS); + if ((timeout > 0) && (piperp->ps.sleep_time > PS_MINIMUM_SLEEP_TIME)) { + /* + * If there is enough time left that it makes sense to duty + * cycle, then program the timer and advance to the next state. + */ + piperp->ps.state = PS_STATE_WAIT_FOR_STOP_TRANSMIT_EVENT; + ps_set_timer_event(piperp, PS_EVENT_STOP_TRANSMIT_TIMER_EXPIRED, timeout); + + break; + } + } else { + if ( (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE) + || (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT)) { + ps_resume_transmits(piperp); + } + digi_dbg("*** Beacon event in state %d.\n", piperp->ps.state); + } + /* + * We will come here if we were in the wrong state when we received the + * beacon, or if the duty cycle is too short. + */ + piperp->ps.state = PS_STATE_WAIT_FOR_BEACON; + ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON, + piperp->ps.beacon_int + PS_BEACON_TIMEOUT_MS); + break; + case PS_EVENT_STOP_TRANSMIT_TIMER_EXPIRED: + /* + * This event is hit when it's time to start powering down. Unfortunately, this is + * not a simple thing to do. The first things to do are: + * + * 1. Set the power save on flag. This will cause any transmit frame to be + * sent with the power management bit set. + * 2. Stop the mac80211 layer from sending us anymore frames. + * 3. Signal the AP that we are powering down by sending a null-data frame with the + * power management bit set. + */ + if (piperp->ps.state == PS_STATE_WAIT_FOR_STOP_TRANSMIT_EVENT) { + if (piperp->ps.scan_timer) { + /* + * Don't shut down if we are scanning. + */ + piperp->ps.state = PS_STATE_WAIT_FOR_BEACON; + break; + } + piperp->ps.power_management = POWERING_DOWN; + piperp->ps.allowTransmits = false; + ieee80211_stop_queues(piperp->hw); + piperp->ps.stopped_tx_queues = true; + piper_sendNullDataFrame(piperp, POWERING_DOWN); + piperp->ps.state = PS_STATE_WAIT_FOR_TRANSMITTER_DONE; + ps_set_timer_event(piperp, PS_EVENT_TRANSMITTER_DONE_TIMER_TICK, + PS_TRANSMIT_TIMER_TICK_MS); + } else { + /* + * This should never happen (famous last words) + */ + digi_dbg("** stop tx event, state = %d.\n", piperp->ps.state); + piperp->ps.state = PS_STATE_WAIT_FOR_BEACON; + ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON, + piperp->ps.beacon_int + PS_BEACON_TIMEOUT_MS); + } + break; + case PS_EVENT_TRANSMITTER_DONE: + /* + * This event is triggered when the receive task finishes processing the ACK + * from the null-data frame sent by the PS_EVENT_STOP_TRANSMIT_TIMER_EXPIRED event. + * We try to power down now. + */ + if (piperp->ps.scan_timer == 0) { + if ( (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE) + || (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT)) { + ps_cancel_timer_event(piperp); /* cancel transmitter done timeout timer*/ + if (piper_MacEnterSleepMode(piperp, PS_DONT_FORCE) == 0) { + piperp->ps.state = PS_STATE_WAIT_FOR_WAKEUP_ALARM; + /* + * Note that the value PS_EVENT_TRANSMITTER_DONE_TIMER_TICK is + * updated as necessary by the PS_EVENT_TRANSMITTER_DONE_TIMER_TICK + * event to take into account the amount of time it took for the + * transmitter to finish sending the last frame. + */ + ps_set_timer_event(piperp, PS_EVENT_WAKEUP, piperp->ps.sleep_time); + break; + } + } else { +#ifdef WANT_DEBUG + printk(KERN_ERR "couldn't sleep, rxt=%d, AES busy = %d, txfifo=%d, txt=%d, rxfifo=%d\n", + (piperp->ps.rxTaskletRunning),((piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) != 0), + ( (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL)& BB_GENERAL_CTL_TX_FIFO_EMPTY) == 0), + (piperp->tx_tasklet_running), + ( (piperp->ac->rd_reg(piperp, BB_GENERAL_STAT) + & BB_GENERAL_STAT_RX_FIFO_EMPTY) == 0)); +#endif + digi_dbg("** PS_EVENT_TRANSMITTER_DONE event, but state == %d.\n", piperp->ps.state); + } + } + /* + * If we fall through to here, then either we are in the wrong state, or we were + * not able to power down the H/W. + */ + ps_resume_transmits(piperp); + piperp->ps.state = PS_STATE_WAIT_FOR_BEACON; + ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON, + piperp->ps.beacon_int + PS_BEACON_TIMEOUT_MS); + break; + case PS_EVENT_TRANSMITTER_DONE_TIMER_TICK: + /* + * This event is triggered periodically while we are waiting for the + * transmitter to finish sending that last packet. We decrement + * piperp->ps.sleep_time (which is used by the PS_EVENT_TRANSMITTER_DONE + * event). If piperp->ps.sleep_time is still larger than our minimum + * required sleep time, then we just restart the timer. + */ + if (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE) { + piperp->ps.sleep_time -= PS_TRANSMIT_TIMER_TICK_MS; + if (piperp->ps.sleep_time >= PS_MINIMUM_SLEEP_TIME) { + piperp->ps.state = PS_STATE_WAIT_FOR_TRANSMITTER_DONE; + ps_set_timer_event(piperp, PS_EVENT_TRANSMITTER_DONE_TIMER_TICK, + PS_TRANSMIT_TIMER_TICK_MS); + } else { + /* + * Transmitter did not shut down in time. Resume normal operations + * and stay awake until the next beacon. + */ + ps_resume_transmits(piperp); + piperp->ps.state = PS_STATE_WAIT_FOR_BEACON; + ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON, + piperp->ps.sleep_time + PS_WAKE_BEFORE_BEACON_MS + + PS_BEACON_TIMEOUT_MS); + } + } else if (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT) { + piperp->ps.sleep_time -= PS_TRANSMIT_TIMER_TICK_MS; + /* + * The piper_ps_rx_task_exiting routine sets this state just before it + * releases the lock on ps.state and calls this event. If we ever + * come here, then the timer tick occurred just between the time + * piper_ps_rx_task_exiting released the lock and ps_state_machine + * reset it. Since the tx done event is in progress, we should ignore + * this tick. + */ + break; + } else { + digi_dbg("** done tick in state %d.\n", piperp->ps.state); + } + break; + case PS_EVENT_WAKEUP: + /* + * This event is called when we have powered down and it is time + * to power back up again. + * + * 1) Power up the H/W. + * 2) Resume normal operations + * 3) Update our state. + * 4) Set a timeout for receiving the next beacom frame. + */ + if (piperp->ps.state == PS_STATE_WAIT_FOR_WAKEUP_ALARM) { + piper_MacEnterActiveMode(piperp, PS_NO_SPIKE_SUPPRESSION); + ps_resume_transmits(piperp); + piperp->ps.state = PS_STATE_WAIT_FOR_BEACON; + ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON, + PS_BEACON_TIMEOUT_MS + PS_WAKE_BEFORE_BEACON_MS); + } else { + digi_dbg("** wake event in state %d.\n", piperp->ps.state); + } + break; + case PS_EVENT_MISSED_BEACON: + /* + * This event is called when we miss a beacon. For now just update + * our statistics. + */ + piperp->ps.state = PS_STATE_WAIT_FOR_BEACON; + if ((piperp->areWeAssociated) && (piperp->ps.scan_timer == 0)) { + stats.missedBeacons++; + ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON, piperp->ps.beacon_int); + } + break; + default: + digi_dbg("**** ps_state_machine received unknown event %d.\n", event); + break; + } + spin_unlock_irqrestore(&piperp->ps.lock, flags); + +} + +/* + * This routine is called by the receiver task when it exits. We use it to generate + * the PS_EVENT_TRANSMITTER_DONE event. The event signifies that the transmitter has + * sent our null-data frame and is idle. We determine this by checking frames_pending, + * while will be nonzero if a null-data frame is waiting to be sent, and the machine's + * state. + */ +void piper_ps_rx_task_exiting(struct piper_priv *piperp) +{ + unsigned long flags; + + spin_lock_irqsave(&piperp->ps.lock, flags); + + if ( (piperp->ps.frames_pending == 0) + && (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE)) { + /* + * We have a race condition between the transmitter done tick and + * this routine. So this routine changes the state to + * PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT before it releases the + * lock so that we don't get confused. + */ + piperp->ps.state = PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT; + spin_unlock_irqrestore(&piperp->ps.lock, flags); + ps_state_machine(piperp, PS_EVENT_TRANSMITTER_DONE); + } else { + spin_unlock_irqrestore(&piperp->ps.lock, flags); + } +} + +/* + * Called when the event timer expires. Call the state machine to handle + * the event. + */ +static void ps_timer(unsigned long context) +{ + struct piper_priv *piperp = (struct piper_priv *) context; + + ps_state_machine(piperp, piperp->ps.this_event); +} + + + +/* + * This routine is called when we receive a beacon. We extract the beacon interval + * in case it has changed and then call the state machine. + */ +static void piper_ps_handle_beacon(struct piper_priv *piperp, struct sk_buff *skb) +{ +#define BEACON_INT_LSB (8) +#define BEACON_INT_MSB (9) + u32 beacon_int; + _80211HeaderType *header = (_80211HeaderType *) skb->data; + bool fromOurAp = piperp->areWeAssociated + && (memcmp(piperp->bssid, header->addr3, sizeof (header->addr3)) == 0); + + /* + * mac80211 does not inform us when the beacon interval changes, so we have + * to read this information from the beacon ourselves. + */ + + if (fromOurAp) { + beacon_int = skb->data[sizeof(_80211HeaderType) + BEACON_INT_LSB]; + beacon_int |= (skb->data[sizeof(_80211HeaderType) + BEACON_INT_MSB] << 8); + piperp->ps.beacon_int = beacon_int; + + if (piperp->ps.mode == PS_MODE_LOW_POWER) { + ps_state_machine(piperp, PS_EVENT_BEACON_RECEIVED); + } + } +} + + + +/* + * This routine is called when mac80211 starts doing things that might indicate it + * is attempting to scan or reassociate. Things like changing the channel or + * disassociating. When we receive an event like that, we stop duty cycling for + * a while since it may interfere with attempts to reassociate with an access point. + */ +void piper_ps_scan_event(struct piper_priv *piperp) +{ + (void) piperp; +#if 0 + /* + * It appears that pausing duty cycling during association events may actually + * worsen performance I suspect that either the AP or mac80211 is measuring + * our throughput and adjusting the load accordingly, and that momentary changes + * in performance caused by pausing duty cyling interfere with this. + * + * TODO: Consider removing this code. I left it in for now in case we decide + * to try it again, but if we're not going to use it, it just makes the + * driver more confusing and should be removed. + */ + if (piperp->ps.beacon_int != 0) { + piperp->ps.scan_timer = PS_SCAN_DELAY / piperp->ps.beacon_int; + } else { + piperp->ps.scan_timer = PS_SCAN_DELAY / 100; + } +#endif +} + + + +/* + * This routine is called so we can process incoming frames. We do the + * handshaking to receive buffered frames in PS mode here. + */ +void piper_ps_process_receive_frame(struct piper_priv *piperp, struct sk_buff *skb) +{ + _80211HeaderType *header = (_80211HeaderType *) skb->data; + + if (header->fc.type == TYPE_BEACON) { + piper_ps_handle_beacon(piperp, skb); + } else if ( (header->fc.type == TYPE_ASSOC_RESP) + || (header->fc.type == TYPE_REASSOC_RESP) + || (header->fc.type == TYPE_PROBE_RESP) + || (header->fc.type == TYPE_DISASSOC) + || (header->fc.type == TYPE_DEAUTH) + || (header->fc.type == TYPE_ACTION)) { + piper_ps_scan_event(piperp); + } +} + +EXPORT_SYMBOL_GPL(piper_ps_process_receive_frame); + + + +/* + * This function turns power save mode on or off. + */ +void piper_ps_set(struct piper_priv *piperp, bool powerSaveOn) +{ +#define MAX_SHUTDOWN_TIMEOUT (100) + unsigned long flags; + + spin_lock_irqsave(&piperp->ps.lock, flags); + + piper_ps_scan_event(piperp); + if (powerSaveOn) { + if (piperp->ps.beacon_int >= PS_MINIMUM_BEACON_INT) { + if (piperp->ps.mode != PS_MODE_LOW_POWER) { + piperp->ps.aid = 0; + piperp->ps.mode = PS_MODE_LOW_POWER; + piperp->ps.state= PS_STATE_WAIT_FOR_BEACON; + piperp->ps.power_management = POWERED_UP; + piperp->ps.poweredDown = false; + piperp->ps.allowTransmits = true; + piperp->ps.stopped_tx_queues = false; + stats.receivedBeacons = 0; + stats.missedBeacons = 0; + stats.modeStart = jiffies; + stats.cycleStart = jiffies; + stats.jiffiesOff = 0; + stats.jiffiesOn = 0; + piper_sendNullDataFrame(piperp, POWERED_UP); + /* + * Will start it the next time we receive a beacon. + */ + } + } else { + printk(KERN_ERR + "\nUnable to set power save mode because the beacon \n" + "interval set on this access point less than 100ms.\n"); + } + } else { + ps_cancel_timer_event(piperp); + if (piperp->ps.mode == PS_MODE_LOW_POWER) { + piperp->ps.mode = PS_MODE_FULL_POWER; /* stop duty cycle timer */ + if (piperp->ps.poweredDown) { + /* + * If we were powered down, then power up and do the spike suppression. + */ + piper_MacEnterActiveMode(piperp, PS_WANT_SPIKE_SUPPRESSION); + } else { + unsigned int timeout = 50; + int result; + /* + * If we branch here, then we were already powered up. You would think + * that we would be all set, but it's not that easy. Piper has a bug in + * it where we have to run a special spike suppression routine when we + * power it up. However, this routine takes an average of 30 ms to run, + * and I've see it take as long as 300 ms. This is not acceptable when + * we are duty cycling every 100 ms. To get around this, we do NOT do + * the spike suppression while duty cycling. Instead, we simply avoid + * transmitting at those rates which would cause spikes. Now, however, + * we are ending duty cycling and returning to normal operations so we + * have to do the spike suppression. Since we are powered up, the first + * thing to do is to power down. + */ + if (piperp->ps.state != PS_STATE_WAIT_FOR_STOP_TRANSMIT_EVENT) { + /* + * If we come here, then we did not happen to be trying to power down + * just as we got the command from mac80211, so we have to start + * the procedure. This is the normal case. + * + * 1. Set the power save on flag. This will cause frames to be + * transmitted with the power management bit on. The reason + * for doing that is to tell the AP to stop sending us frames. + * 2. Stop the mac80211 layer from sending us more frames by stopping + * the transmit queues. + * 3. Send a null-data frame to the AP with the power management bit + * set. This should cause it to stop sending us frames. + */ + piperp->ps.power_management = POWERING_DOWN; + piperp->ps.allowTransmits = false; + ieee80211_stop_queues(piperp->hw); + piperp->ps.stopped_tx_queues = true; + piper_sendNullDataFrame(piperp, POWERING_DOWN); + } + /* + * Now wait for that last frame to go and and then shut down. + */ + result = -1; + for (timeout = 0; (timeout < MAX_SHUTDOWN_TIMEOUT) && (result != 0); timeout++) { + spin_unlock_irqrestore(&piperp->ps.lock, flags); + mdelay(10); + spin_lock_irqsave(&piperp->ps.lock, flags); + result = piper_MacEnterSleepMode(piperp, PS_DONT_FORCE); + } + if (result != 0) { + /* + * This is bad. For some reason we are not able to power down. We + * will try to force it now, but this may end up putting the driver + * or H/W into a bad state. However, we can't sit in the loop above + * forever either. + */ +#ifdef WANT_DEBUG + printk(KERN_ERR "Forcing Piper to power down\n"); + printk(KERN_ERR "BB_RSSI_EAS_BUSY = %d\n", piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY); + printk(KERN_ERR "BB_GENERAL_CTL_TX_FIFO_EMPTY = %d\n", + piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_TX_FIFO_EMPTY); + printk(KERN_ERR "BB_GENERAL_STAT_RX_FIFO_EMPTY = %d\n", + piperp->ac->rd_reg(piperp, BB_GENERAL_STAT) & BB_GENERAL_STAT_RX_FIFO_EMPTY); + digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS); +#endif + piper_MacEnterSleepMode(piperp, PS_FORCE_POWER_DOWN); + } + /* + * Wait a moment and then power the H/W back up and execute the spike suppression + * routine. + */ + spin_unlock_irqrestore(&piperp->ps.lock, flags); + mdelay(30); + spin_lock_irqsave(&piperp->ps.lock, flags); + piper_MacEnterActiveMode(piperp, PS_WANT_SPIKE_SUPPRESSION); + ps_resume_transmits(piperp); + } + stats.jiffiesOn += jiffies - stats.cycleStart; +#define WANT_STATS (0) +#if WANT_STATS + if ((piperp->ps.beacon_int != 0) + && ((jiffies - stats.modeStart) != 0)) { + printk(KERN_ERR + "jiffiesOff = %u, jiffiesOn = %u, total time = %lu\n", + stats.jiffiesOff, stats.jiffiesOn, + (jiffies - stats.modeStart)); + printk(KERN_ERR + "Powered down %ld percent of the time.\n", + (stats.jiffiesOff * 100) / (jiffies - stats.modeStart)); + printk(KERN_ERR + "Received %u of %lu beacons while in powersave mode.\n", + stats.receivedBeacons, + (jiffies - + stats.modeStart) / + MILLS_TO_JIFFIES(piperp->ps.beacon_int)); + printk(KERN_ERR "received %d beacons, missed %d\n", + stats.receivedBeacons, stats.missedBeacons); + printk(KERN_ERR "allowTransmits = %d, stopped_tx_queues = %d, q_count = %d\n", + piperp->ps.allowTransmits, piperp->ps.stopped_tx_queues, + piperp->tx_queue_count); + if ((stats.receivedBeacons + stats.missedBeacons) != 0) + printk(KERN_ERR "%d%% beacons were missed\n", + (100 * stats.missedBeacons) / (stats.receivedBeacons + stats.missedBeacons)); + } +#endif + } + piperp->ps.aid = 0; + piperp->ps.state= PS_STATE_WAIT_FOR_BEACON; + piperp->ps.power_management = POWERED_UP; + piperp->ps.poweredDown = false; + piperp->ps.allowTransmits = true; + piperp->ps.stopped_tx_queues = false; + ps_resume_transmits(piperp); + piper_sendNullDataFrame(piperp, POWERED_UP); + } + + spin_unlock_irqrestore(&piperp->ps.lock, flags); +} + +EXPORT_SYMBOL_GPL(piper_ps_set); + + + +/* + * Called when driver is loaded. Initialize our context. + */ +void piper_ps_init(struct piper_priv *piperp) +{ + memset(&piperp->ps, 0, sizeof(piperp->ps)); + piperp->ps.beacon_int = 100; + piperp->ps.aid = 0; + init_timer(&piperp->ps.timer); + piperp->ps.timer.function = ps_timer; + piperp->ps.timer.data = (unsigned long) piperp; + piperp->ps.mode = PS_MODE_FULL_POWER; + piperp->ps.state= PS_STATE_WAIT_FOR_BEACON; + spin_lock_init(&piperp->ps.lock); + piperp->ps.power_management = POWERED_UP; + piperp->ps.poweredDown = false; + piperp->ps.rxTaskletRunning; + piperp->ps.allowTransmits = true; + piperp->ps.stopped_tx_queues = false; + piperp->ps.frames_pending = 0; +} + +EXPORT_SYMBOL_GPL(piper_ps_init); + + +/* + * Called when driver is unloaded. Make sure the PS + * timer is shut down. + */ +void piper_ps_deinit(struct piper_priv *piperp) +{ + piper_ps_set(piperp, true); + piperp->ps.mode = PS_MODE_FULL_POWER; + del_timer_sync(&piperp->ps.timer); +} + +EXPORT_SYMBOL_GPL(piper_ps_deinit); |