Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/wireless/ath/ath9k/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 14 kB image not shown  

Quelle  link.c   Sprache: C

 
/*
 * Copyright (c) 2012 Qualcomm Atheros, Inc.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */


#include "ath9k.h"

/*
 * TX polling - checks if the TX engine is stuck somewhere
 * and issues a chip reset if so.
 */

static bool ath_tx_complete_check(struct ath_softc *sc)
{
 struct ath_txq *txq;
 int i;

 if (sc->tx99_state)
  return true;

 for (i = 0; i < IEEE80211_NUM_ACS; i++) {
  txq = sc->tx.txq_map[i];

  ath_txq_lock(sc, txq);
  if (txq->axq_depth) {
   if (txq->axq_tx_inprogress) {
    ath_txq_unlock(sc, txq);
    goto reset;
   }

   txq->axq_tx_inprogress = true;
  }
  ath_txq_unlock(sc, txq);
 }

 return true;

reset:
 ath_dbg(ath9k_hw_common(sc->sc_ah), RESET,
  "tx hung, resetting the chip\n");
 ath9k_queue_reset(sc, RESET_TYPE_TX_HANG);
 return false;
}

#define RX_INACTIVE_CHECK_INTERVAL (4 * MSEC_PER_SEC)

static bool ath_hw_rx_inactive_check(struct ath_softc *sc)
{
 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 u32 interval, count;

 interval = jiffies_to_msecs(jiffies - sc->rx_active_check_time);
 count = sc->rx_active_count;

 if (interval < RX_INACTIVE_CHECK_INTERVAL)
  return true/* too soon to check */

 sc->rx_active_count = 0;
 sc->rx_active_check_time = jiffies;

 /* Need at least one interrupt per second, and we should only react if
 * we are within a factor two of the expected interval
 */

 if (interval > RX_INACTIVE_CHECK_INTERVAL * 2 ||
     count >= interval / MSEC_PER_SEC)
  return true;

 ath_dbg(common, RESET,
  "RX inactivity detected. Schedule chip reset\n");
 ath9k_queue_reset(sc, RESET_TYPE_RX_INACTIVE);

 return false;
}

void ath_hw_check_work(struct work_struct *work)
{
 struct ath_softc *sc = container_of(work, struct ath_softc,
         hw_check_work.work);

 if (!ath_hw_check(sc) || !ath_tx_complete_check(sc) ||
     !ath_hw_rx_inactive_check(sc))
  return;

 ieee80211_queue_delayed_work(sc->hw, &sc->hw_check_work,
         msecs_to_jiffies(ATH_HW_CHECK_POLL_INT));
}

/*
 * Checks if the BB/MAC is hung.
 */

bool ath_hw_check(struct ath_softc *sc)
{
 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 enum ath_reset_type type;
 bool is_alive;

 ath9k_ps_wakeup(sc);

 is_alive = ath9k_hw_check_alive(sc->sc_ah);

 if (!is_alive) {
  ath_dbg(common, RESET,
   "HW hang detected, schedule chip reset\n");
  type = RESET_TYPE_MAC_HANG;
  ath9k_queue_reset(sc, type);
 }

 ath9k_ps_restore(sc);

 return is_alive;
}

/*
 * PLL-WAR for AR9485/AR9340
 */

static bool ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum)
{
 static int count;
 struct ath_common *common = ath9k_hw_common(sc->sc_ah);

 if (pll_sqsum >= 0x40000) {
  count++;
  if (count == 3) {
   ath_dbg(common, RESET, "PLL WAR, resetting the chip\n");
   ath9k_queue_reset(sc, RESET_TYPE_PLL_HANG);
   count = 0;
   return true;
  }
 } else {
  count = 0;
 }

 return false;
}

void ath_hw_pll_work(struct work_struct *work)
{
 u32 pll_sqsum;
 struct ath_softc *sc = container_of(work, struct ath_softc,
         hw_pll_work.work);
 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 /*
 * ensure that the PLL WAR is executed only
 * after the STA is associated (or) if the
 * beaconing had started in interfaces that
 * uses beacons.
 */

 if (!test_bit(ATH_OP_BEACONS, &common->op_flags))
  return;

 if (sc->tx99_state)
  return;

 ath9k_ps_wakeup(sc);
 pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah);
 ath9k_ps_restore(sc);
 if (ath_hw_pll_rx_hang_check(sc, pll_sqsum))
  return;

 ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work,
         msecs_to_jiffies(ATH_PLL_WORK_INTERVAL));
}

/*
 * PA Pre-distortion.
 */

static void ath_paprd_activate(struct ath_softc *sc)
{
 struct ath_hw *ah = sc->sc_ah;
 struct ath_common *common = ath9k_hw_common(ah);
 struct ath9k_hw_cal_data *caldata = ah->caldata;
 int chain;

 if (!caldata || !test_bit(PAPRD_DONE, &caldata->cal_flags)) {
  ath_dbg(common, CALIBRATE, "Failed to activate PAPRD\n");
  return;
 }

 ar9003_paprd_enable(ah, false);
 for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
  if (!(ah->txchainmask & BIT(chain)))
   continue;

  ar9003_paprd_populate_single_table(ah, caldata, chain);
 }

 ath_dbg(common, CALIBRATE, "Activating PAPRD\n");
 ar9003_paprd_enable(ah, true);
}

static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int chain)
{
 struct ieee80211_hw *hw = sc->hw;
 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
 struct ath_hw *ah = sc->sc_ah;
 struct ath_common *common = ath9k_hw_common(ah);
 struct ath_tx_control txctl;
 unsigned long time_left;

 memset(&txctl, 0, sizeof(txctl));
 txctl.txq = sc->tx.txq_map[IEEE80211_AC_BE];

 memset(tx_info, 0, sizeof(*tx_info));
 tx_info->band = sc->cur_chandef.chan->band;
 tx_info->flags |= IEEE80211_TX_CTL_NO_ACK;
 tx_info->control.rates[0].idx = 0;
 tx_info->control.rates[0].count = 1;
 tx_info->control.rates[0].flags = IEEE80211_TX_RC_MCS;
 tx_info->control.rates[1].idx = -1;

 init_completion(&sc->paprd_complete);
 txctl.paprd = BIT(chain);

 if (ath_tx_start(hw, skb, &txctl) != 0) {
  ath_dbg(common, CALIBRATE, "PAPRD TX failed\n");
  dev_kfree_skb_any(skb);
  return false;
 }

 time_left = wait_for_completion_timeout(&sc->paprd_complete,
   msecs_to_jiffies(ATH_PAPRD_TIMEOUT));

 if (!time_left)
  ath_dbg(common, CALIBRATE,
   "Timeout waiting for paprd training on TX chain %d\n",
   chain);

 return !!time_left;
}

void ath_paprd_calibrate(struct work_struct *work)
{
 struct ath_softc *sc = container_of(work, struct ath_softc, paprd_work);
 struct ieee80211_hw *hw = sc->hw;
 struct ath_hw *ah = sc->sc_ah;
 struct ieee80211_hdr *hdr;
 struct sk_buff *skb = NULL;
 struct ath9k_hw_cal_data *caldata = ah->caldata;
 struct ath_common *common = ath9k_hw_common(ah);
 int ftype;
 int chain_ok = 0;
 int chain;
 int len = 1800;
 int ret;

 if (!caldata ||
     !test_bit(PAPRD_PACKET_SENT, &caldata->cal_flags) ||
     test_bit(PAPRD_DONE, &caldata->cal_flags)) {
  ath_dbg(common, CALIBRATE, "Skipping PAPRD calibration\n");
  return;
 }

 ath9k_ps_wakeup(sc);

 if (ar9003_paprd_init_table(ah) < 0)
  goto fail_paprd;

 skb = alloc_skb(len, GFP_KERNEL);
 if (!skb)
  goto fail_paprd;

 skb_put(skb, len);
 memset(skb->data, 0, len);
 hdr = (struct ieee80211_hdr *)skb->data;
 ftype = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC;
 hdr->frame_control = cpu_to_le16(ftype);
 hdr->duration_id = cpu_to_le16(10);
 memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN);
 memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN);
 memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN);

 for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
  if (!(ah->txchainmask & BIT(chain)))
   continue;

  chain_ok = 0;
  ar9003_paprd_setup_gain_table(ah, chain);

  ath_dbg(common, CALIBRATE,
   "Sending PAPRD training frame on chain %d\n", chain);
  if (!ath_paprd_send_frame(sc, skb, chain))
   goto fail_paprd;

  if (!ar9003_paprd_is_done(ah)) {
   ath_dbg(common, CALIBRATE,
    "PAPRD not yet done on chain %d\n", chain);
   break;
  }

  ret = ar9003_paprd_create_curve(ah, caldata, chain);
  if (ret == -EINPROGRESS) {
   ath_dbg(common, CALIBRATE,
    "PAPRD curve on chain %d needs to be re-trained\n",
    chain);
   break;
  } else if (ret) {
   ath_dbg(common, CALIBRATE,
    "PAPRD create curve failed on chain %d\n",
    chain);
   break;
  }

  chain_ok = 1;
 }
 kfree_skb(skb);

 if (chain_ok) {
  set_bit(PAPRD_DONE, &caldata->cal_flags);
  ath_paprd_activate(sc);
 }

fail_paprd:
 ath9k_ps_restore(sc);
}

/*
 *  ANI performs periodic noise floor calibration
 *  that is used to adjust and optimize the chip performance.  This
 *  takes environmental changes (location, temperature) into account.
 *  When the task is complete, it reschedules itself depending on the
 *  appropriate interval that was calculated.
 */

void ath_ani_calibrate(struct timer_list *t)
{
 struct ath_common *common = timer_container_of(common, t, ani.timer);
 struct ath_softc *sc = common->priv;
 struct ath_hw *ah = sc->sc_ah;
 bool longcal = false;
 bool shortcal = false;
 bool aniflag = false;
 unsigned int timestamp = jiffies_to_msecs(jiffies);
 u32 cal_interval, short_cal_interval, long_cal_interval;
 unsigned long flags;

 if (ah->caldata && test_bit(NFCAL_INTF, &ah->caldata->cal_flags))
  long_cal_interval = ATH_LONG_CALINTERVAL_INT;
 else
  long_cal_interval = ATH_LONG_CALINTERVAL;

 short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
  ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;

 /* Only calibrate if awake */
 if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) {
  if (++ah->ani_skip_count >= ATH_ANI_MAX_SKIP_COUNT) {
   spin_lock_irqsave(&sc->sc_pm_lock, flags);
   sc->ps_flags |= PS_WAIT_FOR_ANI;
   spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
  }
  goto set_timer;
 }
 ah->ani_skip_count = 0;
 spin_lock_irqsave(&sc->sc_pm_lock, flags);
 sc->ps_flags &= ~PS_WAIT_FOR_ANI;
 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);

 ath9k_ps_wakeup(sc);

 /* Long calibration runs independently of short calibration. */
 if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) {
  longcal = true;
  common->ani.longcal_timer = timestamp;
 }

 /* Short calibration applies only while caldone is false */
 if (!common->ani.caldone) {
  if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) {
   shortcal = true;
   common->ani.shortcal_timer = timestamp;
   common->ani.resetcal_timer = timestamp;
  }
 } else {
  if ((timestamp - common->ani.resetcal_timer) >=
      ATH_RESTART_CALINTERVAL) {
   common->ani.caldone = ath9k_hw_reset_calvalid(ah);
   if (common->ani.caldone)
    common->ani.resetcal_timer = timestamp;
  }
 }

 /* Verify whether we must check ANI */
 if ((timestamp - common->ani.checkani_timer) >= ah->config.ani_poll_interval) {
  aniflag = true;
  common->ani.checkani_timer = timestamp;
 }

 /* Call ANI routine if necessary */
 if (aniflag) {
  spin_lock_irqsave(&common->cc_lock, flags);
  ath9k_hw_ani_monitor(ah, ah->curchan);
  ath_update_survey_stats(sc);
  spin_unlock_irqrestore(&common->cc_lock, flags);
 }

 /* Perform calibration if necessary */
 if (longcal || shortcal) {
  int ret = ath9k_hw_calibrate(ah, ah->curchan, ah->rxchainmask,
          longcal);
  if (ret < 0) {
   common->ani.caldone = 0;
   ath9k_queue_reset(sc, RESET_TYPE_CALIBRATION);
   return;
  }

  common->ani.caldone = ret;
 }

 ath_dbg(common, ANI,
  "Calibration @%lu finished: %s %s %s, caldone: %s\n",
  jiffies,
  longcal ? "long" : "", shortcal ? "short" : "",
  aniflag ? "ani" : "", common->ani.caldone ? "true" : "false");

 ath9k_ps_restore(sc);

set_timer:
 /*
* Set timer interval based on previous results.
* The interval must be the shortest necessary to satisfy ANI,
* short calibration and long calibration.
*/

 cal_interval = ATH_LONG_CALINTERVAL;
 cal_interval = min(cal_interval, (u32)ah->config.ani_poll_interval);
 if (!common->ani.caldone)
  cal_interval = min(cal_interval, (u32)short_cal_interval);

 mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval));

 if (ar9003_is_paprd_enabled(ah) && ah->caldata) {
  if (!test_bit(PAPRD_DONE, &ah->caldata->cal_flags)) {
   ieee80211_queue_work(sc->hw, &sc->paprd_work);
  } else if (!ah->paprd_table_write_done) {
   ath9k_ps_wakeup(sc);
   ath_paprd_activate(sc);
   ath9k_ps_restore(sc);
  }
 }
}

void ath_start_ani(struct ath_softc *sc)
{
 struct ath_hw *ah = sc->sc_ah;
 struct ath_common *common = ath9k_hw_common(ah);
 unsigned long timestamp = jiffies_to_msecs(jiffies);

 if (common->disable_ani ||
     !test_bit(ATH_OP_ANI_RUN, &common->op_flags) ||
     sc->cur_chan->offchannel)
  return;

 common->ani.longcal_timer = timestamp;
 common->ani.shortcal_timer = timestamp;
 common->ani.checkani_timer = timestamp;

 ath_dbg(common, ANI, "Starting ANI\n");
 mod_timer(&common->ani.timer,
    jiffies + msecs_to_jiffies((u32)ah->config.ani_poll_interval));
}

void ath_stop_ani(struct ath_softc *sc)
{
 struct ath_common *common = ath9k_hw_common(sc->sc_ah);

 ath_dbg(common, ANI, "Stopping ANI\n");
 timer_delete_sync(&common->ani.timer);
}

void ath_check_ani(struct ath_softc *sc)
{
 struct ath_hw *ah = sc->sc_ah;
 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;

 /*
 * Check for the various conditions in which ANI has to
 * be stopped.
 */

 if (ah->opmode == NL80211_IFTYPE_ADHOC) {
  if (!cur_conf->enable_beacon)
   goto stop_ani;
 } else if (ah->opmode == NL80211_IFTYPE_AP) {
  if (!cur_conf->enable_beacon) {
   /*
 * Disable ANI only when there are no
 * associated stations.
 */

   if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags))
    goto stop_ani;
  }
 } else if (ah->opmode == NL80211_IFTYPE_STATION) {
  if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags))
   goto stop_ani;
 }

 if (!test_bit(ATH_OP_ANI_RUN, &common->op_flags)) {
  set_bit(ATH_OP_ANI_RUN, &common->op_flags);
  ath_start_ani(sc);
 }

 return;

stop_ani:
 clear_bit(ATH_OP_ANI_RUN, &common->op_flags);
 ath_stop_ani(sc);
}

void ath_update_survey_nf(struct ath_softc *sc, int channel)
{
 struct ath_hw *ah = sc->sc_ah;
 struct ath9k_channel *chan = &ah->channels[channel];
 struct survey_info *survey = &sc->survey[channel];

 if (chan->noisefloor) {
  survey->filled |= SURVEY_INFO_NOISE_DBM;
  survey->noise = ath9k_hw_getchan_noise(ah, chan,
             chan->noisefloor);
 }
}

/*
 * Updates the survey statistics and returns the busy time since last
 * update in %, if the measurement duration was long enough for the
 * result to be useful, -1 otherwise.
 */

int ath_update_survey_stats(struct ath_softc *sc)
{
 struct ath_hw *ah = sc->sc_ah;
 struct ath_common *common = ath9k_hw_common(ah);
 int pos = ah->curchan - &ah->channels[0];
 struct survey_info *survey = &sc->survey[pos];
 struct ath_cycle_counters *cc = &common->cc_survey;
 unsigned int div = common->clockrate * 1000;
 int ret = 0;

 if (!ah->curchan)
  return -1;

 if (ah->power_mode == ATH9K_PM_AWAKE)
  ath_hw_cycle_counters_update(common);

 if (cc->cycles > 0) {
  survey->filled |= SURVEY_INFO_TIME |
   SURVEY_INFO_TIME_BUSY |
   SURVEY_INFO_TIME_RX |
   SURVEY_INFO_TIME_TX;
  survey->time += cc->cycles / div;
  survey->time_busy += cc->rx_busy / div;
  survey->time_rx += cc->rx_frame / div;
  survey->time_tx += cc->tx_frame / div;
 }

 if (cc->cycles < div)
  return -1;

 if (cc->cycles > 0)
  ret = cc->rx_busy * 100 / cc->cycles;

 memset(cc, 0, sizeof(*cc));

 ath_update_survey_nf(sc, pos);

 return ret;
}

Messung V0.5
C=97 H=93 G=94

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.