]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/mac80211/main.c
mac80211: Add capability to enable/disable beaconing
[linux-2.6-omap-h63xx.git] / net / mac80211 / main.c
index d631dc96c3231a0de2f9ed9ad36329edc330e687..8d5c19e4a1bcb04ad734cbde093f1bb134d06b05 100644 (file)
@@ -168,7 +168,6 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
                return 0;
 
        memset(&conf, 0, sizeof(conf));
-       conf.changed = changed;
 
        if (sdata->vif.type == NL80211_IFTYPE_STATION ||
            sdata->vif.type == NL80211_IFTYPE_ADHOC)
@@ -176,16 +175,57 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
        else if (sdata->vif.type == NL80211_IFTYPE_AP)
                conf.bssid = sdata->dev->dev_addr;
        else if (ieee80211_vif_is_mesh(&sdata->vif)) {
-               u8 zero[ETH_ALEN] = { 0 };
+               static const u8 zero[ETH_ALEN] = { 0 };
                conf.bssid = zero;
        } else {
                WARN_ON(1);
                return -EINVAL;
        }
 
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_MESH_POINT:
+               break;
+       default:
+               /* do not warn to simplify caller in scan.c */
+               changed &= ~IEEE80211_IFCC_BEACON_ENABLED;
+               if (WARN_ON(changed & IEEE80211_IFCC_BEACON))
+                       return -EINVAL;
+               changed &= ~IEEE80211_IFCC_BEACON;
+               break;
+       }
+
+       if (changed & IEEE80211_IFCC_BEACON_ENABLED) {
+               if (local->sw_scanning) {
+                       conf.enable_beacon = false;
+               } else {
+                       /*
+                        * Beacon should be enabled, but AP mode must
+                        * check whether there is a beacon configured.
+                        */
+                       switch (sdata->vif.type) {
+                       case NL80211_IFTYPE_AP:
+                               conf.enable_beacon =
+                                       !!rcu_dereference(sdata->u.ap.beacon);
+                               break;
+                       case NL80211_IFTYPE_ADHOC:
+                       case NL80211_IFTYPE_MESH_POINT:
+                               conf.enable_beacon = true;
+                               break;
+                       default:
+                               /* not reached */
+                               WARN_ON(1);
+                               break;
+                       }
+               }
+       }
+
        if (WARN_ON(!conf.bssid && (changed & IEEE80211_IFCC_BSSID)))
                return -EINVAL;
 
+       conf.changed = changed;
+
        return local->ops->config_interface(local_to_hw(local),
                                            &sdata->vif, &conf);
 }
@@ -195,24 +235,35 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
        struct ieee80211_channel *chan;
        int ret = 0;
        int power;
+       enum nl80211_channel_type channel_type;
 
        might_sleep();
 
-       if (local->sw_scanning)
+       if (local->sw_scanning) {
                chan = local->scan_channel;
-       else
+               channel_type = NL80211_CHAN_NO_HT;
+       } else {
                chan = local->oper_channel;
+               channel_type = local->oper_channel_type;
+       }
 
-       if (chan != local->hw.conf.channel) {
+       if (chan != local->hw.conf.channel ||
+           channel_type != local->hw.conf.channel_type) {
                local->hw.conf.channel = chan;
+               local->hw.conf.channel_type = channel_type;
                changed |= IEEE80211_CONF_CHANGE_CHANNEL;
        }
 
-
-       if (!local->hw.conf.power_level)
+       if (local->sw_scanning)
                power = chan->max_power;
        else
-               power = min(chan->max_power, local->hw.conf.power_level);
+               power = local->power_constr_level ?
+                       (chan->max_power - local->power_constr_level) :
+                       chan->max_power;
+
+       if (local->user_power_level)
+               power = min(power, local->user_power_level);
+
        if (local->hw.conf.power_level != power) {
                changed |= IEEE80211_CONF_CHANGE_POWER;
                local->hw.conf.power_level = power;
@@ -221,10 +272,20 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
        if (changed && local->open_count) {
                ret = local->ops->config(local_to_hw(local), changed);
                /*
+                * Goal:
                 * HW reconfiguration should never fail, the driver has told
                 * us what it can support so it should live up to that promise.
+                *
+                * Current status:
+                * rfkill is not integrated with mac80211 and a
+                * configuration command can thus fail if hardware rfkill
+                * is enabled
+                *
+                * FIXME: integrate rfkill with mac80211 and then add this
+                * WARN_ON() back
+                *
                 */
-               WARN_ON(ret);
+               /* WARN_ON(ret); */
        }
 
        return ret;
@@ -316,7 +377,8 @@ static void ieee80211_tasklet_handler(unsigned long data)
                        dev_kfree_skb(skb);
                        break ;
                default:
-                       WARN_ON(1);
+                       WARN(1, "mac80211: Packet is of unknown type %d\n",
+                            skb->pkt_type);
                        dev_kfree_skb(skb);
                        break;
                }
@@ -699,8 +761,17 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
 
        spin_lock_init(&local->key_lock);
 
+       spin_lock_init(&local->queue_stop_reason_lock);
+
        INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);
 
+       INIT_WORK(&local->dynamic_ps_enable_work,
+                 ieee80211_dynamic_ps_enable_work);
+       INIT_WORK(&local->dynamic_ps_disable_work,
+                 ieee80211_dynamic_ps_disable_work);
+       setup_timer(&local->dynamic_ps_timer,
+                   ieee80211_dynamic_ps_timer, (unsigned long) local);
+
        sta_info_init(local);
 
        tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
@@ -722,7 +793,6 @@ EXPORT_SYMBOL(ieee80211_alloc_hw);
 int ieee80211_register_hw(struct ieee80211_hw *hw)
 {
        struct ieee80211_local *local = hw_to_local(hw);
-       const char *name;
        int result;
        enum ieee80211_band band;
        struct net_device *mdev;
@@ -787,8 +857,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
        mdev->header_ops = &ieee80211_header_ops;
        mdev->set_multicast_list = ieee80211_master_set_multicast_list;
 
-       name = wiphy_dev(local->hw.wiphy)->driver->name;
-       local->hw.workqueue = create_freezeable_workqueue(name);
+       local->hw.workqueue =
+               create_freezeable_workqueue(wiphy_name(local->hw.wiphy));
        if (!local->hw.workqueue) {
                result = -ENOMEM;
                goto fail_workqueue;
@@ -855,12 +925,14 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 
        local->mdev->select_queue = ieee80211_select_queue;
 
-       /* add one default STA interface */
-       result = ieee80211_if_add(local, "wlan%d", NULL,
-                                 NL80211_IFTYPE_STATION, NULL);
-       if (result)
-               printk(KERN_WARNING "%s: Failed to add default virtual iface\n",
-                      wiphy_name(local->hw.wiphy));
+       /* add one default STA interface if supported */
+       if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) {
+               result = ieee80211_if_add(local, "wlan%d", NULL,
+                                         NL80211_IFTYPE_STATION, NULL);
+               if (result)
+                       printk(KERN_WARNING "%s: Failed to add default virtual iface\n",
+                              wiphy_name(local->hw.wiphy));
+       }
 
        rtnl_unlock();