Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac802...
[firefly-linux-kernel-4.4.55.git] / net / mac80211 / rx.c
index 8e29526202568f0223401659f4301500d5f9d424..6b85f95b9ba16ed010d6f8a87b2829dae3dd7ca9 100644 (file)
@@ -87,11 +87,13 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
        int len;
 
        /* always present fields */
-       len = sizeof(struct ieee80211_radiotap_header) + 9;
+       len = sizeof(struct ieee80211_radiotap_header) + 8;
 
-       /* allocate extra bitmap */
+       /* allocate extra bitmaps */
        if (status->vendor_radiotap_len)
                len += 4;
+       if (status->chains)
+               len += 4 * hweight8(status->chains);
 
        if (ieee80211_have_rx_timestamp(status)) {
                len = ALIGN(len, 8);
@@ -100,6 +102,10 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
        if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
                len += 1;
 
+       /* antenna field, if we don't have per-chain info */
+       if (!status->chains)
+               len += 1;
+
        /* padding for RX_FLAGS if necessary */
        len = ALIGN(len, 2);
 
@@ -116,6 +122,11 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
                len += 12;
        }
 
+       if (status->chains) {
+               /* antenna and antenna signal fields */
+               len += 2 * hweight8(status->chains);
+       }
+
        if (status->vendor_radiotap_len) {
                if (WARN_ON_ONCE(status->vendor_radiotap_align == 0))
                        status->vendor_radiotap_align = 1;
@@ -145,8 +156,12 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_radiotap_header *rthdr;
        unsigned char *pos;
+       __le32 *it_present;
+       u32 it_present_val;
        u16 rx_flags = 0;
-       int mpdulen;
+       u16 channel_flags = 0;
+       int mpdulen, chain;
+       unsigned long chains = status->chains;
 
        mpdulen = skb->len;
        if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)))
@@ -154,25 +169,39 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
 
        rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
        memset(rthdr, 0, rtap_len);
+       it_present = &rthdr->it_present;
 
        /* radiotap header, set always present flags */
-       rthdr->it_present =
-               cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
-                           (1 << IEEE80211_RADIOTAP_CHANNEL) |
-                           (1 << IEEE80211_RADIOTAP_ANTENNA) |
-                           (1 << IEEE80211_RADIOTAP_RX_FLAGS));
        rthdr->it_len = cpu_to_le16(rtap_len + status->vendor_radiotap_len);
+       it_present_val = BIT(IEEE80211_RADIOTAP_FLAGS) |
+                        BIT(IEEE80211_RADIOTAP_CHANNEL) |
+                        BIT(IEEE80211_RADIOTAP_RX_FLAGS);
 
-       pos = (unsigned char *)(rthdr + 1);
+       if (!status->chains)
+               it_present_val |= BIT(IEEE80211_RADIOTAP_ANTENNA);
+
+       for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+               it_present_val |=
+                       BIT(IEEE80211_RADIOTAP_EXT) |
+                       BIT(IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE);
+               put_unaligned_le32(it_present_val, it_present);
+               it_present++;
+               it_present_val = BIT(IEEE80211_RADIOTAP_ANTENNA) |
+                                BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
+       }
 
        if (status->vendor_radiotap_len) {
-               rthdr->it_present |=
-                       cpu_to_le32(BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE)) |
-                       cpu_to_le32(BIT(IEEE80211_RADIOTAP_EXT));
-               put_unaligned_le32(status->vendor_radiotap_bitmap, pos);
-               pos += 4;
+               it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) |
+                                 BIT(IEEE80211_RADIOTAP_EXT);
+               put_unaligned_le32(it_present_val, it_present);
+               it_present++;
+               it_present_val = status->vendor_radiotap_bitmap;
        }
 
+       put_unaligned_le32(it_present_val, it_present);
+
+       pos = (void *)(it_present + 1);
+
        /* the order of the following fields is important */
 
        /* IEEE80211_RADIOTAP_TSFT */
@@ -207,28 +236,35 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                 */
                *pos = 0;
        } else {
+               int shift = 0;
                rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
-               *pos = rate->bitrate / 5;
+               if (status->flag & RX_FLAG_10MHZ)
+                       shift = 1;
+               else if (status->flag & RX_FLAG_5MHZ)
+                       shift = 2;
+               *pos = DIV_ROUND_UP(rate->bitrate, 5 * (1 << shift));
        }
        pos++;
 
        /* IEEE80211_RADIOTAP_CHANNEL */
        put_unaligned_le16(status->freq, pos);
        pos += 2;
+       if (status->flag & RX_FLAG_10MHZ)
+               channel_flags |= IEEE80211_CHAN_HALF;
+       else if (status->flag & RX_FLAG_5MHZ)
+               channel_flags |= IEEE80211_CHAN_QUARTER;
+
        if (status->band == IEEE80211_BAND_5GHZ)
-               put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ,
-                                  pos);
+               channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ;
        else if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT))
-               put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ,
-                                  pos);
+               channel_flags |= IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
        else if (rate && rate->flags & IEEE80211_RATE_ERP_G)
-               put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ,
-                                  pos);
+               channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
        else if (rate)
-               put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ,
-                                  pos);
+               channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
        else
-               put_unaligned_le16(IEEE80211_CHAN_2GHZ, pos);
+               channel_flags |= IEEE80211_CHAN_2GHZ;
+       put_unaligned_le16(channel_flags, pos);
        pos += 2;
 
        /* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
@@ -242,9 +278,11 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
 
        /* IEEE80211_RADIOTAP_LOCK_QUALITY is missing */
 
-       /* IEEE80211_RADIOTAP_ANTENNA */
-       *pos = status->antenna;
-       pos++;
+       if (!status->chains) {
+               /* IEEE80211_RADIOTAP_ANTENNA */
+               *pos = status->antenna;
+               pos++;
+       }
 
        /* IEEE80211_RADIOTAP_DB_ANTNOISE is not used */
 
@@ -258,6 +296,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
        pos += 2;
 
        if (status->flag & RX_FLAG_HT) {
+               unsigned int stbc;
+
                rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS);
                *pos++ = local->hw.radiotap_mcs_details;
                *pos = 0;
@@ -267,6 +307,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                        *pos |= IEEE80211_RADIOTAP_MCS_BW_40;
                if (status->flag & RX_FLAG_HT_GF)
                        *pos |= IEEE80211_RADIOTAP_MCS_FMT_GF;
+               stbc = (status->flag & RX_FLAG_STBC_MASK) >> RX_FLAG_STBC_SHIFT;
+               *pos |= stbc << IEEE80211_RADIOTAP_MCS_STBC_SHIFT;
                pos++;
                *pos++ = status->rate_idx;
        }
@@ -337,6 +379,11 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
                pos += 2;
        }
 
+       for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+               *pos++ = status->chain_signal[chain];
+               *pos++ = chain;
+       }
+
        if (status->vendor_radiotap_len) {
                /* ensure 2 byte alignment for the vendor field as required */
                if ((pos - (u8 *)rthdr) & 1)
@@ -932,8 +979,14 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
 
-       /* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */
-       if (rx->sta && !is_multicast_ether_addr(hdr->addr1)) {
+       /*
+        * Drop duplicate 802.11 retransmissions
+        * (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery")
+        */
+       if (rx->skb->len >= 24 && rx->sta &&
+           !ieee80211_is_ctl(hdr->frame_control) &&
+           !ieee80211_is_qos_nullfunc(hdr->frame_control) &&
+           !is_multicast_ether_addr(hdr->addr1)) {
                if (unlikely(ieee80211_has_retry(hdr->frame_control) &&
                             rx->sta->last_seq_ctrl[rx->seqno_idx] ==
                             hdr->seq_ctrl)) {
@@ -1372,6 +1425,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
        struct sk_buff *skb = rx->skb;
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       int i;
 
        if (!sta)
                return RX_CONTINUE;
@@ -1422,6 +1476,19 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
                ewma_add(&sta->avg_signal, -status->signal);
        }
 
+       if (status->chains) {
+               sta->chains = status->chains;
+               for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) {
+                       int signal = status->chain_signal[i];
+
+                       if (!(status->chains & BIT(i)))
+                               continue;
+
+                       sta->chain_signal_last[i] = signal;
+                       ewma_add(&sta->chain_signal_avg[i], -signal);
+               }
+       }
+
        /*
         * Change STA power saving mode only at the end of a frame
         * exchange sequence.
@@ -1608,7 +1675,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
                        entry->ccmp = 1;
                        memcpy(entry->last_pn,
                               rx->key->u.ccmp.rx_pn[queue],
-                              CCMP_PN_LEN);
+                              IEEE80211_CCMP_PN_LEN);
                }
                return RX_QUEUED;
        }
@@ -1627,21 +1694,21 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
         * (IEEE 802.11i, 8.3.3.4.5) */
        if (entry->ccmp) {
                int i;
-               u8 pn[CCMP_PN_LEN], *rpn;
+               u8 pn[IEEE80211_CCMP_PN_LEN], *rpn;
                int queue;
                if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP)
                        return RX_DROP_UNUSABLE;
-               memcpy(pn, entry->last_pn, CCMP_PN_LEN);
-               for (i = CCMP_PN_LEN - 1; i >= 0; i--) {
+               memcpy(pn, entry->last_pn, IEEE80211_CCMP_PN_LEN);
+               for (i = IEEE80211_CCMP_PN_LEN - 1; i >= 0; i--) {
                        pn[i]++;
                        if (pn[i])
                                break;
                }
                queue = rx->security_idx;
                rpn = rx->key->u.ccmp.rx_pn[queue];
-               if (memcmp(pn, rpn, CCMP_PN_LEN))
+               if (memcmp(pn, rpn, IEEE80211_CCMP_PN_LEN))
                        return RX_DROP_UNUSABLE;
-               memcpy(entry->last_pn, pn, CCMP_PN_LEN);
+               memcpy(entry->last_pn, pn, IEEE80211_CCMP_PN_LEN);
        }
 
        skb_pull(rx->skb, ieee80211_hdrlen(fc));
@@ -1729,27 +1796,21 @@ static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
                if (unlikely(!ieee80211_has_protected(fc) &&
                             ieee80211_is_unicast_robust_mgmt_frame(rx->skb) &&
                             rx->key)) {
-                       if (ieee80211_is_deauth(fc))
-                               cfg80211_send_unprot_deauth(rx->sdata->dev,
-                                                           rx->skb->data,
-                                                           rx->skb->len);
-                       else if (ieee80211_is_disassoc(fc))
-                               cfg80211_send_unprot_disassoc(rx->sdata->dev,
-                                                             rx->skb->data,
-                                                             rx->skb->len);
+                       if (ieee80211_is_deauth(fc) ||
+                           ieee80211_is_disassoc(fc))
+                               cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
+                                                            rx->skb->data,
+                                                            rx->skb->len);
                        return -EACCES;
                }
                /* BIP does not use Protected field, so need to check MMIE */
                if (unlikely(ieee80211_is_multicast_robust_mgmt_frame(rx->skb) &&
                             ieee80211_get_mmie_keyidx(rx->skb) < 0)) {
-                       if (ieee80211_is_deauth(fc))
-                               cfg80211_send_unprot_deauth(rx->sdata->dev,
-                                                           rx->skb->data,
-                                                           rx->skb->len);
-                       else if (ieee80211_is_disassoc(fc))
-                               cfg80211_send_unprot_disassoc(rx->sdata->dev,
-                                                             rx->skb->data,
-                                                             rx->skb->len);
+                       if (ieee80211_is_deauth(fc) ||
+                           ieee80211_is_disassoc(fc))
+                               cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
+                                                            rx->skb->data,
+                                                            rx->skb->len);
                        return -EACCES;
                }
                /*