[PATCH] ipw2100: add radiotap headers to packtes captured in monitor mode
authorStefan Rompf <stefan@loplof.de>
Tue, 21 Feb 2006 10:36:17 +0000 (18:36 +0800)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 17 Mar 2006 21:14:48 +0000 (16:14 -0500)
Signed-off-by: Stefan Rompf <stefan@loplof.de>
Signed-off-by: Andrea Merello <andreamrl at tiscali it>
Signed-off-by: Zhu Yi <yi.zhu@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ipw2100.c
drivers/net/wireless/ipw2100.h

index 84f63876bf7574bfaa1972a0f182306e40f5a520..d429fd576a66e4da1b1b855283da21f1f6e14602 100644 (file)
@@ -2390,15 +2390,6 @@ static void isr_rx(struct ipw2100_priv *priv, int i,
                IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
                return;
        }
-#ifdef CONFIG_IPW2100_MONITOR
-       if (unlikely(priv->ieee->iw_mode == IW_MODE_MONITOR &&
-                    priv->config & CFG_CRC_CHECK &&
-                    status->flags & IPW_STATUS_FLAG_CRC_ERROR)) {
-               IPW_DEBUG_RX("CRC error in packet.  Dropping.\n");
-               priv->ieee->stats.rx_errors++;
-               return;
-       }
-#endif
 
        if (unlikely(priv->ieee->iw_mode != IW_MODE_MONITOR &&
                     !(priv->status & STATUS_ASSOCIATED))) {
@@ -2446,6 +2437,88 @@ static void isr_rx(struct ipw2100_priv *priv, int i,
        priv->rx_queue.drv[i].host_addr = packet->dma_addr;
 }
 
+#ifdef CONFIG_IPW2100_MONITOR
+
+static void isr_rx_monitor(struct ipw2100_priv *priv, int i,
+                  struct ieee80211_rx_stats *stats)
+{
+       struct ipw2100_status *status = &priv->status_queue.drv[i];
+       struct ipw2100_rx_packet *packet = &priv->rx_buffers[i];
+
+       IPW_DEBUG_RX("Handler...\n");
+
+       /* Magic struct that slots into the radiotap header -- no reason
+        * to build this manually element by element, we can write it much
+        * more efficiently than we can parse it. ORDER MATTERS HERE */
+       struct ipw_rt_hdr {
+               struct ieee80211_radiotap_header rt_hdr;
+               s8 rt_dbmsignal; /* signal in dbM, kluged to signed */
+       } *ipw_rt;
+
+       if (unlikely(status->frame_size > skb_tailroom(packet->skb) - sizeof(struct ipw_rt_hdr))) {
+               IPW_DEBUG_INFO("%s: frame_size (%u) > skb_tailroom (%u)!"
+                              "  Dropping.\n",
+                              priv->net_dev->name,
+                              status->frame_size, skb_tailroom(packet->skb));
+               priv->ieee->stats.rx_errors++;
+               return;
+       }
+
+       if (unlikely(!netif_running(priv->net_dev))) {
+               priv->ieee->stats.rx_errors++;
+               priv->wstats.discard.misc++;
+               IPW_DEBUG_DROP("Dropping packet while interface is not up.\n");
+               return;
+       }
+
+       if (unlikely(priv->config & CFG_CRC_CHECK &&
+                    status->flags & IPW_STATUS_FLAG_CRC_ERROR)) {
+               IPW_DEBUG_RX("CRC error in packet.  Dropping.\n");
+               priv->ieee->stats.rx_errors++;
+               return;
+       }
+
+       pci_unmap_single(priv->pci_dev,
+                        packet->dma_addr,
+                        sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE);
+       memmove(packet->skb->data + sizeof(struct ipw_rt_hdr),
+               packet->skb->data, status->frame_size);
+
+       ipw_rt = (struct ipw_rt_hdr *) packet->skb->data;
+
+       ipw_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
+       ipw_rt->rt_hdr.it_pad = 0; /* always good to zero */
+       ipw_rt->rt_hdr.it_len = sizeof(struct ipw_rt_hdr); /* total header+data */
+
+       ipw_rt->rt_hdr.it_present = 1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL;
+
+       ipw_rt->rt_dbmsignal = status->rssi + IPW2100_RSSI_TO_DBM;
+
+       skb_put(packet->skb, status->frame_size + sizeof(struct ipw_rt_hdr));
+
+       if (!ieee80211_rx(priv->ieee, packet->skb, stats)) {
+               priv->ieee->stats.rx_errors++;
+
+               /* ieee80211_rx failed, so it didn't free the SKB */
+               dev_kfree_skb_any(packet->skb);
+               packet->skb = NULL;
+       }
+
+       /* We need to allocate a new SKB and attach it to the RDB. */
+       if (unlikely(ipw2100_alloc_skb(priv, packet))) {
+               IPW_DEBUG_WARNING(
+                       "%s: Unable to allocate SKB onto RBD ring - disabling "
+                       "adapter.\n", priv->net_dev->name);
+               /* TODO: schedule adapter shutdown */
+               IPW_DEBUG_INFO("TODO: Shutdown adapter...\n");
+       }
+
+       /* Update the RDB entry */
+       priv->rx_queue.drv[i].host_addr = packet->dma_addr;
+}
+
+#endif
+
 static int ipw2100_corruption_check(struct ipw2100_priv *priv, int i)
 {
        struct ipw2100_status *status = &priv->status_queue.drv[i];
@@ -2577,7 +2650,7 @@ static void __ipw2100_rx_process(struct ipw2100_priv *priv)
                case P8023_DATA_VAL:
 #ifdef CONFIG_IPW2100_MONITOR
                        if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
-                               isr_rx(priv, i, &stats);
+                               isr_rx_monitor(priv, i, &stats);
                                break;
                        }
 #endif
@@ -3882,7 +3955,7 @@ static int ipw2100_switch_mode(struct ipw2100_priv *priv, u32 mode)
 #ifdef CONFIG_IPW2100_MONITOR
        case IW_MODE_MONITOR:
                priv->last_mode = priv->ieee->iw_mode;
-               priv->net_dev->type = ARPHRD_IEEE80211;
+               priv->net_dev->type = ARPHRD_IEEE80211_RADIOTAP;
                break;
 #endif                         /* CONFIG_IPW2100_MONITOR */
        }
index 948d426fcf9d050aaab3a5963aae0d0a4117fed9..6f0b40f96abd94688dc139aac3ba3ce350720336 100644 (file)
 
 #include <net/ieee80211.h>
 
+#ifdef CONFIG_IPW2100_MONITOR
+#include <net/ieee80211_radiotap.h>
+#endif
+
 #include <linux/workqueue.h>
 
 struct ipw2100_priv;