summaryrefslogtreecommitdiffstats
path: root/drivers/staging/csr/monitor.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/csr/monitor.c')
-rw-r--r--drivers/staging/csr/monitor.c399
1 files changed, 399 insertions, 0 deletions
diff --git a/drivers/staging/csr/monitor.c b/drivers/staging/csr/monitor.c
new file mode 100644
index 000000000000..628782ad641e
--- /dev/null
+++ b/drivers/staging/csr/monitor.c
@@ -0,0 +1,399 @@
+/*
+ * ---------------------------------------------------------------------------
+ * FILE: monitor.c
+ *
+ * Copyright (C) 2006-2008 by Cambridge Silicon Radio Ltd.
+ *
+ * Refer to LICENSE.txt included with this source code for details on
+ * the license terms.
+ *
+ * ---------------------------------------------------------------------------
+ */
+
+#include "unifi_priv.h"
+
+#ifdef UNIFI_SNIFF_ARPHRD
+
+
+#if (UNIFI_SNIFF_ARPHRD == ARPHRD_IEEE80211_RADIOTAP)
+#include <net/ieee80211_radiotap.h>
+#endif
+
+#ifndef ETH_P_80211_RAW
+#define ETH_P_80211_RAW ETH_P_ALL
+#endif
+
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * uf_start_sniff
+ *
+ * Start UniFi capture in SNIFF mode, i.e capture everything it hears.
+ *
+ * Arguments:
+ * priv Pointer to device private context struct
+ *
+ * Returns:
+ * 0 on success or kernel error code
+ * ---------------------------------------------------------------------------
+ */
+int
+uf_start_sniff(unifi_priv_t *priv)
+{
+ ul_client_t *pcli = priv->wext_client;
+ CSR_SIGNAL signal;
+ CSR_MLME_SNIFFJOIN_REQUEST *req = &signal.u.MlmeSniffjoinRequest;
+ int timeout = 1000;
+ int r;
+
+ req->Ifindex = priv->if_index;
+ req->Channel = priv->wext_conf.channel;
+ req->ChannelStartingFactor = 0;
+
+ signal.SignalPrimitiveHeader.SignalId = CSR_MLME_SNIFFJOIN_REQUEST_ID;
+
+ r = unifi_mlme_blocking_request(priv, pcli, &signal, NULL, timeout);
+ if (r < 0) {
+ unifi_error(priv, "failed to send SNIFFJOIN request, error %d\n", r);
+ return r;
+ }
+
+ r = pcli->reply_signal->u.MlmeSniffjoinConfirm.Resultcode;
+ if (r) {
+ unifi_notice(priv, "SNIFFJOIN request was rejected with result 0x%X (%s)\n",
+ r, lookup_result_code(r));
+ return -EIO;
+ }
+
+ return 0;
+} /* uf_start_sniff() */
+
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * netrx_radiotap
+ *
+ * Reformat a UniFi SNIFFDATA signal into a radiotap packet.
+ *
+ * Arguments:
+ * priv OS private context pointer.
+ * ind Pointer to a MA_UNITDATA_INDICATION or
+ * DS_UNITDATA_INDICATION indication structure.
+ *
+ * Notes:
+ * Radiotap header values are all little-endian, UniFi signals will have
+ * been converted to host-endian.
+ * ---------------------------------------------------------------------------
+ */
+#if (UNIFI_SNIFF_ARPHRD == ARPHRD_IEEE80211_RADIOTAP)
+static void
+netrx_radiotap(unifi_priv_t *priv,
+ const CSR_MA_SNIFFDATA_INDICATION *ind,
+ struct sk_buff *skb_orig)
+{
+ struct net_device *dev = priv->netdev;
+ struct sk_buff *skb = NULL;
+ unsigned char *ptr;
+ unsigned char *base;
+ int ind_data_len = skb_orig->len - 2 - ETH_HLEN;
+ struct unifi_rx_radiotap_header {
+ struct ieee80211_radiotap_header rt_hdr;
+ /* IEEE80211_RADIOTAP_TSFT */
+ u64 rt_tsft;
+ /* IEEE80211_RADIOTAP_FLAGS */
+ u8 rt_flags;
+ /* IEEE80211_RADIOTAP_RATE */
+ u8 rt_rate;
+ /* IEEE80211_RADIOTAP_CHANNEL */
+ u16 rt_chan;
+ u16 rt_chan_flags;
+ /* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
+ u8 rt_dbm_antsignal;
+ /* IEEE80211_RADIOTAP_DBM_ANTNOISE */
+ u8 rt_dbm_antnoise;
+ /* IEEE80211_RADIOTAP_ANTENNA */
+ u8 rt_antenna;
+
+ /* pad to 4-byte boundary */
+ u8 pad[3];
+ } __attribute__((__packed__));
+
+ struct unifi_rx_radiotap_header *unifi_rt;
+ int signal, noise, snr;
+
+ func_enter();
+
+ if (ind_data_len <= 0) {
+ unifi_error(priv, "Invalid length in CSR_MA_SNIFFDATA_INDICATION.\n");
+ return;
+ }
+
+ /*
+ * Allocate a SKB for the received data packet, including radiotap
+ * header.
+ */
+ skb = dev_alloc_skb(ind_data_len + sizeof(struct unifi_rx_radiotap_header) + 4);
+ if (! skb) {
+ unifi_error(priv, "alloc_skb failed.\n");
+ priv->stats.rx_errors++;
+ return;
+ }
+
+ base = skb->data;
+
+ /* Reserve the radiotap header at the front of skb */
+ unifi_rt = (struct unifi_rx_radiotap_header *)
+ skb_put(skb, sizeof(struct unifi_rx_radiotap_header));
+
+ /* Copy in the 802.11 frame */
+ ptr = skb_put(skb, ind_data_len);
+ memcpy(ptr, skb_orig->data, ind_data_len);
+
+ unifi_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
+ unifi_rt->rt_hdr.it_pad = 0; /* always good to zero */
+ unifi_rt->rt_hdr.it_len = sizeof(struct unifi_rx_radiotap_header);
+
+ /* Big bitfield of all the fields we provide in radiotap */
+ unifi_rt->rt_hdr.it_present = 0
+ | (1 << IEEE80211_RADIOTAP_TSFT)
+ | (1 << IEEE80211_RADIOTAP_FLAGS)
+ | (1 << IEEE80211_RADIOTAP_RATE)
+ | (1 << IEEE80211_RADIOTAP_CHANNEL)
+ | (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL)
+ | (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE)
+ | (1 << IEEE80211_RADIOTAP_ANTENNA)
+ ;
+
+
+ /* No flags to set */
+ unifi_rt->rt_tsft = (((u64)ind->Timestamp.x[7]) | (((u64)ind->Timestamp.x[6]) << 8) |
+ (((u64)ind->Timestamp.x[5]) << 16) | (((u64)ind->Timestamp.x[4]) << 24) |
+ (((u64)ind->Timestamp.x[3]) << 32) | (((u64)ind->Timestamp.x[2]) << 40) |
+ (((u64)ind->Timestamp.x[1]) << 48) | (((u64)ind->Timestamp.x[0]) << 56));
+
+ unifi_rt->rt_flags = 0;
+
+ unifi_rt->rt_rate = ind->Rate;
+
+ unifi_rt->rt_chan = cpu_to_le16(ieee80211chan2mhz(priv->wext_conf.channel));
+ unifi_rt->rt_chan_flags = 0;
+
+ /* Convert signal to dBm */
+ signal = (s16)unifi2host_16(ind->Rssi); /* in dBm */
+ snr = (s16)unifi2host_16(ind->Snr); /* in dB */
+ noise = signal - snr;
+
+ unifi_rt->rt_dbm_antsignal = signal;
+ unifi_rt->rt_dbm_antnoise = noise;
+
+ unifi_rt->rt_antenna = ind->AntennaId;
+
+
+ skb->dev = dev;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
+ skb->mac_header = skb->data;
+#else
+ skb->mac.raw = skb->data;
+#endif
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = __constant_htons(ETH_P_80211_RAW);
+ memset(skb->cb, 0, sizeof(skb->cb));
+
+ /* Pass up to Linux network stack */
+ netif_rx_ni(skb);
+
+ dev->last_rx = jiffies;
+
+ /* Bump the rx stats */
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += ind_data_len;
+
+ func_exit();
+} /* netrx_radiotap() */
+#endif /* RADIOTAP */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * netrx_prism
+ *
+ * Reformat a UniFi SNIFFDATA signal into a Prism format sniff packet.
+ *
+ * Arguments:
+ * priv OS private context pointer.
+ * ind Pointer to a MA_UNITDATA_INDICATION or
+ * DS_UNITDATA_INDICATION indication structure.
+ *
+ * Notes:
+ * Radiotap header values are all little-endian, UniFi signals will have
+ * been converted to host-endian.
+ * ---------------------------------------------------------------------------
+ */
+#if (UNIFI_SNIFF_ARPHRD == ARPHRD_IEEE80211_PRISM)
+static void
+netrx_prism(unifi_priv_t *priv,
+ const CSR_MA_SNIFFDATA_INDICATION *ind,
+ struct sk_buff *skb_orig)
+{
+ struct net_device *dev = priv->netdev;
+ struct sk_buff *skb = NULL;
+ unsigned char *ptr;
+ unsigned char *base;
+ int ind_data_len = skb_orig->len - 2 - ETH_HLEN;
+#define WLANCAP_MAGIC_COOKIE_V1 0x80211001
+ struct avs_header_v1 {
+ uint32 version;
+ uint32 length;
+ uint64 mactime;
+ uint64 hosttime;
+ uint32 phytype;
+ uint32 channel;
+ uint32 datarate;
+ uint32 antenna;
+ uint32 priority;
+ uint32 ssi_type;
+ int32 ssi_signal;
+ int32 ssi_noise;
+ uint32 preamble;
+ uint32 encoding;
+ } *avs;
+ int signal, noise, snr;
+
+ func_enter();
+
+ if (ind_data_len <= 0) {
+ unifi_error(priv, "Invalid length in CSR_MA_SNIFFDATA_INDICATION.\n");
+ return;
+ }
+
+ /*
+ * Allocate a SKB for the received data packet, including radiotap
+ * header.
+ */
+ skb = dev_alloc_skb(ind_data_len + sizeof(struct avs_header_v1) + 4);
+ if (! skb) {
+ unifi_error(priv, "alloc_skb failed.\n");
+ priv->stats.rx_errors++;
+ return;
+ }
+
+ base = skb->data;
+
+ /* Reserve the radiotap header at the front of skb */
+ avs = (struct avs_header_v1 *)skb_put(skb, sizeof(struct avs_header_v1));
+
+ /* Copy in the 802.11 frame */
+ ptr = skb_put(skb, ind_data_len);
+ memcpy(ptr, skb_orig->data, ind_data_len);
+
+ /* Convert signal to dBm */
+ signal = 0x10000 - ((s16)unifi2host_16(ind->Rssi)); /* in dBm */
+ snr = (s16)unifi2host_16(ind->Snr); /* in dB */
+ noise = signal - snr;
+
+ avs->version = htonl(WLANCAP_MAGIC_COOKIE_V1);
+ avs->length = htonl(sizeof(struct avs_header_v1));
+ avs->mactime = __cpu_to_be64(ind->Timestamp);
+ avs->hosttime = __cpu_to_be64(jiffies);
+ avs->phytype = htonl(9); /* dss_ofdm_dot11_g */
+ avs->channel = htonl(priv->wext_conf.channel);
+ avs->datarate = htonl(ind->Rate * 5);
+ avs->antenna = htonl(ind->Antenna);
+ avs->priority = htonl(0); /* unknown */
+ avs->ssi_type = htonl(2); /* dBm */
+ avs->ssi_signal = htonl(signal);
+ avs->ssi_noise = htonl(noise);
+ avs->preamble = htonl(0); /* unknown */
+ avs->encoding = htonl(0); /* unknown */
+
+
+ skb->dev = dev;
+ skb->mac.raw = skb->data;
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = __constant_htons(ETH_P_80211_RAW);
+ memset(skb->cb, 0, sizeof(skb->cb));
+
+ /* Pass up to Linux network stack */
+ netif_rx_ni(skb);
+
+ dev->last_rx = jiffies;
+
+ /* Bump the rx stats */
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += ind_data_len;
+
+ func_exit();
+} /* netrx_prism() */
+#endif /* PRISM */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * ma_sniffdata_ind
+ *
+ * Reformat a UniFi SNIFFDATA signal into a network
+ *
+ * Arguments:
+ * ospriv OS private context pointer.
+ * ind Pointer to a MA_UNITDATA_INDICATION or
+ * DS_UNITDATA_INDICATION indication structure.
+ * bulkdata Pointer to a bulk data structure, describing
+ * the data received.
+ *
+ * Notes:
+ * Radiotap header values are all little-endian, UniFi signals will have
+ * been converted to host-endian.
+ * ---------------------------------------------------------------------------
+ */
+void
+ma_sniffdata_ind(void *ospriv,
+ const CSR_MA_SNIFFDATA_INDICATION *ind,
+ const bulk_data_param_t *bulkdata)
+{
+ unifi_priv_t *priv = ospriv;
+ struct net_device *dev = priv->netdev;
+ struct sk_buff *skb = (struct sk_buff*)bulkdata->d[0].os_net_buf_ptr;
+
+ func_enter();
+
+ if (bulkdata->d[0].data_length == 0) {
+ unifi_warning(priv, "rx: MA-SNIFFDATA indication with zero bulk data\n");
+ func_exit();
+ return;
+ }
+
+ skb->len = bulkdata->d[0].data_length;
+
+ /* We only process data packets if the interface is open */
+ if (unlikely(!netif_running(dev))) {
+ priv->stats.rx_dropped++;
+ priv->wext_conf.wireless_stats.discard.misc++;
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ if (ind->ReceptionStatus) {
+ priv->stats.rx_dropped++;
+ priv->wext_conf.wireless_stats.discard.misc++;
+ printk(KERN_INFO "unifi: Dropping corrupt sniff packet\n");
+ dev_kfree_skb(skb);
+ return;
+ }
+
+#if (UNIFI_SNIFF_ARPHRD == ARPHRD_IEEE80211_PRISM)
+ netrx_prism(priv, ind, skb);
+#endif /* PRISM */
+
+#if (UNIFI_SNIFF_ARPHRD == ARPHRD_IEEE80211_RADIOTAP)
+ netrx_radiotap(priv, ind, skb);
+#endif /* RADIOTAP */
+
+ dev_kfree_skb(skb);
+
+} /* ma_sniffdata_ind() */
+
+
+#endif /* UNIFI_SNIFF_ARPHRD */
+