]> Pileus Git - ~andy/linux/blob - drivers/staging/csr/monitor.c
Merge branch 'wl12xx-next' into for-linville
[~andy/linux] / drivers / staging / csr / monitor.c
1 /*
2  * ---------------------------------------------------------------------------
3  *  FILE:     monitor.c
4  *
5  * Copyright (C) 2006-2008 by Cambridge Silicon Radio Ltd.
6  *
7  * Refer to LICENSE.txt included with this source code for details on
8  * the license terms.
9  *
10  * ---------------------------------------------------------------------------
11  */
12
13 #include "unifi_priv.h"
14
15 #ifdef UNIFI_SNIFF_ARPHRD
16
17
18 #if (UNIFI_SNIFF_ARPHRD == ARPHRD_IEEE80211_RADIOTAP)
19 #include <net/ieee80211_radiotap.h>
20 #endif
21
22 #ifndef ETH_P_80211_RAW
23 #define ETH_P_80211_RAW ETH_P_ALL
24 #endif
25
26 /*
27  * ---------------------------------------------------------------------------
28  *  uf_start_sniff
29  *
30  *      Start UniFi capture in SNIFF mode, i.e capture everything it hears.
31  *
32  *  Arguments:
33  *      priv            Pointer to device private context struct
34  *
35  *  Returns:
36  *      0 on success or kernel error code
37  * ---------------------------------------------------------------------------
38  */
39 int
40 uf_start_sniff(unifi_priv_t *priv)
41 {
42     ul_client_t *pcli = priv->wext_client;
43     CSR_SIGNAL signal;
44     CSR_MLME_SNIFFJOIN_REQUEST *req = &signal.u.MlmeSniffjoinRequest;
45     int timeout = 1000;
46     int r;
47
48     req->Ifindex = priv->if_index;
49     req->Channel = priv->wext_conf.channel;
50     req->ChannelStartingFactor = 0;
51
52     signal.SignalPrimitiveHeader.SignalId = CSR_MLME_SNIFFJOIN_REQUEST_ID;
53
54     r = unifi_mlme_blocking_request(priv, pcli, &signal, NULL, timeout);
55     if (r < 0) {
56         unifi_error(priv, "failed to send SNIFFJOIN request, error %d\n", r);
57         return r;
58     }
59
60     r = pcli->reply_signal->u.MlmeSniffjoinConfirm.Resultcode;
61     if (r) {
62         unifi_notice(priv, "SNIFFJOIN request was rejected with result 0x%X (%s)\n",
63                      r, lookup_result_code(r));
64         return -EIO;
65     }
66
67     return 0;
68 } /* uf_start_sniff() */
69
70
71
72 /*
73  * ---------------------------------------------------------------------------
74  * netrx_radiotap
75  *
76  *      Reformat a UniFi SNIFFDATA signal into a radiotap packet.
77  *
78  * Arguments:
79  *      priv            OS private context pointer.
80  *      ind             Pointer to a MA_UNITDATA_INDICATION or
81  *                      DS_UNITDATA_INDICATION indication structure.
82  *
83  * Notes:
84  *      Radiotap header values are all little-endian, UniFi signals will have
85  *      been converted to host-endian.
86  * ---------------------------------------------------------------------------
87  */
88 #if (UNIFI_SNIFF_ARPHRD == ARPHRD_IEEE80211_RADIOTAP)
89 static void
90 netrx_radiotap(unifi_priv_t *priv,
91                const CSR_MA_SNIFFDATA_INDICATION *ind,
92                struct sk_buff *skb_orig)
93 {
94     struct net_device *dev = priv->netdev;
95     struct sk_buff *skb = NULL;
96     unsigned char *ptr;
97     unsigned char *base;
98     int ind_data_len = skb_orig->len - 2 - ETH_HLEN;
99     struct unifi_rx_radiotap_header {
100         struct ieee80211_radiotap_header rt_hdr;
101         /* IEEE80211_RADIOTAP_TSFT */
102         u64 rt_tsft;
103         /* IEEE80211_RADIOTAP_FLAGS */
104         u8  rt_flags;
105         /* IEEE80211_RADIOTAP_RATE */
106         u8  rt_rate;
107         /* IEEE80211_RADIOTAP_CHANNEL */
108         u16 rt_chan;
109         u16 rt_chan_flags;
110         /* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
111         u8  rt_dbm_antsignal;
112         /* IEEE80211_RADIOTAP_DBM_ANTNOISE */
113         u8  rt_dbm_antnoise;
114         /* IEEE80211_RADIOTAP_ANTENNA */
115         u8  rt_antenna;
116
117         /* pad to 4-byte boundary */
118         u8 pad[3];
119     } __attribute__((__packed__));
120
121     struct unifi_rx_radiotap_header *unifi_rt;
122     int signal, noise, snr;
123
124     if (ind_data_len <= 0) {
125         unifi_error(priv, "Invalid length in CSR_MA_SNIFFDATA_INDICATION.\n");
126         return;
127     }
128
129     /*
130      * Allocate a SKB for the received data packet, including radiotap
131      * header.
132      */
133     skb = dev_alloc_skb(ind_data_len + sizeof(struct unifi_rx_radiotap_header) + 4);
134     if (! skb) {
135         unifi_error(priv, "alloc_skb failed.\n");
136         priv->stats.rx_errors++;
137         return;
138     }
139
140     base = skb->data;
141
142     /* Reserve the radiotap header at the front of skb */
143     unifi_rt = (struct unifi_rx_radiotap_header *)
144         skb_put(skb, sizeof(struct unifi_rx_radiotap_header));
145
146     /* Copy in the 802.11 frame */
147     ptr = skb_put(skb, ind_data_len);
148     memcpy(ptr, skb_orig->data, ind_data_len);
149
150     unifi_rt->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION;
151     unifi_rt->rt_hdr.it_pad = 0;        /* always good to zero */
152     unifi_rt->rt_hdr.it_len = sizeof(struct unifi_rx_radiotap_header);
153
154     /* Big bitfield of all the fields we provide in radiotap */
155     unifi_rt->rt_hdr.it_present = 0
156         | (1 << IEEE80211_RADIOTAP_TSFT)
157         | (1 << IEEE80211_RADIOTAP_FLAGS)
158         | (1 << IEEE80211_RADIOTAP_RATE)
159         | (1 << IEEE80211_RADIOTAP_CHANNEL)
160         | (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL)
161         | (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE)
162         | (1 << IEEE80211_RADIOTAP_ANTENNA)
163         ;
164
165
166     /* No flags to set */
167     unifi_rt->rt_tsft = (((u64)ind->Timestamp.x[7]) | (((u64)ind->Timestamp.x[6]) << 8) |
168                          (((u64)ind->Timestamp.x[5]) << 16) | (((u64)ind->Timestamp.x[4]) << 24) |
169                          (((u64)ind->Timestamp.x[3]) << 32) | (((u64)ind->Timestamp.x[2]) << 40) |
170                          (((u64)ind->Timestamp.x[1]) << 48) | (((u64)ind->Timestamp.x[0]) << 56));
171
172     unifi_rt->rt_flags = 0;
173
174     unifi_rt->rt_rate = ind->Rate;
175
176     unifi_rt->rt_chan = cpu_to_le16(ieee80211chan2mhz(priv->wext_conf.channel));
177     unifi_rt->rt_chan_flags = 0;
178
179     /* Convert signal to dBm */
180     signal = (s16)unifi2host_16(ind->Rssi);  /* in dBm */
181     snr    = (s16)unifi2host_16(ind->Snr);   /* in dB */
182     noise  = signal - snr;
183
184     unifi_rt->rt_dbm_antsignal = signal;
185     unifi_rt->rt_dbm_antnoise = noise;
186
187     unifi_rt->rt_antenna = ind->AntennaId;
188
189
190     skb->dev = dev;
191     skb->mac_header = skb->data;
192     skb->pkt_type = PACKET_OTHERHOST;
193     skb->protocol = __constant_htons(ETH_P_80211_RAW);
194     memset(skb->cb, 0, sizeof(skb->cb));
195
196     /* Pass up to Linux network stack */
197     netif_rx_ni(skb);
198
199     dev->last_rx = jiffies;
200
201     /* Bump the rx stats */
202     priv->stats.rx_packets++;
203     priv->stats.rx_bytes += ind_data_len;
204
205 } /* netrx_radiotap() */
206 #endif /* RADIOTAP */
207
208
209 /*
210  * ---------------------------------------------------------------------------
211  * netrx_prism
212  *
213  *      Reformat a UniFi SNIFFDATA signal into a Prism format sniff packet.
214  *
215  * Arguments:
216  *      priv            OS private context pointer.
217  *      ind             Pointer to a MA_UNITDATA_INDICATION or
218  *                      DS_UNITDATA_INDICATION indication structure.
219  *
220  * Notes:
221  *      Radiotap header values are all little-endian, UniFi signals will have
222  *      been converted to host-endian.
223  * ---------------------------------------------------------------------------
224  */
225 #if (UNIFI_SNIFF_ARPHRD == ARPHRD_IEEE80211_PRISM)
226 static void
227 netrx_prism(unifi_priv_t *priv,
228             const CSR_MA_SNIFFDATA_INDICATION *ind,
229             struct sk_buff *skb_orig)
230 {
231     struct net_device *dev = priv->netdev;
232     struct sk_buff *skb = NULL;
233     unsigned char *ptr;
234     unsigned char *base;
235     int ind_data_len = skb_orig->len - 2 - ETH_HLEN;
236 #define WLANCAP_MAGIC_COOKIE_V1 0x80211001
237     struct avs_header_v1 {
238         uint32  version;
239         uint32  length;
240         uint64  mactime;
241         uint64  hosttime;
242         uint32  phytype;
243         uint32  channel;
244         uint32  datarate;
245         uint32  antenna;
246         uint32  priority;
247         uint32  ssi_type;
248         int32   ssi_signal;
249         int32   ssi_noise;
250         uint32  preamble;
251         uint32  encoding;
252     } *avs;
253     int signal, noise, snr;
254
255     if (ind_data_len <= 0) {
256         unifi_error(priv, "Invalid length in CSR_MA_SNIFFDATA_INDICATION.\n");
257         return;
258     }
259
260     /*
261      * Allocate a SKB for the received data packet, including radiotap
262      * header.
263      */
264     skb = dev_alloc_skb(ind_data_len + sizeof(struct avs_header_v1) + 4);
265     if (! skb) {
266         unifi_error(priv, "alloc_skb failed.\n");
267         priv->stats.rx_errors++;
268         return;
269     }
270
271     base = skb->data;
272
273     /* Reserve the radiotap header at the front of skb */
274     avs = (struct avs_header_v1 *)skb_put(skb, sizeof(struct avs_header_v1));
275
276     /* Copy in the 802.11 frame */
277     ptr = skb_put(skb, ind_data_len);
278     memcpy(ptr, skb_orig->data, ind_data_len);
279
280     /* Convert signal to dBm */
281     signal = 0x10000 - ((s16)unifi2host_16(ind->Rssi));  /* in dBm */
282     snr    = (s16)unifi2host_16(ind->Snr);   /* in dB */
283     noise  = signal - snr;
284
285     avs->version        = htonl(WLANCAP_MAGIC_COOKIE_V1);
286     avs->length         = htonl(sizeof(struct avs_header_v1));
287     avs->mactime        = __cpu_to_be64(ind->Timestamp);
288     avs->hosttime       = __cpu_to_be64(jiffies);
289     avs->phytype        = htonl(9);             /* dss_ofdm_dot11_g */
290     avs->channel        = htonl(priv->wext_conf.channel);
291     avs->datarate       = htonl(ind->Rate * 5);
292     avs->antenna        = htonl(ind->Antenna);
293     avs->priority       = htonl(0);             /* unknown */
294     avs->ssi_type       = htonl(2);             /* dBm */
295     avs->ssi_signal     = htonl(signal);
296     avs->ssi_noise      = htonl(noise);
297     avs->preamble       = htonl(0); /* unknown */
298     avs->encoding       = htonl(0); /* unknown */
299
300
301     skb->dev = dev;
302     skb->mac.raw = skb->data;
303     skb->pkt_type = PACKET_OTHERHOST;
304     skb->protocol = __constant_htons(ETH_P_80211_RAW);
305     memset(skb->cb, 0, sizeof(skb->cb));
306
307     /* Pass up to Linux network stack */
308     netif_rx_ni(skb);
309
310     dev->last_rx = jiffies;
311
312     /* Bump the rx stats */
313     priv->stats.rx_packets++;
314     priv->stats.rx_bytes += ind_data_len;
315
316 } /* netrx_prism() */
317 #endif /* PRISM */
318
319
320 /*
321  * ---------------------------------------------------------------------------
322  * ma_sniffdata_ind
323  *
324  *      Reformat a UniFi SNIFFDATA signal into a network
325  *
326  * Arguments:
327  *      ospriv          OS private context pointer.
328  *      ind             Pointer to a MA_UNITDATA_INDICATION or
329  *                      DS_UNITDATA_INDICATION indication structure.
330  *      bulkdata        Pointer to a bulk data structure, describing
331  *                      the data received.
332  *
333  * Notes:
334  *      Radiotap header values are all little-endian, UniFi signals will have
335  *      been converted to host-endian.
336  * ---------------------------------------------------------------------------
337  */
338 void
339 ma_sniffdata_ind(void *ospriv,
340                  const CSR_MA_SNIFFDATA_INDICATION *ind,
341                  const bulk_data_param_t *bulkdata)
342 {
343     unifi_priv_t *priv = ospriv;
344     struct net_device *dev = priv->netdev;
345     struct sk_buff *skb = (struct sk_buff*)bulkdata->d[0].os_net_buf_ptr;
346
347     if (bulkdata->d[0].data_length == 0) {
348         unifi_warning(priv, "rx: MA-SNIFFDATA indication with zero bulk data\n");
349         return;
350     }
351
352     skb->len = bulkdata->d[0].data_length;
353
354     /* We only process data packets if the interface is open */
355     if (unlikely(!netif_running(dev))) {
356         priv->stats.rx_dropped++;
357         priv->wext_conf.wireless_stats.discard.misc++;
358         dev_kfree_skb(skb);
359         return;
360     }
361
362     if (ind->ReceptionStatus) {
363         priv->stats.rx_dropped++;
364         priv->wext_conf.wireless_stats.discard.misc++;
365         printk(KERN_INFO "unifi: Dropping corrupt sniff packet\n");
366         dev_kfree_skb(skb);
367         return;
368     }
369
370 #if (UNIFI_SNIFF_ARPHRD == ARPHRD_IEEE80211_PRISM)
371     netrx_prism(priv, ind, skb);
372 #endif /* PRISM */
373
374 #if (UNIFI_SNIFF_ARPHRD == ARPHRD_IEEE80211_RADIOTAP)
375     netrx_radiotap(priv, ind, skb);
376 #endif /* RADIOTAP */
377
378     dev_kfree_skb(skb);
379
380 } /* ma_sniffdata_ind() */
381
382
383 #endif /* UNIFI_SNIFF_ARPHRD */
384