Merge git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
authorJohn W. Linville <linville@tuxdriver.com>
Wed, 25 Jun 2014 19:15:14 +0000 (15:15 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 25 Jun 2014 19:26:36 +0000 (15:26 -0400)
1  2 
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/mwifiex/cfg80211.c

@@@ -2206,402 -2230,6 +2206,403 @@@ static void ath9k_sw_scan_complete(stru
        clear_bit(ATH_OP_SCANNING, &common->op_flags);
  }
  
-                        struct cfg80211_scan_request *req)
 +static int ath_scan_channel_duration(struct ath_softc *sc,
 +                                   struct ieee80211_channel *chan)
 +{
 +      struct cfg80211_scan_request *req = sc->offchannel.scan_req;
 +
 +      if (!req->n_ssids || (chan->flags & IEEE80211_CHAN_NO_IR))
 +              return (HZ / 9); /* ~110 ms */
 +
 +      return (HZ / 16); /* ~60 ms */
 +}
 +
 +static void
 +ath_scan_next_channel(struct ath_softc *sc)
 +{
 +      struct cfg80211_scan_request *req = sc->offchannel.scan_req;
 +      struct ieee80211_channel *chan;
 +
 +      if (sc->offchannel.scan_idx >= req->n_channels) {
 +              sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
 +              ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false),
 +                                 NULL);
 +              return;
 +      }
 +
 +      chan = req->channels[sc->offchannel.scan_idx++];
 +      sc->offchannel.duration = ath_scan_channel_duration(sc, chan);
 +      sc->offchannel.state = ATH_OFFCHANNEL_PROBE_SEND;
 +      ath_chanctx_offchan_switch(sc, chan);
 +}
 +
 +static void ath_offchannel_next(struct ath_softc *sc)
 +{
 +      struct ieee80211_vif *vif;
 +
 +      if (sc->offchannel.scan_req) {
 +              vif = sc->offchannel.scan_vif;
 +              sc->offchannel.chan.txpower = vif->bss_conf.txpower;
 +              ath_scan_next_channel(sc);
 +      } else if (sc->offchannel.roc_vif) {
 +              vif = sc->offchannel.roc_vif;
 +              sc->offchannel.chan.txpower = vif->bss_conf.txpower;
 +              sc->offchannel.duration = sc->offchannel.roc_duration;
 +              sc->offchannel.state = ATH_OFFCHANNEL_ROC_START;
 +              ath_chanctx_offchan_switch(sc, sc->offchannel.roc_chan);
 +      } else {
 +              ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false),
 +                                 NULL);
 +              sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
 +              if (sc->ps_idle)
 +                      ath_cancel_work(sc);
 +      }
 +}
 +
 +static void ath_roc_complete(struct ath_softc *sc, bool abort)
 +{
 +      sc->offchannel.roc_vif = NULL;
 +      sc->offchannel.roc_chan = NULL;
 +      if (!abort)
 +              ieee80211_remain_on_channel_expired(sc->hw);
 +      ath_offchannel_next(sc);
 +      ath9k_ps_restore(sc);
 +}
 +
 +static void ath_scan_complete(struct ath_softc *sc, bool abort)
 +{
 +      struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 +
 +      sc->offchannel.scan_req = NULL;
 +      sc->offchannel.scan_vif = NULL;
 +      sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
 +      ieee80211_scan_completed(sc->hw, abort);
 +      clear_bit(ATH_OP_SCANNING, &common->op_flags);
 +      ath_offchannel_next(sc);
 +      ath9k_ps_restore(sc);
 +}
 +
 +static void ath_scan_send_probe(struct ath_softc *sc,
 +                              struct cfg80211_ssid *ssid)
 +{
 +      struct cfg80211_scan_request *req = sc->offchannel.scan_req;
 +      struct ieee80211_vif *vif = sc->offchannel.scan_vif;
 +      struct ath_tx_control txctl = {};
 +      struct sk_buff *skb;
 +      struct ieee80211_tx_info *info;
 +      int band = sc->offchannel.chan.chandef.chan->band;
 +
 +      skb = ieee80211_probereq_get(sc->hw, vif,
 +                      ssid->ssid, ssid->ssid_len, req->ie_len);
 +      if (!skb)
 +              return;
 +
 +      info = IEEE80211_SKB_CB(skb);
 +      if (req->no_cck)
 +              info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
 +
 +      if (req->ie_len)
 +              memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len);
 +
 +      skb_set_queue_mapping(skb, IEEE80211_AC_VO);
 +
 +      if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, NULL))
 +              goto error;
 +
 +      txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
 +      txctl.force_channel = true;
 +      if (ath_tx_start(sc->hw, skb, &txctl))
 +              goto error;
 +
 +      return;
 +
 +error:
 +      ieee80211_free_txskb(sc->hw, skb);
 +}
 +
 +static void ath_scan_channel_start(struct ath_softc *sc)
 +{
 +      struct cfg80211_scan_request *req = sc->offchannel.scan_req;
 +      int i;
 +
 +      if (!(sc->cur_chan->chandef.chan->flags & IEEE80211_CHAN_NO_IR) &&
 +          req->n_ssids) {
 +              for (i = 0; i < req->n_ssids; i++)
 +                      ath_scan_send_probe(sc, &req->ssids[i]);
 +
 +      }
 +
 +      sc->offchannel.state = ATH_OFFCHANNEL_PROBE_WAIT;
 +      mod_timer(&sc->offchannel.timer, jiffies + sc->offchannel.duration);
 +}
 +
 +void ath_offchannel_channel_change(struct ath_softc *sc)
 +{
 +      switch (sc->offchannel.state) {
 +      case ATH_OFFCHANNEL_PROBE_SEND:
 +              if (!sc->offchannel.scan_req)
 +                      return;
 +
 +              if (sc->cur_chan->chandef.chan !=
 +                  sc->offchannel.chan.chandef.chan)
 +                      return;
 +
 +              ath_scan_channel_start(sc);
 +              break;
 +      case ATH_OFFCHANNEL_IDLE:
 +              if (!sc->offchannel.scan_req)
 +                      return;
 +
 +              ath_scan_complete(sc, false);
 +              break;
 +      case ATH_OFFCHANNEL_ROC_START:
 +              if (sc->cur_chan != &sc->offchannel.chan)
 +                      break;
 +
 +              sc->offchannel.state = ATH_OFFCHANNEL_ROC_WAIT;
 +              mod_timer(&sc->offchannel.timer, jiffies +
 +                        msecs_to_jiffies(sc->offchannel.duration));
 +              ieee80211_ready_on_channel(sc->hw);
 +              break;
 +      case ATH_OFFCHANNEL_ROC_DONE:
 +              ath_roc_complete(sc, false);
 +              break;
 +      default:
 +              break;
 +      }
 +}
 +
 +void ath_offchannel_timer(unsigned long data)
 +{
 +      struct ath_softc *sc = (struct ath_softc *)data;
 +      struct ath_chanctx *ctx;
 +
 +      switch (sc->offchannel.state) {
 +      case ATH_OFFCHANNEL_PROBE_WAIT:
 +              if (!sc->offchannel.scan_req)
 +                      return;
 +
 +              /* get first active channel context */
 +              ctx = ath_chanctx_get_oper_chan(sc, true);
 +              if (ctx->active) {
 +                      sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND;
 +                      ath_chanctx_switch(sc, ctx, NULL);
 +                      mod_timer(&sc->offchannel.timer, jiffies + HZ / 10);
 +                      break;
 +              }
 +              /* fall through */
 +      case ATH_OFFCHANNEL_SUSPEND:
 +              if (!sc->offchannel.scan_req)
 +                      return;
 +
 +              ath_scan_next_channel(sc);
 +              break;
 +      case ATH_OFFCHANNEL_ROC_START:
 +      case ATH_OFFCHANNEL_ROC_WAIT:
 +              ctx = ath_chanctx_get_oper_chan(sc, false);
 +              sc->offchannel.state = ATH_OFFCHANNEL_ROC_DONE;
 +              ath_chanctx_switch(sc, ctx, NULL);
 +              break;
 +      default:
 +              break;
 +      }
 +}
 +
 +static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
++                       struct ieee80211_scan_request *hw_req)
 +{
++      struct cfg80211_scan_request *req = &hw_req->req;
 +      struct ath_softc *sc = hw->priv;
 +      struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 +      int ret = 0;
 +
 +      mutex_lock(&sc->mutex);
 +
 +      if (WARN_ON(sc->offchannel.scan_req)) {
 +              ret = -EBUSY;
 +              goto out;
 +      }
 +
 +      ath9k_ps_wakeup(sc);
 +      set_bit(ATH_OP_SCANNING, &common->op_flags);
 +      sc->offchannel.scan_vif = vif;
 +      sc->offchannel.scan_req = req;
 +      sc->offchannel.scan_idx = 0;
 +
 +      if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE)
 +              ath_offchannel_next(sc);
 +
 +out:
 +      mutex_unlock(&sc->mutex);
 +
 +      return ret;
 +}
 +
 +static void ath9k_cancel_hw_scan(struct ieee80211_hw *hw,
 +                               struct ieee80211_vif *vif)
 +{
 +      struct ath_softc *sc = hw->priv;
 +
 +      mutex_lock(&sc->mutex);
 +      del_timer_sync(&sc->offchannel.timer);
 +      ath_scan_complete(sc, true);
 +      mutex_unlock(&sc->mutex);
 +}
 +
 +static int ath9k_remain_on_channel(struct ieee80211_hw *hw,
 +                                 struct ieee80211_vif *vif,
 +                                 struct ieee80211_channel *chan, int duration,
 +                                 enum ieee80211_roc_type type)
 +{
 +      struct ath_softc *sc = hw->priv;
 +      int ret = 0;
 +
 +      mutex_lock(&sc->mutex);
 +
 +      if (WARN_ON(sc->offchannel.roc_vif)) {
 +              ret = -EBUSY;
 +              goto out;
 +      }
 +
 +      ath9k_ps_wakeup(sc);
 +      sc->offchannel.roc_vif = vif;
 +      sc->offchannel.roc_chan = chan;
 +      sc->offchannel.roc_duration = duration;
 +
 +      if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE)
 +              ath_offchannel_next(sc);
 +
 +out:
 +      mutex_unlock(&sc->mutex);
 +
 +      return ret;
 +}
 +
 +static int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw)
 +{
 +      struct ath_softc *sc = hw->priv;
 +
 +      mutex_lock(&sc->mutex);
 +
 +      del_timer_sync(&sc->offchannel.timer);
 +
 +      if (sc->offchannel.roc_vif) {
 +              if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START)
 +                      ath_roc_complete(sc, true);
 +      }
 +
 +      mutex_unlock(&sc->mutex);
 +
 +      return 0;
 +}
 +
 +static int ath9k_add_chanctx(struct ieee80211_hw *hw,
 +                           struct ieee80211_chanctx_conf *conf)
 +{
 +      struct ath_softc *sc = hw->priv;
 +      struct ath_chanctx *ctx, **ptr;
 +      int pos;
 +
 +      mutex_lock(&sc->mutex);
 +
 +      ath_for_each_chanctx(sc, ctx) {
 +              if (ctx->assigned)
 +                      continue;
 +
 +              ptr = (void *) conf->drv_priv;
 +              *ptr = ctx;
 +              ctx->assigned = true;
 +              pos = ctx - &sc->chanctx[0];
 +              ctx->hw_queue_base = pos * IEEE80211_NUM_ACS;
 +              ath_chanctx_set_channel(sc, ctx, &conf->def);
 +              mutex_unlock(&sc->mutex);
 +              return 0;
 +      }
 +      mutex_unlock(&sc->mutex);
 +      return -ENOSPC;
 +}
 +
 +
 +static void ath9k_remove_chanctx(struct ieee80211_hw *hw,
 +                               struct ieee80211_chanctx_conf *conf)
 +{
 +      struct ath_softc *sc = hw->priv;
 +      struct ath_chanctx *ctx = ath_chanctx_get(conf);
 +
 +      mutex_lock(&sc->mutex);
 +      ctx->assigned = false;
 +      ctx->hw_queue_base = -1;
 +      ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_UNASSIGN);
 +      mutex_unlock(&sc->mutex);
 +}
 +
 +static void ath9k_change_chanctx(struct ieee80211_hw *hw,
 +                               struct ieee80211_chanctx_conf *conf,
 +                               u32 changed)
 +{
 +      struct ath_softc *sc = hw->priv;
 +      struct ath_chanctx *ctx = ath_chanctx_get(conf);
 +
 +      mutex_lock(&sc->mutex);
 +      ath_chanctx_set_channel(sc, ctx, &conf->def);
 +      mutex_unlock(&sc->mutex);
 +}
 +
 +static int ath9k_assign_vif_chanctx(struct ieee80211_hw *hw,
 +                                  struct ieee80211_vif *vif,
 +                                  struct ieee80211_chanctx_conf *conf)
 +{
 +      struct ath_softc *sc = hw->priv;
 +      struct ath_vif *avp = (void *)vif->drv_priv;
 +      struct ath_chanctx *ctx = ath_chanctx_get(conf);
 +      int i;
 +
 +      mutex_lock(&sc->mutex);
 +      avp->chanctx = ctx;
 +      list_add_tail(&avp->list, &ctx->vifs);
 +      ath9k_calculate_summary_state(sc, ctx);
 +      for (i = 0; i < IEEE80211_NUM_ACS; i++)
 +              vif->hw_queue[i] = ctx->hw_queue_base + i;
 +      mutex_unlock(&sc->mutex);
 +
 +      return 0;
 +}
 +
 +static void ath9k_unassign_vif_chanctx(struct ieee80211_hw *hw,
 +                                     struct ieee80211_vif *vif,
 +                                     struct ieee80211_chanctx_conf *conf)
 +{
 +      struct ath_softc *sc = hw->priv;
 +      struct ath_vif *avp = (void *)vif->drv_priv;
 +      struct ath_chanctx *ctx = ath_chanctx_get(conf);
 +      int ac;
 +
 +      mutex_lock(&sc->mutex);
 +      avp->chanctx = NULL;
 +      list_del(&avp->list);
 +      ath9k_calculate_summary_state(sc, ctx);
 +      for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
 +              vif->hw_queue[ac] = IEEE80211_INVAL_HW_QUEUE;
 +      mutex_unlock(&sc->mutex);
 +}
 +
 +void ath9k_fill_chanctx_ops(void)
 +{
 +      if (!ath9k_use_chanctx)
 +              return;
 +
 +      ath9k_ops.hw_scan = ath9k_hw_scan;
 +      ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan;
 +      ath9k_ops.remain_on_channel  = ath9k_remain_on_channel;
 +      ath9k_ops.cancel_remain_on_channel = ath9k_cancel_remain_on_channel;
 +      ath9k_ops.add_chanctx        = ath9k_add_chanctx;
 +      ath9k_ops.remove_chanctx     = ath9k_remove_chanctx;
 +      ath9k_ops.change_chanctx     = ath9k_change_chanctx;
 +      ath9k_ops.assign_vif_chanctx = ath9k_assign_vif_chanctx;
 +      ath9k_ops.unassign_vif_chanctx = ath9k_unassign_vif_chanctx;
 +      ath9k_ops.mgd_prepare_tx = ath9k_chanctx_force_active;
 +}
 +
  struct ieee80211_ops ath9k_ops = {
        .tx                 = ath9k_tx,
        .start              = ath9k_start,