]> Pileus Git - ~andy/linux/blobdiff - net/mac80211/status.c
Bluetooth: Fix L2CAP Disconnect response for unknown CID
[~andy/linux] / net / mac80211 / status.c
index 43439203f4e4cf2262092a9e391e7d048f10ef9a..368837fe3b800e87f408039ef4d36e84bfaa7069 100644 (file)
@@ -235,7 +235,8 @@ static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info)
 
        /* IEEE80211_RADIOTAP_RATE rate */
        if (info->status.rates[0].idx >= 0 &&
-           !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS))
+           !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS |
+                                            IEEE80211_TX_RC_VHT_MCS)))
                len += 2;
 
        /* IEEE80211_RADIOTAP_TX_FLAGS */
@@ -244,17 +245,23 @@ static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info)
        /* IEEE80211_RADIOTAP_DATA_RETRIES */
        len += 1;
 
-       /* IEEE80211_TX_RC_MCS */
-       if (info->status.rates[0].idx >= 0 &&
-           info->status.rates[0].flags & IEEE80211_TX_RC_MCS)
-               len += 3;
+       /* IEEE80211_RADIOTAP_MCS
+        * IEEE80211_RADIOTAP_VHT */
+       if (info->status.rates[0].idx >= 0) {
+               if (info->status.rates[0].flags & IEEE80211_TX_RC_MCS)
+                       len += 3;
+               else if (info->status.rates[0].flags & IEEE80211_TX_RC_VHT_MCS)
+                       len = ALIGN(len, 2) + 12;
+       }
 
        return len;
 }
 
-static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
-                                            *sband, struct sk_buff *skb,
-                                            int retry_count, int rtap_len)
+static void
+ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
+                                struct ieee80211_supported_band *sband,
+                                struct sk_buff *skb, int retry_count,
+                                int rtap_len, int shift)
 {
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@@ -279,9 +286,13 @@ static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
 
        /* IEEE80211_RADIOTAP_RATE */
        if (info->status.rates[0].idx >= 0 &&
-           !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) {
+           !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS |
+                                            IEEE80211_TX_RC_VHT_MCS))) {
+               u16 rate;
+
                rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
-               *pos = sband->bitrates[info->status.rates[0].idx].bitrate / 5;
+               rate = sband->bitrates[info->status.rates[0].idx].bitrate;
+               *pos = DIV_ROUND_UP(rate, 5 * (1 << shift));
                /* padding for tx flags */
                pos += 2;
        }
@@ -306,9 +317,12 @@ static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
        *pos = retry_count;
        pos++;
 
-       /* IEEE80211_TX_RC_MCS */
-       if (info->status.rates[0].idx >= 0 &&
-           info->status.rates[0].flags & IEEE80211_TX_RC_MCS) {
+       if (info->status.rates[0].idx < 0)
+               return;
+
+       /* IEEE80211_RADIOTAP_MCS
+        * IEEE80211_RADIOTAP_VHT */
+       if (info->status.rates[0].flags & IEEE80211_TX_RC_MCS) {
                rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS);
                pos[0] = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
                         IEEE80211_RADIOTAP_MCS_HAVE_GI |
@@ -321,8 +335,48 @@ static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
                        pos[1] |= IEEE80211_RADIOTAP_MCS_FMT_GF;
                pos[2] = info->status.rates[0].idx;
                pos += 3;
-       }
+       } else if (info->status.rates[0].flags & IEEE80211_TX_RC_VHT_MCS) {
+               u16 known = local->hw.radiotap_vht_details &
+                       (IEEE80211_RADIOTAP_VHT_KNOWN_GI |
+                        IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH);
+
+               rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
+
+               /* required alignment from rthdr */
+               pos = (u8 *)rthdr + ALIGN(pos - (u8 *)rthdr, 2);
 
+               /* u16 known - IEEE80211_RADIOTAP_VHT_KNOWN_* */
+               put_unaligned_le16(known, pos);
+               pos += 2;
+
+               /* u8 flags - IEEE80211_RADIOTAP_VHT_FLAG_* */
+               if (info->status.rates[0].flags & IEEE80211_TX_RC_SHORT_GI)
+                       *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
+               pos++;
+
+               /* u8 bandwidth */
+               if (info->status.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+                       *pos = 1;
+               else if (info->status.rates[0].flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+                       *pos = 4;
+               else if (info->status.rates[0].flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
+                       *pos = 11;
+               else /* IEEE80211_TX_RC_{20_MHZ_WIDTH,FIXME:DUP_DATA} */
+                       *pos = 0;
+               pos++;
+
+               /* u8 mcs_nss[4] */
+               *pos = (ieee80211_rate_get_vht_mcs(&info->status.rates[0]) << 4) |
+                       ieee80211_rate_get_vht_nss(&info->status.rates[0]);
+               pos += 4;
+
+               /* u8 coding */
+               pos++;
+               /* u8 group_id */
+               pos++;
+               /* u16 partial_aid */
+               pos += 2;
+       }
 }
 
 static void ieee80211_report_used_skb(struct ieee80211_local *local,
@@ -424,6 +478,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
        bool acked;
        struct ieee80211_bar *bar;
        int rtap_len;
+       int shift = 0;
 
        for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
                if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
@@ -458,6 +513,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr))
                        continue;
 
+               shift = ieee80211_vif_get_shift(&sta->sdata->vif);
+
                if (info->flags & IEEE80211_TX_STATUS_EOSP)
                        clear_sta_flag(sta, WLAN_STA_SP);
 
@@ -557,7 +614,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
 
        rcu_read_unlock();
 
-       ieee80211_led_tx(local, 0);
+       ieee80211_led_tx(local);
 
        /* SNMP counters
         * Fragments are passed to low-level drivers as separate skbs, so these
@@ -624,7 +681,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
                dev_kfree_skb(skb);
                return;
        }
-       ieee80211_add_tx_radiotap_header(sband, skb, retry_count, rtap_len);
+       ieee80211_add_tx_radiotap_header(local, sband, skb, retry_count,
+                                        rtap_len, shift);
 
        /* XXX: is this sufficient for BPF? */
        skb_set_mac_header(skb, 0);