d80211: add radiotap support

This patch adds support for radiotap to d80211. The driver must set
IEEE80211_HW_MONITOR_DURING_OPER as well as
IEEE80211_HW_RADIOTAP_SUPPORTED, and it must send radiotap frames when
there is at least one monitor interface up. Tested with zd1211rw-d80211.

Signed-off-by: Michael Wu <[EMAIL PROTECTED]>
---

 include/net/d80211.h         |    3 +++
 net/d80211/ieee80211.c       |   41 ++++++++++++++++++++++++++++++++---------
 net/d80211/ieee80211_iface.c |    5 ++++-
 3 files changed, 39 insertions(+), 10 deletions(-)

diff --git a/include/net/d80211.h b/include/net/d80211.h
index 30980e1..27b2487 100644
--- a/include/net/d80211.h
+++ b/include/net/d80211.h
@@ -501,6 +501,9 @@ struct ieee80211_hw {
         * per-packet RC4 key with each TX frame when doing hwcrypto */
 #define IEEE80211_HW_TKIP_REQ_PHASE2_KEY (1<<14)
 
+       /* Driver supports radiotap. Temporary until all drivers support it. */
+#define IEEE80211_HW_RADIOTAP_SUPPORTED (1<<20)
+
        u32 flags;                      /* hardware flags defined above */
 
        /* Set to the size of a needed device specific skb headroom for TX 
skbs. */
diff --git a/net/d80211/ieee80211.c b/net/d80211/ieee80211.c
index 6e10db5..a73046c 100644
--- a/net/d80211/ieee80211.c
+++ b/net/d80211/ieee80211.c
@@ -8,6 +8,7 @@
  */
 
 #include <net/d80211.h>
+#include <net/ieee80211_radiotap.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/netdevice.h>
@@ -284,6 +285,14 @@ int ieee80211_get_hdrlen_from_skb(struct
 }
 EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
 
+static int ieee80211_get_radiotap_len(struct sk_buff *skb)
+{
+       struct ieee80211_radiotap_header *hdr =
+               (struct ieee80211_radiotap_header *) skb->data;
+
+       return le16_to_cpu(hdr->it_len);
+}
+
 #ifdef CONFIG_D80211_LOWTX_FRAME_DUMP
 static void ieee80211_dump_frame(const char *ifname, const char *title,
                                 struct sk_buff *skb)
@@ -2650,17 +2659,19 @@ ieee80211_rx_monitor(struct net_device *
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (skb_headroom(skb) < hlen) {
-               I802_DEBUG_INC(local->rx_expand_skb_head);
-               if (pskb_expand_head(skb, hlen, 0, GFP_ATOMIC)) {
-                       dev_kfree_skb(skb);
-                        return;
+       if (!(local->hw.flags & IEEE80211_HW_RADIOTAP_SUPPORTED)) {
+               if (skb_headroom(skb) < hlen) {
+                       I802_DEBUG_INC(local->rx_expand_skb_head);
+                       if (pskb_expand_head(skb, hlen, 0, GFP_ATOMIC)) {
+                               dev_kfree_skb(skb);
+                               return;
+                       }
                }
-       }
 
-       fi = (struct ieee80211_frame_info *) skb_push(skb, hlen);
+               fi = (struct ieee80211_frame_info *) skb_push(skb, hlen);
+               ieee80211_fill_frame_info(local, fi, status);
+       }
 
-       ieee80211_fill_frame_info(local, fi, status);
        sdata->stats.rx_packets++;
        sdata->stats.rx_bytes += skb->len;
 
@@ -3064,6 +3075,10 @@ ieee80211_rx_h_monitor(struct ieee80211_
                return TXRX_QUEUED;
        }
 
+       if (rx->local->monitors &&
+           rx->local->hw.flags & IEEE80211_HW_RADIOTAP_SUPPORTED)
+               skb_pull(rx->skb, ieee80211_get_radiotap_len(rx->skb));
+
        return TXRX_CONTINUE;
 }
 
@@ -3628,6 +3643,13 @@ void __ieee80211_rx(struct ieee80211_hw
        struct ieee80211_txrx_data rx;
        u16 type;
        int multicast;
+       int radiotap_len = 0;
+
+       if (local->monitors &&
+           local->hw.flags & IEEE80211_HW_RADIOTAP_SUPPORTED) {
+               radiotap_len = ieee80211_get_radiotap_len(skb);
+               skb_pull(skb, radiotap_len);
+       }
 
        hdr = (struct ieee80211_hdr *) skb->data;
        memset(&rx, 0, sizeof(rx));
@@ -3664,6 +3686,7 @@ void __ieee80211_rx(struct ieee80211_hw
                goto end;
        skb = rx.skb;
 
+       skb_push(skb, radiotap_len);
        if (sta && !sta->assoc_ap && !(sta->flags & WLAN_STA_WDS) &&
            !local->iff_promiscs && !multicast) {
                rx.u.rx.ra_match = 1;
@@ -3672,7 +3695,7 @@ void __ieee80211_rx(struct ieee80211_hw
        } else {
                struct ieee80211_sub_if_data *prev = NULL;
                struct sk_buff *skb_new;
-               u8 *bssid = ieee80211_get_bssid(hdr, skb->len);
+               u8 *bssid = ieee80211_get_bssid(hdr, skb->len - radiotap_len);
 
                list_for_each_entry(sdata, &local->sub_if_list, list) {
                        rx.u.rx.ra_match = 1;
diff --git a/net/d80211/ieee80211_iface.c b/net/d80211/ieee80211_iface.c
index 3e9d531..c1bb6d0 100644
--- a/net/d80211/ieee80211_iface.c
+++ b/net/d80211/ieee80211_iface.c
@@ -198,7 +198,10 @@ void ieee80211_if_set_type(struct net_de
                break;
        }
        case IEEE80211_IF_TYPE_MNTR:
-               dev->type = ARPHRD_IEEE80211_PRISM;
+               if (sdata->local->hw.flags & IEEE80211_HW_RADIOTAP_SUPPORTED)
+                       dev->type = ARPHRD_IEEE80211_RADIOTAP;
+               else
+                       dev->type = ARPHRD_IEEE80211_PRISM;
                break;
        default:
                printk(KERN_WARNING "%s: %s: Unknown interface type 0x%x",

Attachment: pgpQ25c7C6xRv.pgp
Description: PGP signature

Reply via email to