writeback: limit write_cache_pages integrity scanning to current EOF
[safe/jmp/linux-2.6] / net / mac80211 / iface.c
index 264a6c9..50deb01 100644 (file)
@@ -10,6 +10,7 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+#include <linux/slab.h>
 #include <linux/kernel.h>
 #include <linux/if_arp.h>
 #include <linux/netdevice.h>
@@ -412,8 +413,7 @@ static int ieee80211_stop(struct net_device *dev)
 
        netif_addr_lock_bh(dev);
        spin_lock_bh(&local->filter_lock);
-       __dev_addr_unsync(&local->mc_list, &local->mc_count,
-                         &dev->mc_list, &dev->mc_count);
+       __hw_addr_unsync(&local->mc_list, &dev->mc, dev->addr_len);
        spin_unlock_bh(&local->filter_lock);
        netif_addr_unlock_bh(dev);
 
@@ -486,7 +486,7 @@ static int ieee80211_stop(struct net_device *dev)
                cancel_work_sync(&sdata->u.mgd.work);
                cancel_work_sync(&sdata->u.mgd.chswitch_work);
                cancel_work_sync(&sdata->u.mgd.monitor_work);
-               cancel_work_sync(&sdata->u.mgd.beacon_loss_work);
+               cancel_work_sync(&sdata->u.mgd.beacon_connection_loss_work);
 
                /*
                 * When we get here, the interface is marked down.
@@ -596,8 +596,7 @@ static void ieee80211_set_multicast_list(struct net_device *dev)
                sdata->flags ^= IEEE80211_SDATA_PROMISC;
        }
        spin_lock_bh(&local->filter_lock);
-       __dev_addr_sync(&local->mc_list, &local->mc_count,
-                       &dev->mc_list, &dev->mc_count);
+       __hw_addr_sync(&local->mc_list, &dev->mc, dev->addr_len);
        spin_unlock_bh(&local->filter_lock);
        ieee80211_queue_work(&local->hw, &local->reconfig_filter);
 }
@@ -684,20 +683,28 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev,
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_hdr *hdr;
        struct ieee80211_radiotap_header *rtap = (void *)skb->data;
+       u8 *p;
 
        if (local->hw.queues < 4)
                return 0;
 
        if (skb->len < 4 ||
-           skb->len < rtap->it_len + 2 /* frame control */)
+           skb->len < le16_to_cpu(rtap->it_len) + 2 /* frame control */)
                return 0; /* doesn't matter, frame will be dropped */
 
-       hdr = (void *)((u8 *)skb->data + rtap->it_len);
+       hdr = (void *)((u8 *)skb->data + le16_to_cpu(rtap->it_len));
 
        if (!ieee80211_is_data(hdr->frame_control)) {
                skb->priority = 7;
                return ieee802_1d_to_ac[skb->priority];
        }
+       if (!ieee80211_is_data_qos(hdr->frame_control)) {
+               skb->priority = 0;
+               return ieee802_1d_to_ac[skb->priority];
+       }
+
+       p = ieee80211_get_qos_ctl(hdr);
+       skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK;
 
        return ieee80211_downgrade_queue(local, skb);
 }
@@ -807,6 +814,118 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
        return 0;
 }
 
+static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
+                                      struct net_device *dev,
+                                      enum nl80211_iftype type)
+{
+       struct ieee80211_sub_if_data *sdata;
+       u64 mask, start, addr, val, inc;
+       u8 *m;
+       u8 tmp_addr[ETH_ALEN];
+       int i;
+
+       /* default ... something at least */
+       memcpy(dev->perm_addr, local->hw.wiphy->perm_addr, ETH_ALEN);
+
+       if (is_zero_ether_addr(local->hw.wiphy->addr_mask) &&
+           local->hw.wiphy->n_addresses <= 1)
+               return;
+
+
+       mutex_lock(&local->iflist_mtx);
+
+       switch (type) {
+       case NL80211_IFTYPE_MONITOR:
+               /* doesn't matter */
+               break;
+       case NL80211_IFTYPE_WDS:
+       case NL80211_IFTYPE_AP_VLAN:
+               /* match up with an AP interface */
+               list_for_each_entry(sdata, &local->interfaces, list) {
+                       if (sdata->vif.type != NL80211_IFTYPE_AP)
+                               continue;
+                       memcpy(dev->perm_addr, sdata->vif.addr, ETH_ALEN);
+                       break;
+               }
+               /* keep default if no AP interface present */
+               break;
+       default:
+               /* assign a new address if possible -- try n_addresses first */
+               for (i = 0; i < local->hw.wiphy->n_addresses; i++) {
+                       bool used = false;
+
+                       list_for_each_entry(sdata, &local->interfaces, list) {
+                               if (memcmp(local->hw.wiphy->addresses[i].addr,
+                                          sdata->vif.addr, ETH_ALEN) == 0) {
+                                       used = true;
+                                       break;
+                               }
+                       }
+
+                       if (!used) {
+                               memcpy(dev->perm_addr,
+                                      local->hw.wiphy->addresses[i].addr,
+                                      ETH_ALEN);
+                               break;
+                       }
+               }
+
+               /* try mask if available */
+               if (is_zero_ether_addr(local->hw.wiphy->addr_mask))
+                       break;
+
+               m = local->hw.wiphy->addr_mask;
+               mask =  ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
+                       ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
+                       ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
+
+               if (__ffs64(mask) + hweight64(mask) != fls64(mask)) {
+                       /* not a contiguous mask ... not handled now! */
+                       printk(KERN_DEBUG "not contiguous\n");
+                       break;
+               }
+
+               m = local->hw.wiphy->perm_addr;
+               start = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
+                       ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
+                       ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
+
+               inc = 1ULL<<__ffs64(mask);
+               val = (start & mask);
+               addr = (start & ~mask) | (val & mask);
+               do {
+                       bool used = false;
+
+                       tmp_addr[5] = addr >> 0*8;
+                       tmp_addr[4] = addr >> 1*8;
+                       tmp_addr[3] = addr >> 2*8;
+                       tmp_addr[2] = addr >> 3*8;
+                       tmp_addr[1] = addr >> 4*8;
+                       tmp_addr[0] = addr >> 5*8;
+
+                       val += inc;
+
+                       list_for_each_entry(sdata, &local->interfaces, list) {
+                               if (memcmp(tmp_addr, sdata->vif.addr,
+                                                       ETH_ALEN) == 0) {
+                                       used = true;
+                                       break;
+                               }
+                       }
+
+                       if (!used) {
+                               memcpy(dev->perm_addr, tmp_addr, ETH_ALEN);
+                               break;
+                       }
+                       addr = (start & ~mask) | (val & mask);
+               } while (addr != start);
+
+               break;
+       }
+
+       mutex_unlock(&local->iflist_mtx);
+}
+
 int ieee80211_if_add(struct ieee80211_local *local, const char *name,
                     struct net_device **new_dev, enum nl80211_iftype type,
                     struct vif_params *params)
@@ -836,8 +955,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
        if (ret < 0)
                goto fail;
 
-       memcpy(ndev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN);
-       memcpy(ndev->perm_addr, ndev->dev_addr, ETH_ALEN);
+       ieee80211_assign_perm_addr(local, ndev, type);
+       memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN);
        SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy));
 
        /* don't use IEEE80211_DEV_TO_SUB_IF because it checks too much */
@@ -856,8 +975,12 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
 
        INIT_LIST_HEAD(&sdata->key_list);
 
-       sdata->force_unicast_rateidx = -1;
-       sdata->max_ratectrl_rateidx = -1;
+       for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
+               struct ieee80211_supported_band *sband;
+               sband = local->hw.wiphy->bands[i];
+               sdata->rc_rateidx_mask[i] =
+                       sband ? (1 << sband->n_bitrates) - 1 : 0;
+       }
 
        /* setup type-dependent data */
        ieee80211_setup_sdata(sdata, type);
@@ -1019,7 +1142,7 @@ static int netdev_notify(struct notifier_block *nb,
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       memcpy(sdata->name, sdata->name, IFNAMSIZ);
+       memcpy(sdata->name, dev->name, IFNAMSIZ);
 
        ieee80211_debugfs_rename_netdev(sdata);
        return 0;