]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge remote-tracking branch 'wireless/master' into mac80211
authorJohannes Berg <johannes.berg@intel.com>
Mon, 30 Jul 2012 07:13:03 +0000 (09:13 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 30 Jul 2012 07:13:03 +0000 (09:13 +0200)
1  2 
net/mac80211/scan.c
net/wireless/reg.c

diff --combined net/mac80211/scan.c
index 7644181cb6b48ad4595586d9e230e34fbb3f73ea,bcaee5d1283915efdbc8c63f4365c7ed42d486dc..df36280ed78fd06e5cb7799c8cc46be275d42df1
@@@ -83,13 -83,14 +83,14 @@@ ieee80211_bss_info_update(struct ieee80
  
        cbss = cfg80211_inform_bss_frame(local->hw.wiphy, channel,
                                         mgmt, len, signal, GFP_ATOMIC);
        if (!cbss)
                return NULL;
  
        cbss->free_priv = ieee80211_rx_bss_free;
        bss = (void *)cbss->priv;
  
+       bss->device_ts = rx_status->device_timestamp;
        if (elems->parse_error) {
                if (beacon)
                        bss->corrupt_data |= IEEE80211_BSS_CORRUPT_BEACON;
  
        if (elems->tim && (!elems->parse_error ||
                           !(bss->valid_data & IEEE80211_BSS_VALID_DTIM))) {
-               struct ieee80211_tim_ie *tim_ie =
-                       (struct ieee80211_tim_ie *)elems->tim;
+               struct ieee80211_tim_ie *tim_ie = elems->tim;
                bss->dtim_period = tim_ie->dtim_period;
                if (!elems->parse_error)
                                bss->valid_data |= IEEE80211_BSS_VALID_DTIM;
        return bss;
  }
  
- ieee80211_rx_result
- ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
+ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
  {
        struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
-       struct ieee80211_mgmt *mgmt;
+       struct ieee80211_sub_if_data *sdata1, *sdata2;
+       struct ieee80211_mgmt *mgmt = (void *)skb->data;
        struct ieee80211_bss *bss;
        u8 *elements;
        struct ieee80211_channel *channel;
        size_t baselen;
        int freq;
-       __le16 fc;
-       bool presp, beacon = false;
+       bool beacon;
        struct ieee802_11_elems elems;
  
-       if (skb->len < 2)
-               return RX_DROP_UNUSABLE;
-       mgmt = (struct ieee80211_mgmt *) skb->data;
-       fc = mgmt->frame_control;
+       if (skb->len < 24 ||
+           (!ieee80211_is_probe_resp(mgmt->frame_control) &&
+            !ieee80211_is_beacon(mgmt->frame_control)))
+               return;
  
-       if (ieee80211_is_ctl(fc))
-               return RX_CONTINUE;
+       sdata1 = rcu_dereference(local->scan_sdata);
+       sdata2 = rcu_dereference(local->sched_scan_sdata);
  
-       if (skb->len < 24)
-               return RX_CONTINUE;
+       if (likely(!sdata1 && !sdata2))
+               return;
  
-       presp = ieee80211_is_probe_resp(fc);
-       if (presp) {
+       if (ieee80211_is_probe_resp(mgmt->frame_control)) {
                /* ignore ProbeResp to foreign address */
-               if (!ether_addr_equal(mgmt->da, sdata->vif.addr))
-                       return RX_DROP_MONITOR;
+               if ((!sdata1 || !ether_addr_equal(mgmt->da, sdata1->vif.addr)) &&
+                   (!sdata2 || !ether_addr_equal(mgmt->da, sdata2->vif.addr)))
+                       return;
  
-               presp = true;
                elements = mgmt->u.probe_resp.variable;
                baselen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+               beacon = false;
        } else {
-               beacon = ieee80211_is_beacon(fc);
                baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable);
                elements = mgmt->u.beacon.variable;
+               beacon = true;
        }
  
-       if (!presp && !beacon)
-               return RX_CONTINUE;
        if (baselen > skb->len)
-               return RX_DROP_MONITOR;
+               return;
  
        ieee802_11_parse_elems(elements, skb->len - baselen, &elems);
  
        else
                freq = rx_status->freq;
  
-       channel = ieee80211_get_channel(sdata->local->hw.wiphy, freq);
+       channel = ieee80211_get_channel(local->hw.wiphy, freq);
  
        if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
-               return RX_DROP_MONITOR;
+               return;
  
-       bss = ieee80211_bss_info_update(sdata->local, rx_status,
+       bss = ieee80211_bss_info_update(local, rx_status,
                                        mgmt, skb->len, &elems,
                                        channel, beacon);
        if (bss)
-               ieee80211_rx_bss_put(sdata->local, bss);
-       if (channel == sdata->local->oper_channel)
-               return RX_CONTINUE;
-       dev_kfree_skb(skb);
-       return RX_QUEUED;
+               ieee80211_rx_bss_put(local, bss);
  }
  
  /* return false if no more work */
@@@ -293,7 -282,13 +282,13 @@@ static void __ieee80211_scan_completed(
                return;
  
        if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {
-               int rc = drv_hw_scan(local, local->scan_sdata, local->hw_scan_req);
+               int rc;
+               rc = drv_hw_scan(local,
+                       rcu_dereference_protected(local->scan_sdata,
+                                                 lockdep_is_held(&local->mtx)),
+                       local->hw_scan_req);
                if (rc == 0)
                        return;
        }
        if (local->scan_req != local->int_scan_req)
                cfg80211_scan_done(local->scan_req, aborted);
        local->scan_req = NULL;
 -      local->scan_sdata = NULL;
 +      rcu_assign_pointer(local->scan_sdata, NULL);
  
        local->scanning = 0;
        local->scan_channel = NULL;
        ieee80211_mlme_notify_scan_completed(local);
        ieee80211_ibss_notify_scan_completed(local);
        ieee80211_mesh_notify_scan_completed(local);
-       ieee80211_queue_work(&local->hw, &local->work_work);
+       ieee80211_start_next_roc(local);
  }
  
  void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
@@@ -376,7 -371,7 +371,7 @@@ static int ieee80211_start_sw_scan(stru
  static bool ieee80211_can_scan(struct ieee80211_local *local,
                               struct ieee80211_sub_if_data *sdata)
  {
-       if (!list_empty(&local->work_list))
+       if (!list_empty(&local->roc_list))
                return false;
  
        if (sdata->vif.type == NL80211_IFTYPE_STATION &&
@@@ -394,7 -389,10 +389,10 @@@ void ieee80211_run_deferred_scan(struc
        if (!local->scan_req || local->scanning)
                return;
  
-       if (!ieee80211_can_scan(local, local->scan_sdata))
+       if (!ieee80211_can_scan(local,
+                               rcu_dereference_protected(
+                                       local->scan_sdata,
+                                       lockdep_is_held(&local->mtx))))
                return;
  
        ieee80211_queue_delayed_work(&local->hw, &local->scan_work,
@@@ -405,9 -403,12 +403,12 @@@ static void ieee80211_scan_state_send_p
                                            unsigned long *next_delay)
  {
        int i;
-       struct ieee80211_sub_if_data *sdata = local->scan_sdata;
+       struct ieee80211_sub_if_data *sdata;
        enum ieee80211_band band = local->hw.conf.channel->band;
  
+       sdata = rcu_dereference_protected(local->scan_sdata,
+                                         lockdep_is_held(&local->mtx));;
        for (i = 0; i < local->scan_req->n_ssids; i++)
                ieee80211_send_probe_req(
                        sdata, NULL,
@@@ -439,7 -440,7 +440,7 @@@ static int __ieee80211_start_scan(struc
        if (!ieee80211_can_scan(local, sdata)) {
                /* wait for the work to finish/time out */
                local->scan_req = req;
-               local->scan_sdata = sdata;
+               rcu_assign_pointer(local->scan_sdata, sdata);
                return 0;
        }
  
        }
  
        local->scan_req = req;
-       local->scan_sdata = sdata;
+       rcu_assign_pointer(local->scan_sdata, sdata);
  
        if (local->ops->hw_scan) {
                __set_bit(SCAN_HW_SCANNING, &local->scanning);
                ieee80211_recalc_idle(local);
  
                local->scan_req = NULL;
-               local->scan_sdata = NULL;
+               rcu_assign_pointer(local->scan_sdata, NULL);
        }
  
        return rc;
@@@ -720,7 -721,8 +721,8 @@@ void ieee80211_scan_work(struct work_st
  
        mutex_lock(&local->mtx);
  
-       sdata = local->scan_sdata;
+       sdata = rcu_dereference_protected(local->scan_sdata,
+                                         lockdep_is_held(&local->mtx));
  
        /* When scanning on-channel, the first-callback means completed. */
        if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) {
                int rc;
  
                local->scan_req = NULL;
-               local->scan_sdata = NULL;
+               rcu_assign_pointer(local->scan_sdata, NULL);
  
                rc = __ieee80211_start_scan(sdata, req);
                if (rc) {
@@@ -893,7 -895,9 +895,9 @@@ void ieee80211_scan_cancel(struct ieee8
  
        if (test_bit(SCAN_HW_SCANNING, &local->scanning)) {
                if (local->ops->cancel_hw_scan)
-                       drv_cancel_hw_scan(local, local->scan_sdata);
+                       drv_cancel_hw_scan(local,
+                               rcu_dereference_protected(local->scan_sdata,
+                                               lockdep_is_held(&local->mtx)));
                goto out;
        }
  
@@@ -915,9 -919,9 +919,9 @@@ int ieee80211_request_sched_scan_start(
        struct ieee80211_local *local = sdata->local;
        int ret, i;
  
-       mutex_lock(&sdata->local->mtx);
+       mutex_lock(&local->mtx);
  
-       if (local->sched_scanning) {
+       if (rcu_access_pointer(local->sched_scan_sdata)) {
                ret = -EBUSY;
                goto out;
        }
        }
  
        for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
+               if (!local->hw.wiphy->bands[i])
+                       continue;
                local->sched_scan_ies.ie[i] = kzalloc(2 +
                                                      IEEE80211_MAX_SSID_LEN +
                                                      local->scan_ies_len +
        ret = drv_sched_scan_start(local, sdata, req,
                                   &local->sched_scan_ies);
        if (ret == 0) {
-               local->sched_scanning = true;
+               rcu_assign_pointer(local->sched_scan_sdata, sdata);
                goto out;
        }
  
@@@ -956,7 -963,7 +963,7 @@@ out_free
        while (i > 0)
                kfree(local->sched_scan_ies.ie[--i]);
  out:
-       mutex_unlock(&sdata->local->mtx);
+       mutex_unlock(&local->mtx);
        return ret;
  }
  
@@@ -965,22 -972,22 +972,22 @@@ int ieee80211_request_sched_scan_stop(s
        struct ieee80211_local *local = sdata->local;
        int ret = 0, i;
  
-       mutex_lock(&sdata->local->mtx);
+       mutex_lock(&local->mtx);
  
        if (!local->ops->sched_scan_stop) {
                ret = -ENOTSUPP;
                goto out;
        }
  
-       if (local->sched_scanning) {
+       if (rcu_access_pointer(local->sched_scan_sdata)) {
                for (i = 0; i < IEEE80211_NUM_BANDS; i++)
                        kfree(local->sched_scan_ies.ie[i]);
  
                drv_sched_scan_stop(local, sdata);
-               local->sched_scanning = false;
+               rcu_assign_pointer(local->sched_scan_sdata, NULL);
        }
  out:
-       mutex_unlock(&sdata->local->mtx);
+       mutex_unlock(&local->mtx);
  
        return ret;
  }
@@@ -1004,7 -1011,7 +1011,7 @@@ void ieee80211_sched_scan_stopped_work(
  
        mutex_lock(&local->mtx);
  
-       if (!local->sched_scanning) {
+       if (!rcu_access_pointer(local->sched_scan_sdata)) {
                mutex_unlock(&local->mtx);
                return;
        }
        for (i = 0; i < IEEE80211_NUM_BANDS; i++)
                kfree(local->sched_scan_ies.ie[i]);
  
-       local->sched_scanning = false;
+       rcu_assign_pointer(local->sched_scan_sdata, NULL);
  
        mutex_unlock(&local->mtx);
  
diff --combined net/wireless/reg.c
index 460af03d8149a711baf8438996fe1474c372a7f9,2303ee73b50ad2fc40dc136c9250861e9bb890ee..a9175fedeb59c1d0ab9797160e93929933e0544d
@@@ -97,9 -97,16 +97,16 @@@ const struct ieee80211_regdomain *cfg80
   *     - cfg80211_world_regdom
   *     - cfg80211_regdom
   *     - last_request
+  *     - reg_num_devs_support_basehint
   */
  static DEFINE_MUTEX(reg_mutex);
  
+ /*
+  * Number of devices that registered to the core
+  * that support cellular base station regulatory hints
+  */
+ static int reg_num_devs_support_basehint;
  static inline void assert_reg_lock(void)
  {
        lockdep_assert_held(&reg_mutex);
@@@ -129,7 -136,7 +136,7 @@@ static DECLARE_DELAYED_WORK(reg_timeout
  
  /* We keep a static world regulatory domain in case of the absence of CRDA */
  static const struct ieee80211_regdomain world_regdom = {
-       .n_reg_rules = 5,
+       .n_reg_rules = 6,
        .alpha2 =  "00",
        .reg_rules = {
                /* IEEE 802.11b/g, channels 1..11 */
                REG_RULE(5745-10, 5825+10, 40, 6, 20,
                        NL80211_RRF_PASSIVE_SCAN |
                        NL80211_RRF_NO_IBSS),
+               /* IEEE 802.11ad (60gHz), channels 1..3 */
+               REG_RULE(56160+2160*1-1080, 56160+2160*3+1080, 2160, 0, 0, 0),
        }
  };
  
@@@ -891,21 -901,7 +901,21 @@@ static void handle_channel(struct wiph
        chan->max_antenna_gain = min(chan->orig_mag,
                (int) MBI_TO_DBI(power_rule->max_antenna_gain));
        chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp);
 -      chan->max_power = min(chan->max_power, chan->max_reg_power);
 +      if (chan->orig_mpwr) {
 +              /*
 +               * Devices that have their own custom regulatory domain
 +               * but also use WIPHY_FLAG_STRICT_REGULATORY will follow the
 +               * passed country IE power settings.
 +               */
 +              if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
 +                  wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY &&
 +                  wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
 +                      chan->max_power = chan->max_reg_power;
 +              else
 +                      chan->max_power = min(chan->orig_mpwr,
 +                                            chan->max_reg_power);
 +      } else
 +              chan->max_power = chan->max_reg_power;
  }
  
  static void handle_band(struct wiphy *wiphy,
                handle_channel(wiphy, initiator, band, i);
  }
  
+ static bool reg_request_cell_base(struct regulatory_request *request)
+ {
+       if (request->initiator != NL80211_REGDOM_SET_BY_USER)
+               return false;
+       if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE)
+               return false;
+       return true;
+ }
+ bool reg_last_request_cell_base(void)
+ {
+       bool val;
+       assert_cfg80211_lock();
+       mutex_lock(&reg_mutex);
+       val = reg_request_cell_base(last_request);
+       mutex_unlock(&reg_mutex);
+       return val;
+ }
+ #ifdef CONFIG_CFG80211_CERTIFICATION_ONUS
+ /* Core specific check */
+ static int reg_ignore_cell_hint(struct regulatory_request *pending_request)
+ {
+       if (!reg_num_devs_support_basehint)
+               return -EOPNOTSUPP;
+       if (reg_request_cell_base(last_request)) {
+               if (!regdom_changes(pending_request->alpha2))
+                       return -EALREADY;
+               return 0;
+       }
+       return 0;
+ }
+ /* Device specific check */
+ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
+ {
+       if (!(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS))
+               return true;
+       return false;
+ }
+ #else
+ static int reg_ignore_cell_hint(struct regulatory_request *pending_request)
+ {
+       return -EOPNOTSUPP;
+ }
+ static int reg_dev_ignore_cell_hint(struct wiphy *wiphy)
+ {
+       return true;
+ }
+ #endif
  static bool ignore_reg_update(struct wiphy *wiphy,
                              enum nl80211_reg_initiator initiator)
  {
                return true;
        }
  
+       if (reg_request_cell_base(last_request))
+               return reg_dev_ignore_cell_hint(wiphy);
        return false;
  }
  
@@@ -1180,14 -1234,6 +1248,6 @@@ static void wiphy_update_regulatory(str
                wiphy->reg_notifier(wiphy, last_request);
  }
  
- void regulatory_update(struct wiphy *wiphy,
-                      enum nl80211_reg_initiator setby)
- {
-       mutex_lock(&reg_mutex);
-       wiphy_update_regulatory(wiphy, setby);
-       mutex_unlock(&reg_mutex);
- }
  static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
  {
        struct cfg80211_registered_device *rdev;
@@@ -1318,6 -1364,13 +1378,13 @@@ static int ignore_request(struct wiphy 
                return 0;
        case NL80211_REGDOM_SET_BY_COUNTRY_IE:
  
+               if (reg_request_cell_base(last_request)) {
+                       /* Trust a Cell base station over the AP's country IE */
+                       if (regdom_changes(pending_request->alpha2))
+                               return -EOPNOTSUPP;
+                       return -EALREADY;
+               }
                last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
  
                if (unlikely(!is_an_alpha2(pending_request->alpha2)))
  
                return REG_INTERSECT;
        case NL80211_REGDOM_SET_BY_USER:
+               if (reg_request_cell_base(pending_request))
+                       return reg_ignore_cell_hint(pending_request);
+               if (reg_request_cell_base(last_request))
+                       return -EOPNOTSUPP;
                if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
                        return REG_INTERSECT;
                /*
@@@ -1651,7 -1710,8 +1724,8 @@@ static int regulatory_hint_core(const c
  }
  
  /* User hints */
- int regulatory_hint_user(const char *alpha2)
+ int regulatory_hint_user(const char *alpha2,
+                        enum nl80211_user_reg_hint_type user_reg_hint_type)
  {
        struct regulatory_request *request;
  
        request->alpha2[0] = alpha2[0];
        request->alpha2[1] = alpha2[1];
        request->initiator = NL80211_REGDOM_SET_BY_USER;
+       request->user_reg_hint_type = user_reg_hint_type;
  
        queue_regulatory_request(request);
  
@@@ -1917,7 -1978,7 +1992,7 @@@ static void restore_regulatory_settings
         * settings, user regulatory settings takes precedence.
         */
        if (is_an_alpha2(alpha2))
-               regulatory_hint_user(user_alpha2);
+               regulatory_hint_user(user_alpha2, NL80211_USER_REG_HINT_USER);
  
        if (list_empty(&tmp_reg_req_list))
                return;
@@@ -2092,9 -2153,16 +2167,16 @@@ static void print_regdomain(const struc
        else {
                if (is_unknown_alpha2(rd->alpha2))
                        pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n");
-               else
-                       pr_info("Regulatory domain changed to country: %c%c\n",
-                               rd->alpha2[0], rd->alpha2[1]);
+               else {
+                       if (reg_request_cell_base(last_request))
+                               pr_info("Regulatory domain changed "
+                                       "to country: %c%c by Cell Station\n",
+                                       rd->alpha2[0], rd->alpha2[1]);
+                       else
+                               pr_info("Regulatory domain changed "
+                                       "to country: %c%c\n",
+                                       rd->alpha2[0], rd->alpha2[1]);
+               }
        }
        print_dfs_region(rd->dfs_region);
        print_rd_rules(rd);
@@@ -2139,7 -2207,7 +2221,7 @@@ static int __set_regdom(const struct ie
                 * checking if the alpha2 changes if CRDA was already called
                 */
                if (!regdom_changes(rd->alpha2))
-                       return -EINVAL;
+                       return -EALREADY;
        }
  
        /*
@@@ -2259,6 -2327,9 +2341,9 @@@ int set_regdom(const struct ieee80211_r
        /* Note that this doesn't update the wiphys, this is done below */
        r = __set_regdom(rd);
        if (r) {
+               if (r == -EALREADY)
+                       reg_set_request_processed();
                kfree(rd);
                mutex_unlock(&reg_mutex);
                return r;
@@@ -2301,8 -2372,22 +2386,22 @@@ int reg_device_uevent(struct device *de
  }
  #endif /* CONFIG_HOTPLUG */
  
+ void wiphy_regulatory_register(struct wiphy *wiphy)
+ {
+       assert_cfg80211_lock();
+       mutex_lock(&reg_mutex);
+       if (!reg_dev_ignore_cell_hint(wiphy))
+               reg_num_devs_support_basehint++;
+       wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE);
+       mutex_unlock(&reg_mutex);
+ }
  /* Caller must hold cfg80211_mutex */
- void reg_device_remove(struct wiphy *wiphy)
+ void wiphy_regulatory_deregister(struct wiphy *wiphy)
  {
        struct wiphy *request_wiphy = NULL;
  
  
        mutex_lock(&reg_mutex);
  
+       if (!reg_dev_ignore_cell_hint(wiphy))
+               reg_num_devs_support_basehint--;
        kfree(wiphy->regd);
  
        if (last_request)
@@@ -2375,7 -2463,8 +2477,8 @@@ int __init regulatory_init(void
         * as a user hint.
         */
        if (!is_world_regdom(ieee80211_regdom))
-               regulatory_hint_user(ieee80211_regdom);
+               regulatory_hint_user(ieee80211_regdom,
+                                    NL80211_USER_REG_HINT_USER);
  
        return 0;
  }