Merge branch 'master' of github.com:davem330/net
[linux-2.6.git] / drivers / net / wireless / rt2x00 / rt2800lib.c
index 0019dfd..31c9850 100644 (file)
@@ -493,7 +493,7 @@ void rt2800_write_tx_data(struct queue_entry *entry,
        rt2x00_set_field32(&word, TXWI_W1_BW_WIN_SIZE, txdesc->u.ht.ba_size);
        rt2x00_set_field32(&word, TXWI_W1_WIRELESS_CLI_ID,
                           test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags) ?
-                          txdesc->key_idx : 0xff);
+                          txdesc->key_idx : txdesc->u.ht.wcid);
        rt2x00_set_field32(&word, TXWI_W1_MPDU_TOTAL_BYTE_COUNT,
                           txdesc->length);
        rt2x00_set_field32(&word, TXWI_W1_PACKETID_QUEUE, entry->queue->qid);
@@ -601,7 +601,7 @@ void rt2800_process_rxwi(struct queue_entry *entry,
 }
 EXPORT_SYMBOL_GPL(rt2800_process_rxwi);
 
-void rt2800_txdone_entry(struct queue_entry *entry, u32 status)
+void rt2800_txdone_entry(struct queue_entry *entry, u32 status, __le32 *txwi)
 {
        struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
        struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
@@ -609,13 +609,11 @@ void rt2800_txdone_entry(struct queue_entry *entry, u32 status)
        u32 word;
        u16 mcs, real_mcs;
        int aggr, ampdu;
-       __le32 *txwi;
 
        /*
         * Obtain the status about this packet.
         */
        txdesc.flags = 0;
-       txwi = rt2800_drv_get_txwi(entry);
        rt2x00_desc_read(txwi, 0, &word);
 
        mcs = rt2x00_get_field32(word, TXWI_W0_MCS);
@@ -899,28 +897,12 @@ static void rt2800_brightness_set(struct led_classdev *led_cdev,
        }
 }
 
-static int rt2800_blink_set(struct led_classdev *led_cdev,
-                           unsigned long *delay_on, unsigned long *delay_off)
-{
-       struct rt2x00_led *led =
-           container_of(led_cdev, struct rt2x00_led, led_dev);
-       u32 reg;
-
-       rt2800_register_read(led->rt2x00dev, LED_CFG, &reg);
-       rt2x00_set_field32(&reg, LED_CFG_ON_PERIOD, *delay_on);
-       rt2x00_set_field32(&reg, LED_CFG_OFF_PERIOD, *delay_off);
-       rt2800_register_write(led->rt2x00dev, LED_CFG, reg);
-
-       return 0;
-}
-
 static void rt2800_init_led(struct rt2x00_dev *rt2x00dev,
                     struct rt2x00_led *led, enum led_type type)
 {
        led->rt2x00dev = rt2x00dev;
        led->type = type;
        led->led_dev.brightness_set = rt2800_brightness_set;
-       led->led_dev.blink_set = rt2800_blink_set;
        led->flags = LED_INITIALIZED;
 }
 #endif /* CONFIG_RT2X00_LIB_LEDS */
@@ -928,11 +910,51 @@ static void rt2800_init_led(struct rt2x00_dev *rt2x00dev,
 /*
  * Configuration handlers.
  */
-static void rt2800_config_wcid_attr(struct rt2x00_dev *rt2x00dev,
-                                   struct rt2x00lib_crypto *crypto,
-                                   struct ieee80211_key_conf *key)
+static void rt2800_config_wcid(struct rt2x00_dev *rt2x00dev,
+                              const u8 *address,
+                              int wcid)
 {
        struct mac_wcid_entry wcid_entry;
+       u32 offset;
+
+       offset = MAC_WCID_ENTRY(wcid);
+
+       memset(&wcid_entry, 0xff, sizeof(wcid_entry));
+       if (address)
+               memcpy(wcid_entry.mac, address, ETH_ALEN);
+
+       rt2800_register_multiwrite(rt2x00dev, offset,
+                                     &wcid_entry, sizeof(wcid_entry));
+}
+
+static void rt2800_delete_wcid_attr(struct rt2x00_dev *rt2x00dev, int wcid)
+{
+       u32 offset;
+       offset = MAC_WCID_ATTR_ENTRY(wcid);
+       rt2800_register_write(rt2x00dev, offset, 0);
+}
+
+static void rt2800_config_wcid_attr_bssidx(struct rt2x00_dev *rt2x00dev,
+                                          int wcid, u32 bssidx)
+{
+       u32 offset = MAC_WCID_ATTR_ENTRY(wcid);
+       u32 reg;
+
+       /*
+        * The BSS Idx numbers is split in a main value of 3 bits,
+        * and a extended field for adding one additional bit to the value.
+        */
+       rt2800_register_read(rt2x00dev, offset, &reg);
+       rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_BSS_IDX, (bssidx & 0x7));
+       rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_BSS_IDX_EXT,
+                          (bssidx & 0x8) >> 3);
+       rt2800_register_write(rt2x00dev, offset, reg);
+}
+
+static void rt2800_config_wcid_attr_cipher(struct rt2x00_dev *rt2x00dev,
+                                          struct rt2x00lib_crypto *crypto,
+                                          struct ieee80211_key_conf *key)
+{
        struct mac_iveiv_entry iveiv_entry;
        u32 offset;
        u32 reg;
@@ -952,14 +974,16 @@ static void rt2800_config_wcid_attr(struct rt2x00_dev *rt2x00dev,
                                   (crypto->cipher & 0x7));
                rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_CIPHER_EXT,
                                   (crypto->cipher & 0x8) >> 3);
-               rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_BSS_IDX,
-                                  (crypto->bssidx & 0x7));
-               rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_BSS_IDX_EXT,
-                                  (crypto->bssidx & 0x8) >> 3);
                rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_RX_WIUDF, crypto->cipher);
                rt2800_register_write(rt2x00dev, offset, reg);
        } else {
-               rt2800_register_write(rt2x00dev, offset, 0);
+               /* Delete the cipher without touching the bssidx */
+               rt2800_register_read(rt2x00dev, offset, &reg);
+               rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_KEYTAB, 0);
+               rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_CIPHER, 0);
+               rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_CIPHER_EXT, 0);
+               rt2x00_set_field32(&reg, MAC_WCID_ATTRIBUTE_RX_WIUDF, 0);
+               rt2800_register_write(rt2x00dev, offset, reg);
        }
 
        offset = MAC_IVEIV_ENTRY(key->hw_key_idx);
@@ -972,14 +996,6 @@ static void rt2800_config_wcid_attr(struct rt2x00_dev *rt2x00dev,
        iveiv_entry.iv[3] |= key->keyidx << 6;
        rt2800_register_multiwrite(rt2x00dev, offset,
                                      &iveiv_entry, sizeof(iveiv_entry));
-
-       offset = MAC_WCID_ENTRY(key->hw_key_idx);
-
-       memset(&wcid_entry, 0, sizeof(wcid_entry));
-       if (crypto->cmd == SET_KEY)
-               memcpy(wcid_entry.mac, crypto->address, ETH_ALEN);
-       rt2800_register_multiwrite(rt2x00dev, offset,
-                                     &wcid_entry, sizeof(wcid_entry));
 }
 
 int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev,
@@ -1026,20 +1042,24 @@ int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev,
        /*
         * Update WCID information
         */
-       rt2800_config_wcid_attr(rt2x00dev, crypto, key);
+       rt2800_config_wcid(rt2x00dev, crypto->address, key->hw_key_idx);
+       rt2800_config_wcid_attr_bssidx(rt2x00dev, key->hw_key_idx,
+                                      crypto->bssidx);
+       rt2800_config_wcid_attr_cipher(rt2x00dev, crypto, key);
 
        return 0;
 }
 EXPORT_SYMBOL_GPL(rt2800_config_shared_key);
 
-static inline int rt2800_find_pairwise_keyslot(struct rt2x00_dev *rt2x00dev)
+static inline int rt2800_find_wcid(struct rt2x00_dev *rt2x00dev)
 {
+       struct mac_wcid_entry wcid_entry;
        int idx;
-       u32 offset, reg;
+       u32 offset;
 
        /*
-        * Search for the first free pairwise key entry and return the
-        * corresponding index.
+        * Search for the first free WCID entry and return the corresponding
+        * index.
         *
         * Make sure the WCID starts _after_ the last possible shared key
         * entry (>32).
@@ -1049,11 +1069,17 @@ static inline int rt2800_find_pairwise_keyslot(struct rt2x00_dev *rt2x00dev)
         * first 222 entries.
         */
        for (idx = 33; idx <= 222; idx++) {
-               offset = MAC_WCID_ATTR_ENTRY(idx);
-               rt2800_register_read(rt2x00dev, offset, &reg);
-               if (!reg)
+               offset = MAC_WCID_ENTRY(idx);
+               rt2800_register_multiread(rt2x00dev, offset, &wcid_entry,
+                                         sizeof(wcid_entry));
+               if (is_broadcast_ether_addr(wcid_entry.mac))
                        return idx;
        }
+
+       /*
+        * Use -1 to indicate that we don't have any more space in the WCID
+        * table.
+        */
        return -1;
 }
 
@@ -1063,13 +1089,15 @@ int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev,
 {
        struct hw_key_entry key_entry;
        u32 offset;
-       int idx;
 
        if (crypto->cmd == SET_KEY) {
-               idx = rt2800_find_pairwise_keyslot(rt2x00dev);
-               if (idx < 0)
+               /*
+                * Allow key configuration only for STAs that are
+                * known by the hw.
+                */
+               if (crypto->wcid < 0)
                        return -ENOSPC;
-               key->hw_key_idx = idx;
+               key->hw_key_idx = crypto->wcid;
 
                memcpy(key_entry.key, crypto->key,
                       sizeof(key_entry.key));
@@ -1086,12 +1114,59 @@ int rt2800_config_pairwise_key(struct rt2x00_dev *rt2x00dev,
        /*
         * Update WCID information
         */
-       rt2800_config_wcid_attr(rt2x00dev, crypto, key);
+       rt2800_config_wcid_attr_cipher(rt2x00dev, crypto, key);
 
        return 0;
 }
 EXPORT_SYMBOL_GPL(rt2800_config_pairwise_key);
 
+int rt2800_sta_add(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif,
+                  struct ieee80211_sta *sta)
+{
+       int wcid;
+       struct rt2x00_sta *sta_priv = sta_to_rt2x00_sta(sta);
+
+       /*
+        * Find next free WCID.
+        */
+       wcid = rt2800_find_wcid(rt2x00dev);
+
+       /*
+        * Store selected wcid even if it is invalid so that we can
+        * later decide if the STA is uploaded into the hw.
+        */
+       sta_priv->wcid = wcid;
+
+       /*
+        * No space left in the device, however, we can still communicate
+        * with the STA -> No error.
+        */
+       if (wcid < 0)
+               return 0;
+
+       /*
+        * Clean up WCID attributes and write STA address to the device.
+        */
+       rt2800_delete_wcid_attr(rt2x00dev, wcid);
+       rt2800_config_wcid(rt2x00dev, sta->addr, wcid);
+       rt2800_config_wcid_attr_bssidx(rt2x00dev, wcid,
+                                      rt2x00lib_get_bssidx(rt2x00dev, vif));
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800_sta_add);
+
+int rt2800_sta_remove(struct rt2x00_dev *rt2x00dev, int wcid)
+{
+       /*
+        * Remove WCID entry, no need to clean the attributes as they will
+        * get renewed when the WCID is reused.
+        */
+       rt2800_config_wcid(rt2x00dev, NULL, wcid);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800_sta_remove);
+
 void rt2800_config_filter(struct rt2x00_dev *rt2x00dev,
                          const unsigned int filter_flags)
 {
@@ -2771,11 +2846,8 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
                                         SHARED_KEY_MODE_ENTRY(i), 0);
 
        for (i = 0; i < 256; i++) {
-               static const u32 wcid[2] = { 0xffffffff, 0x00ffffff };
-               rt2800_register_multiwrite(rt2x00dev, MAC_WCID_ENTRY(i),
-                                             wcid, sizeof(wcid));
-
-               rt2800_register_write(rt2x00dev, MAC_WCID_ATTR_ENTRY(i), 0);
+               rt2800_config_wcid(rt2x00dev, NULL, i);
+               rt2800_delete_wcid_attr(rt2x00dev, i);
                rt2800_register_write(rt2x00dev, MAC_IVEIV_ENTRY(i), 0);
        }
 
@@ -4414,8 +4486,19 @@ int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        struct ieee80211_sta *sta, u16 tid, u16 *ssn,
                        u8 buf_size)
 {
+       struct rt2x00_sta *sta_priv = (struct rt2x00_sta *)sta->drv_priv;
        int ret = 0;
 
+       /*
+        * Don't allow aggregation for stations the hardware isn't aware
+        * of because tx status reports for frames to an unknown station
+        * always contain wcid=255 and thus we can't distinguish between
+        * multiple stations which leads to unwanted situations when the
+        * hw reorders frames due to aggregation.
+        */
+       if (sta_priv->wcid < 0)
+               return 1;
+
        switch (action) {
        case IEEE80211_AMPDU_RX_START:
        case IEEE80211_AMPDU_RX_STOP: