]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac802...
authorJohn W. Linville <linville@tuxdriver.com>
Fri, 9 Aug 2013 19:08:10 +0000 (15:08 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 9 Aug 2013 19:08:10 +0000 (15:08 -0400)
1  2 
net/mac80211/cfg.c
net/wireless/nl80211.c

diff --combined net/mac80211/cfg.c
index 973594b229f400bbae825034d32f69b07c51c9f7,44449ceb79664026037113126e2f13d0ed0ca16a..31fc2247bc372dffe9fade359f706863daa8b297
@@@ -672,8 -672,6 +672,8 @@@ static void ieee80211_get_et_stats(stru
                        if (sta->sdata->dev != dev)
                                continue;
  
 +                      sinfo.filled = 0;
 +                      sta_set_sinfo(sta, &sinfo);
                        i = 0;
                        ADD_STA_STATS(sta);
                }
@@@ -862,8 -860,8 +862,8 @@@ static int ieee80211_set_probe_resp(str
        return 0;
  }
  
static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
-                                  struct cfg80211_beacon_data *params)
+ int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+                           struct cfg80211_beacon_data *params)
  {
        struct beacon_data *new, *old;
        int new_head_len, new_tail_len;
@@@ -1026,6 -1024,12 +1026,12 @@@ static int ieee80211_change_beacon(stru
  
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
  
+       /* don't allow changing the beacon while CSA is in place - offset
+        * of channel switch counter may change
+        */
+       if (sdata->vif.csa_active)
+               return -EBUSY;
        old = rtnl_dereference(sdata->u.ap.beacon);
        if (!old)
                return -ENOENT;
@@@ -1050,6 -1054,10 +1056,10 @@@ static int ieee80211_stop_ap(struct wip
                return -ENOENT;
        old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp);
  
+       /* abort any running channel switch */
+       sdata->vif.csa_active = false;
+       cancel_work_sync(&sdata->csa_finalize_work);
        /* turn off carrier for this interface and dependent VLANs */
        list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
                netif_carrier_off(vlan->dev);
@@@ -2777,6 -2785,178 +2787,178 @@@ static int ieee80211_start_radar_detect
        return 0;
  }
  
+ static struct cfg80211_beacon_data *
+ cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
+ {
+       struct cfg80211_beacon_data *new_beacon;
+       u8 *pos;
+       int len;
+       len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
+             beacon->proberesp_ies_len + beacon->assocresp_ies_len +
+             beacon->probe_resp_len;
+       new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
+       if (!new_beacon)
+               return NULL;
+       pos = (u8 *)(new_beacon + 1);
+       if (beacon->head_len) {
+               new_beacon->head_len = beacon->head_len;
+               new_beacon->head = pos;
+               memcpy(pos, beacon->head, beacon->head_len);
+               pos += beacon->head_len;
+       }
+       if (beacon->tail_len) {
+               new_beacon->tail_len = beacon->tail_len;
+               new_beacon->tail = pos;
+               memcpy(pos, beacon->tail, beacon->tail_len);
+               pos += beacon->tail_len;
+       }
+       if (beacon->beacon_ies_len) {
+               new_beacon->beacon_ies_len = beacon->beacon_ies_len;
+               new_beacon->beacon_ies = pos;
+               memcpy(pos, beacon->beacon_ies, beacon->beacon_ies_len);
+               pos += beacon->beacon_ies_len;
+       }
+       if (beacon->proberesp_ies_len) {
+               new_beacon->proberesp_ies_len = beacon->proberesp_ies_len;
+               new_beacon->proberesp_ies = pos;
+               memcpy(pos, beacon->proberesp_ies, beacon->proberesp_ies_len);
+               pos += beacon->proberesp_ies_len;
+       }
+       if (beacon->assocresp_ies_len) {
+               new_beacon->assocresp_ies_len = beacon->assocresp_ies_len;
+               new_beacon->assocresp_ies = pos;
+               memcpy(pos, beacon->assocresp_ies, beacon->assocresp_ies_len);
+               pos += beacon->assocresp_ies_len;
+       }
+       if (beacon->probe_resp_len) {
+               new_beacon->probe_resp_len = beacon->probe_resp_len;
+               beacon->probe_resp = pos;
+               memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
+               pos += beacon->probe_resp_len;
+       }
+       return new_beacon;
+ }
+ void ieee80211_csa_finalize_work(struct work_struct *work)
+ {
+       struct ieee80211_sub_if_data *sdata =
+               container_of(work, struct ieee80211_sub_if_data,
+                            csa_finalize_work);
+       struct ieee80211_local *local = sdata->local;
+       int err, changed;
+       if (!ieee80211_sdata_running(sdata))
+               return;
+       if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
+               return;
+       sdata->radar_required = sdata->csa_radar_required;
+       err = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
+                                          &changed);
+       if (WARN_ON(err < 0))
+               return;
+       err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
+       if (err < 0)
+               return;
+       changed |= err;
+       kfree(sdata->u.ap.next_beacon);
+       sdata->u.ap.next_beacon = NULL;
+       sdata->vif.csa_active = false;
+       ieee80211_wake_queues_by_reason(&sdata->local->hw,
+                                       IEEE80211_MAX_QUEUE_MAP,
+                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       ieee80211_bss_info_change_notify(sdata, changed);
+       cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef);
+ }
+ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+                                   struct cfg80211_csa_settings *params)
+ {
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
+       struct ieee80211_chanctx_conf *chanctx_conf;
+       struct ieee80211_chanctx *chanctx;
+       int err, num_chanctx;
+       if (!list_empty(&local->roc_list) || local->scanning)
+               return -EBUSY;
+       if (sdata->wdev.cac_started)
+               return -EBUSY;
+       if (cfg80211_chandef_identical(&params->chandef,
+                                      &sdata->vif.bss_conf.chandef))
+               return -EINVAL;
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+       if (!chanctx_conf) {
+               rcu_read_unlock();
+               return -EBUSY;
+       }
+       /* don't handle for multi-VIF cases */
+       chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
+       if (chanctx->refcount > 1) {
+               rcu_read_unlock();
+               return -EBUSY;
+       }
+       num_chanctx = 0;
+       list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
+               num_chanctx++;
+       rcu_read_unlock();
+       if (num_chanctx > 1)
+               return -EBUSY;
+       /* don't allow another channel switch if one is already active. */
+       if (sdata->vif.csa_active)
+               return -EBUSY;
+       /* only handle AP for now. */
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_AP:
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+       sdata->u.ap.next_beacon = cfg80211_beacon_dup(&params->beacon_after);
+       if (!sdata->u.ap.next_beacon)
+               return -ENOMEM;
+       sdata->csa_counter_offset_beacon = params->counter_offset_beacon;
+       sdata->csa_counter_offset_presp = params->counter_offset_presp;
+       sdata->csa_radar_required = params->radar_required;
+       if (params->block_tx)
+               ieee80211_stop_queues_by_reason(&local->hw,
+                               IEEE80211_MAX_QUEUE_MAP,
+                               IEEE80211_QUEUE_STOP_REASON_CSA);
+       err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
+       if (err < 0)
+               return err;
+       local->csa_chandef = params->chandef;
+       sdata->vif.csa_active = true;
+       ieee80211_bss_info_change_notify(sdata, err);
+       drv_channel_switch_beacon(sdata, &params->chandef);
+       return 0;
+ }
  static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                             struct ieee80211_channel *chan, bool offchan,
                             unsigned int wait, const u8 *buf, size_t len,
@@@ -3494,4 -3674,5 +3676,5 @@@ struct cfg80211_ops mac80211_config_op
        .get_et_strings = ieee80211_get_et_strings,
        .get_channel = ieee80211_cfg_get_channel,
        .start_radar_detection = ieee80211_start_radar_detection,
+       .channel_switch = ieee80211_channel_switch,
  };
diff --combined net/wireless/nl80211.c
index 587ff843cf944ef48a8eeb96d13bad1614ef22af,f7cb12178bd23a4e739e91a2a098381aea46a12f..adf1e98f4c3ebadbb86e35c21f58f0994260b2b1
@@@ -349,6 -349,11 +349,11 @@@ static const struct nla_policy nl80211_
        [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
                                  .len = IEEE80211_MAX_DATA_LEN },
        [NL80211_ATTR_PEER_AID] = { .type = NLA_U16 },
+       [NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 },
+       [NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG },
+       [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
+       [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 },
+       [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 },
  };
  
  /* policy for the key attributes */
@@@ -449,12 -454,10 +454,12 @@@ static int nl80211_prepare_wdev_dump(st
                        goto out_unlock;
                }
                *rdev = wiphy_to_dev((*wdev)->wiphy);
 -              cb->args[0] = (*rdev)->wiphy_idx;
 +              /* 0 is the first index - add 1 to parse only once */
 +              cb->args[0] = (*rdev)->wiphy_idx + 1;
                cb->args[1] = (*wdev)->identifier;
        } else {
 -              struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0]);
 +              /* subtract the 1 again here */
 +              struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
                struct wireless_dev *tmp;
  
                if (!wiphy) {
@@@ -1424,6 -1427,8 +1429,8 @@@ static int nl80211_send_wiphy(struct cf
                if (state->split) {
                        CMD(crit_proto_start, CRIT_PROTOCOL_START);
                        CMD(crit_proto_stop, CRIT_PROTOCOL_STOP);
+                       if (dev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
+                               CMD(channel_switch, CHANNEL_SWITCH);
                }
  
  #ifdef CONFIG_NL80211_TESTMODE
@@@ -4807,9 -4812,9 +4814,9 @@@ do {                                                                        
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, 0, 1,
                                  mask, NL80211_MESHCONF_FORWARDING,
                                  nla_get_u8);
 -      FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, 1, 255,
 +      FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, -255, 0,
                                  mask, NL80211_MESHCONF_RSSI_THRESHOLD,
 -                                nla_get_u32);
 +                                nla_get_s32);
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, 0, 16,
                                  mask, NL80211_MESHCONF_HT_OPMODE,
                                  nla_get_u16);
@@@ -5615,6 -5620,111 +5622,111 @@@ static int nl80211_start_radar_detectio
        return err;
  }
  
+ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
+ {
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_csa_settings params;
+       /* csa_attrs is defined static to avoid waste of stack size - this
+        * function is called under RTNL lock, so this should not be a problem.
+        */
+       static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
+       u8 radar_detect_width = 0;
+       int err;
+       if (!rdev->ops->channel_switch ||
+           !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
+               return -EOPNOTSUPP;
+       /* may add IBSS support later */
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+               return -EOPNOTSUPP;
+       memset(&params, 0, sizeof(params));
+       if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
+           !info->attrs[NL80211_ATTR_CH_SWITCH_COUNT])
+               return -EINVAL;
+       /* only important for AP, IBSS and mesh create IEs internally */
+       if (!info->attrs[NL80211_ATTR_CSA_IES])
+               return -EINVAL;
+       /* useless if AP is not running */
+       if (!wdev->beacon_interval)
+               return -EINVAL;
+       params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
+       err = nl80211_parse_beacon(info->attrs, &params.beacon_after);
+       if (err)
+               return err;
+       err = nla_parse_nested(csa_attrs, NL80211_ATTR_MAX,
+                              info->attrs[NL80211_ATTR_CSA_IES],
+                              nl80211_policy);
+       if (err)
+               return err;
+       err = nl80211_parse_beacon(csa_attrs, &params.beacon_csa);
+       if (err)
+               return err;
+       if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON])
+               return -EINVAL;
+       params.counter_offset_beacon =
+               nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
+       if (params.counter_offset_beacon >= params.beacon_csa.tail_len)
+               return -EINVAL;
+       /* sanity check - counters should be the same */
+       if (params.beacon_csa.tail[params.counter_offset_beacon] !=
+           params.count)
+               return -EINVAL;
+       if (csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) {
+               params.counter_offset_presp =
+                       nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
+               if (params.counter_offset_presp >=
+                   params.beacon_csa.probe_resp_len)
+                       return -EINVAL;
+               if (params.beacon_csa.probe_resp[params.counter_offset_presp] !=
+                   params.count)
+                       return -EINVAL;
+       }
+       err = nl80211_parse_chandef(rdev, info, &params.chandef);
+       if (err)
+               return err;
+       if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
+               return -EINVAL;
+       err = cfg80211_chandef_dfs_required(wdev->wiphy, &params.chandef);
+       if (err < 0) {
+               return err;
+       } else if (err) {
+               radar_detect_width = BIT(params.chandef.width);
+               params.radar_required = true;
+       }
+       err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
+                                          params.chandef.chan,
+                                          CHAN_MODE_SHARED,
+                                          radar_detect_width);
+       if (err)
+               return err;
+       if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
+               params.block_tx = true;
+       return rdev_channel_switch(rdev, dev, &params);
+ }
  static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
                            u32 seq, int flags,
                            struct cfg80211_registered_device *rdev,
@@@ -6666,14 -6776,12 +6778,14 @@@ EXPORT_SYMBOL(cfg80211_testmode_alloc_e
  
  void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
  {
 +      struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
        void *hdr = ((void **)skb->cb)[1];
        struct nlattr *data = ((void **)skb->cb)[2];
  
        nla_nest_end(skb, data);
        genlmsg_end(skb, hdr);
 -      genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
 +      genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), skb, 0,
 +                              nl80211_testmode_mcgrp.id, gfp);
  }
  EXPORT_SYMBOL(cfg80211_testmode_event);
  #endif
@@@ -9365,7 -9473,15 +9477,15 @@@ static struct genl_ops nl80211_ops[] = 
                .flags = GENL_ADMIN_PERM,
                .internal_flags = NL80211_FLAG_NEED_WIPHY |
                                  NL80211_FLAG_NEED_RTNL,
-       }
+       },
+       {
+               .cmd = NL80211_CMD_CHANNEL_SWITCH,
+               .doit = nl80211_channel_switch,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
  };
  
  static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@@ -10390,8 -10506,7 +10510,8 @@@ void cfg80211_mgmt_tx_status(struct wir
  
        genlmsg_end(msg, hdr);
  
 -      genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
 +      genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
 +                              nl80211_mlme_mcgrp.id, gfp);
        return;
  
   nla_put_failure: