Merge commit '4cb38750d49010ae72e718d46605ac9ba5a851b4' into stable/for-linus-3.6
[firefly-linux-kernel-4.4.55.git] / drivers / staging / csr / csr_wifi_hip_ta_sampling.c
1 /*****************************************************************************
2
3             (c) Cambridge Silicon Radio Limited 2012
4             All rights reserved and confidential information of CSR
5
6             Refer to LICENSE.txt included with this source for details
7             on the license terms.
8
9 *****************************************************************************/
10
11 /*
12  * ---------------------------------------------------------------------------
13  *  FILE:     csr_wifi_hip_ta_sampling.c
14  *
15  *  PURPOSE:
16  *      The traffic analysis sampling module.
17  *      This gathers data which is sent to the SME and used to analyse
18  *      the traffic behaviour.
19  *
20  * Provides:
21  *      unifi_ta_sampling_init - Initialise the internal state
22  *      unifi_ta_sample        - Sampling function, call this for every data packet
23  *
24  * Calls these external functions which must be provided:
25  *      unifi_ta_indicate_sampling - Pass sample data to the SME.
26  *      unifi_ta_indicate_protocol - Report certain data packet types to the SME.
27  * ---------------------------------------------------------------------------
28  */
29
30 #include "csr_wifi_hip_card_sdio.h"
31
32 /* Maximum number of Tx frames we store each CYCLE_1, for detecting period */
33 #define TA_MAX_INTERVALS_IN_C1          100
34
35 /* Number of intervals in CYCLE_1 (one second), for detecting periodic */
36 /* Must match size of unifi_TrafficStats.intervals - 1 */
37 #define TA_INTERVALS_NUM               10
38
39 /* Step (in msecs) between intervals, for detecting periodic */
40 /* We are only interested in periods up to 100ms, i.e. between beacons */
41 /* This is correct for TA_INTERVALS_NUM=10 */
42 #define TA_INTERVALS_STEP               10
43
44
45 enum ta_frame_identity
46 {
47     TA_FRAME_UNKNOWN,
48     TA_FRAME_ETHERNET_UNINTERESTING,
49     TA_FRAME_ETHERNET_INTERESTING
50 };
51
52
53 #define TA_ETHERNET_TYPE_OFFSET     6
54 #define TA_LLC_HEADER_SIZE          8
55 #define TA_IP_TYPE_OFFSET           17
56 #define TA_UDP_SOURCE_PORT_OFFSET   28
57 #define TA_UDP_DEST_PORT_OFFSET     (TA_UDP_SOURCE_PORT_OFFSET + 2)
58 #define TA_BOOTP_CLIENT_MAC_ADDR_OFFSET 64
59 #define TA_DHCP_MESSAGE_TYPE_OFFSET 278
60 #define TA_DHCP_MESSAGE_TYPE_ACK    0x05
61 #define TA_PROTO_TYPE_IP            0x0800
62 #define TA_PROTO_TYPE_EAP           0x888E
63 #define TA_PROTO_TYPE_WAI           0x8864
64 #define TA_PROTO_TYPE_ARP           0x0806
65 #define TA_IP_TYPE_TCP              0x06
66 #define TA_IP_TYPE_UDP              0x11
67 #define TA_UDP_PORT_BOOTPC          0x0044
68 #define TA_UDP_PORT_BOOTPS          0x0043
69 #define TA_EAPOL_TYPE_OFFSET        9
70 #define TA_EAPOL_TYPE_START         0x01
71
72 #define snap_802_2                  0xAAAA0300
73 #define oui_rfc1042                 0x00000000
74 #define oui_8021h                   0x0000f800
75 static const u8 aironet_snap[5] = { 0x00, 0x40, 0x96, 0x00, 0x00 };
76
77
78 /*
79  * ---------------------------------------------------------------------------
80  *  ta_detect_protocol
81  *
82  *      Internal only.
83  *      Detects a specific protocol in a frame and indicates a TA event.
84  *
85  *  Arguments:
86  *      ta              The pointer to the TA module.
87  *      direction       The direction of the frame (tx or rx).
88  *      data            Pointer to the structure that contains the data.
89  *
90  *  Returns:
91  *      None
92  * ---------------------------------------------------------------------------
93  */
94 static enum ta_frame_identity ta_detect_protocol(card_t *card, CsrWifiRouterCtrlProtocolDirection direction,
95                                                  const bulk_data_desc_t *data,
96                                                  const u8 *saddr,
97                                                  const u8 *sta_macaddr)
98 {
99     ta_data_t *tad = &card->ta_sampling;
100     u16 proto;
101     u16 source_port, dest_port;
102     CsrWifiMacAddress srcAddress;
103     u32 snap_hdr, oui_hdr;
104
105     if (data->data_length < TA_LLC_HEADER_SIZE)
106     {
107         return TA_FRAME_UNKNOWN;
108     }
109
110     snap_hdr = (((u32)data->os_data_ptr[0]) << 24) |
111                (((u32)data->os_data_ptr[1]) << 16) |
112                (((u32)data->os_data_ptr[2]) << 8);
113     if (snap_hdr != snap_802_2)
114     {
115         return TA_FRAME_UNKNOWN;
116     }
117
118     if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_CUSTOM)
119     {
120         /*
121          * Here we would use the custom filter to detect interesting frames.
122          */
123     }
124
125     oui_hdr = (((u32)data->os_data_ptr[3]) << 24) |
126               (((u32)data->os_data_ptr[4]) << 16) |
127               (((u32)data->os_data_ptr[5]) << 8);
128     if ((oui_hdr == oui_rfc1042) || (oui_hdr == oui_8021h))
129     {
130         proto = (data->os_data_ptr[TA_ETHERNET_TYPE_OFFSET] * 256) +
131                 data->os_data_ptr[TA_ETHERNET_TYPE_OFFSET + 1];
132
133         /* The only interesting IP frames are the DHCP */
134         if (proto == TA_PROTO_TYPE_IP)
135         {
136             if (data->data_length > TA_IP_TYPE_OFFSET)
137             {
138                 if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_CUSTOM)
139                 {
140                     ta_l4stats_t *ta_l4stats = &tad->ta_l4stats;
141                     u8 l4proto = data->os_data_ptr[TA_IP_TYPE_OFFSET];
142
143                     if (l4proto == TA_IP_TYPE_TCP)
144                     {
145                         if (direction == CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_TX)
146                         {
147                             ta_l4stats->txTcpBytesCount += data->data_length;
148                         }
149                         else
150                         {
151                             ta_l4stats->rxTcpBytesCount += data->data_length;
152                         }
153                     }
154                     else if (l4proto == TA_IP_TYPE_UDP)
155                     {
156                         if (direction == CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_TX)
157                         {
158                             ta_l4stats->txUdpBytesCount += data->data_length;
159                         }
160                         else
161                         {
162                             ta_l4stats->rxUdpBytesCount += data->data_length;
163                         }
164                     }
165                 }
166
167                 /* detect DHCP frames */
168                 if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_DHCP)
169                 {
170                     /* DHCP frames are UDP frames with BOOTP ports */
171                     if (data->os_data_ptr[TA_IP_TYPE_OFFSET] == TA_IP_TYPE_UDP)
172                     {
173                         if (data->data_length > TA_UDP_DEST_PORT_OFFSET)
174                         {
175                             source_port = (data->os_data_ptr[TA_UDP_SOURCE_PORT_OFFSET] * 256) +
176                                           data->os_data_ptr[TA_UDP_SOURCE_PORT_OFFSET + 1];
177                             dest_port = (data->os_data_ptr[TA_UDP_DEST_PORT_OFFSET] * 256) +
178                                         data->os_data_ptr[TA_UDP_DEST_PORT_OFFSET + 1];
179
180                             if (((source_port == TA_UDP_PORT_BOOTPC) && (dest_port == TA_UDP_PORT_BOOTPS)) ||
181                                 ((source_port == TA_UDP_PORT_BOOTPS) && (dest_port == TA_UDP_PORT_BOOTPC)))
182                             {
183                                 /* The DHCP should have at least a message type (request, ack, nack, etc) */
184                                 if (data->data_length > TA_DHCP_MESSAGE_TYPE_OFFSET + 6)
185                                 {
186                                     UNIFI_MAC_ADDRESS_COPY(srcAddress.a, saddr);
187
188                                     if (direction == CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_TX)
189                                     {
190                                         unifi_ta_indicate_protocol(card->ospriv,
191                                                                    CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_DHCP,
192                                                                    direction,
193                                                                    &srcAddress);
194                                         return TA_FRAME_ETHERNET_UNINTERESTING;
195                                     }
196
197                                     /* DHCPACK is a special indication */
198                                     if (UNIFI_MAC_ADDRESS_CMP(data->os_data_ptr + TA_BOOTP_CLIENT_MAC_ADDR_OFFSET, sta_macaddr) == TRUE)
199                                     {
200                                         if (data->os_data_ptr[TA_DHCP_MESSAGE_TYPE_OFFSET] == TA_DHCP_MESSAGE_TYPE_ACK)
201                                         {
202                                             unifi_ta_indicate_protocol(card->ospriv,
203                                                                        CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_DHCP_ACK,
204                                                                        direction,
205                                                                        &srcAddress);
206                                         }
207                                         else
208                                         {
209                                             unifi_ta_indicate_protocol(card->ospriv,
210                                                                        CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_DHCP,
211                                                                        direction,
212                                                                        &srcAddress);
213                                         }
214                                     }
215                                 }
216                             }
217                         }
218                     }
219                 }
220             }
221
222             return TA_FRAME_ETHERNET_INTERESTING;
223         }
224
225         /* detect protocol type EAPOL or WAI (treated as equivalent here) */
226         if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_EAPOL)
227         {
228             if (TA_PROTO_TYPE_EAP == proto || TA_PROTO_TYPE_WAI == proto)
229             {
230                 if ((TA_PROTO_TYPE_WAI == proto) || (direction != CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_TX) ||
231                     (data->os_data_ptr[TA_EAPOL_TYPE_OFFSET] == TA_EAPOL_TYPE_START))
232                 {
233                     UNIFI_MAC_ADDRESS_COPY(srcAddress.a, saddr);
234                     unifi_ta_indicate_protocol(card->ospriv,
235                                                CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_EAPOL,
236                                                direction, &srcAddress);
237                 }
238                 return TA_FRAME_ETHERNET_UNINTERESTING;
239             }
240         }
241
242         /* detect protocol type 0x0806 (ARP) */
243         if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_ARP)
244         {
245             if (proto == TA_PROTO_TYPE_ARP)
246             {
247                 UNIFI_MAC_ADDRESS_COPY(srcAddress.a, saddr);
248                 unifi_ta_indicate_protocol(card->ospriv,
249                                            CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_ARP,
250                                            direction, &srcAddress);
251                 return TA_FRAME_ETHERNET_UNINTERESTING;
252             }
253         }
254
255         return TA_FRAME_ETHERNET_INTERESTING;
256     }
257     else if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_AIRONET)
258     {
259         /* detect Aironet frames */
260         if (!memcmp(data->os_data_ptr + 3, aironet_snap, 5))
261         {
262             UNIFI_MAC_ADDRESS_COPY(srcAddress.a, saddr);
263             unifi_ta_indicate_protocol(card->ospriv, CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_AIRONET,
264                                        direction, &srcAddress);
265         }
266     }
267
268     return TA_FRAME_ETHERNET_UNINTERESTING;
269 } /* ta_detect_protocol() */
270
271
272 static void tas_reset_data(ta_data_t *tad)
273 {
274     s16 i;
275
276     for (i = 0; i < (TA_INTERVALS_NUM + 1); i++)
277     {
278         tad->stats.intervals[i] = 0;
279     }
280
281     tad->stats.rxFramesNum = 0;
282     tad->stats.txFramesNum = 0;
283     tad->stats.rxBytesCount = 0;
284     tad->stats.txBytesCount = 0;
285     tad->stats.rxMeanRate = 0;
286
287     tad->rx_sum_rate = 0;
288
289     tad->ta_l4stats.rxTcpBytesCount = 0;
290     tad->ta_l4stats.txTcpBytesCount = 0;
291     tad->ta_l4stats.rxUdpBytesCount = 0;
292     tad->ta_l4stats.txUdpBytesCount = 0;
293 } /* tas_reset_data() */
294
295
296 /*
297  * ---------------------------------------------------------------------------
298  *  API.
299  *  unifi_ta_sampling_init
300  *
301  *      (Re)Initialise the Traffic Analysis sampling module.
302  *      Resets the counters and timestamps.
303  *
304  *  Arguments:
305  *      tad             Pointer to a ta_data_t structure containing the
306  *                      context for this device instance.
307  *      drv_priv        An opaque pointer that the TA sampling module will
308  *                      pass in call-outs.
309  *
310  *  Returns:
311  *      None.
312  * ---------------------------------------------------------------------------
313  */
314 void unifi_ta_sampling_init(card_t *card)
315 {
316     (void)unifi_ta_configure(card, CSR_WIFI_ROUTER_CTRL_TRAFFIC_CONFIG_TYPE_RESET, NULL);
317
318     card->ta_sampling.packet_filter = CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_NONE;
319     card->ta_sampling.traffic_type = CSR_WIFI_ROUTER_CTRL_TRAFFIC_TYPE_OCCASIONAL;
320 } /* unifi_ta_sampling_init() */
321
322
323 /*
324  * ---------------------------------------------------------------------------
325  *  API.
326  *  unifi_ta_sample
327  *
328  *      Sample a data frame for the TA module.
329  *      This function stores all the useful information it can extract from
330  *      the frame and detects any specific protocols.
331  *
332  *  Arguments:
333  *      tad             The pointer to the TA sampling context struct.
334  *      direction       The direction of the frame (rx, tx)
335  *      data            Pointer to the frame data
336  *      saddr           Source MAC address of frame.
337  *      timestamp       Time (in msecs) that the frame was received.
338  *      rate            Reported data rate for the rx frame (0 for tx frames)
339  *
340  *  Returns:
341  *      None
342  * ---------------------------------------------------------------------------
343  */
344 void unifi_ta_sample(card_t                            *card,
345                      CsrWifiRouterCtrlProtocolDirection direction,
346                      const bulk_data_desc_t            *data,
347                      const u8                    *saddr,
348                      const u8                    *sta_macaddr,
349                      u32                          timestamp,
350                      u16                          rate)
351 {
352     ta_data_t *tad = &card->ta_sampling;
353     enum ta_frame_identity identity;
354     u32 time_delta;
355
356
357
358     /* Step1: Check for specific frames */
359     if (tad->packet_filter != CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_NONE)
360     {
361         identity = ta_detect_protocol(card, direction, data, saddr, sta_macaddr);
362     }
363     else
364     {
365         identity = TA_FRAME_ETHERNET_INTERESTING;
366     }
367
368
369     /* Step2: Update the information in the current record */
370     if (direction == CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_RX)
371     {
372         /* Update the Rx packet count and the throughput count */
373         tad->stats.rxFramesNum++;
374         tad->stats.rxBytesCount += data->data_length;
375
376         /* Accumulate packet Rx rates for later averaging */
377         tad->rx_sum_rate += rate;
378     }
379     else
380     {
381         if (identity == TA_FRAME_ETHERNET_INTERESTING)
382         {
383             /*
384              * Store the period between the last and the current frame.
385              * There is not point storing more than TA_MAX_INTERVALS_IN_C1 periods,
386              * the traffic will be bursty or continuous.
387              */
388             if (tad->stats.txFramesNum < TA_MAX_INTERVALS_IN_C1)
389             {
390                 u32 interval;
391                 u32 index_in_intervals;
392
393                 interval = timestamp - tad->tx_last_ts;
394                 tad->tx_last_ts = timestamp;
395                 index_in_intervals = (interval + TA_INTERVALS_STEP / 2 - 1) / TA_INTERVALS_STEP;
396
397                 /* If the interval is interesting, update the t1_intervals count */
398                 if (index_in_intervals <= TA_INTERVALS_NUM)
399                 {
400                     unifi_trace(card->ospriv, UDBG5,
401                                 "unifi_ta_sample: TX interval=%d index=%d\n",
402                                 interval, index_in_intervals);
403                     tad->stats.intervals[index_in_intervals]++;
404                 }
405             }
406         }
407
408         /* Update the Tx packet count... */
409         tad->stats.txFramesNum++;
410         /* ... and the number of bytes for throughput. */
411         tad->stats.txBytesCount += data->data_length;
412     }
413
414     /*
415      * If more than one second has elapsed since the last report, send
416      * another one.
417      */
418     /* Unsigned subtraction handles wrap-around from 0xFFFFFFFF to 0 */
419     time_delta = timestamp - tad->last_indication_time;
420     if (time_delta >= 1000)
421     {
422         /*
423          * rxFramesNum can be flashed in tas_reset_data() by another thread.
424          * Use a temp to avoid division by zero.
425          */
426         u32 temp_rxFramesNum;
427         temp_rxFramesNum = tad->stats.rxFramesNum;
428
429         /* Calculate this interval's mean frame Rx rate from the sum */
430         if (temp_rxFramesNum)
431         {
432             tad->stats.rxMeanRate = tad->rx_sum_rate / temp_rxFramesNum;
433         }
434         unifi_trace(card->ospriv, UDBG5,
435                     "unifi_ta_sample: RX fr=%lu, r=%u, sum=%lu, av=%lu\n",
436                     tad->stats.rxFramesNum, rate,
437                     tad->rx_sum_rate, tad->stats.rxMeanRate);
438
439         /*
440          * Send the information collected in the stats struct
441          * to the SME and reset the counters.
442          */
443         if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_CUSTOM)
444         {
445             u32 rxTcpThroughput = tad->ta_l4stats.rxTcpBytesCount / time_delta;
446             u32 txTcpThroughput = tad->ta_l4stats.txTcpBytesCount / time_delta;
447             u32 rxUdpThroughput = tad->ta_l4stats.rxUdpBytesCount / time_delta;
448             u32 txUdpThroughput = tad->ta_l4stats.txUdpBytesCount / time_delta;
449
450             unifi_ta_indicate_l4stats(card->ospriv,
451                                       rxTcpThroughput,
452                                       txTcpThroughput,
453                                       rxUdpThroughput,
454                                       txUdpThroughput
455                                       );
456         }
457         unifi_ta_indicate_sampling(card->ospriv, &tad->stats);
458         tas_reset_data(tad);
459         tad->last_indication_time = timestamp;
460     }
461 } /* unifi_ta_sample() */
462
463
464 /*
465  * ---------------------------------------------------------------------------
466  *  External API.
467  *  unifi_ta_configure
468  *
469  *      Configures the TA module parameters.
470  *
471  *  Arguments:
472  *      ta              The pointer to the TA module.
473  *      config_type     The type of the configuration request
474  *      config          Pointer to the configuration parameters.
475  *
476  *  Returns:
477  *      CSR_RESULT_SUCCESS on success, CSR error code otherwise
478  * ---------------------------------------------------------------------------
479  */
480 CsrResult unifi_ta_configure(card_t                               *card,
481                              CsrWifiRouterCtrlTrafficConfigType    config_type,
482                              const CsrWifiRouterCtrlTrafficConfig *config)
483 {
484     ta_data_t *tad = &card->ta_sampling;
485
486     /* Reinitialise our data when we are reset */
487     if (config_type == CSR_WIFI_ROUTER_CTRL_TRAFFIC_CONFIG_TYPE_RESET)
488     {
489         /* Reset the stats to zero */
490         tas_reset_data(tad);
491
492         /* Reset the timer variables */
493         tad->tx_last_ts = 0;
494         tad->last_indication_time = 0;
495
496         return CSR_RESULT_SUCCESS;
497     }
498
499     if (config_type == CSR_WIFI_ROUTER_CTRL_TRAFFIC_CONFIG_TYPE_FILTER)
500     {
501         tad->packet_filter = config->packetFilter;
502
503         if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_CUSTOM)
504         {
505             tad->custom_filter = config->customFilter;
506         }
507
508         return CSR_RESULT_SUCCESS;
509     }
510
511     return CSR_RESULT_SUCCESS;
512 } /* unifi_ta_configure() */
513
514
515 /*
516  * ---------------------------------------------------------------------------
517  *  External API.
518  *  unifi_ta_classification
519  *
520  *      Configures the current TA classification.
521  *
522  *  Arguments:
523  *      ta              The pointer to the TA module.
524  *      traffic_type    The classification type
525  *      period          The traffic period if the type is periodic
526  *
527  *  Returns:
528  *      None
529  * ---------------------------------------------------------------------------
530  */
531 void unifi_ta_classification(card_t                      *card,
532                              CsrWifiRouterCtrlTrafficType traffic_type,
533                              u16                    period)
534 {
535     unifi_trace(card->ospriv, UDBG3,
536                 "Changed current ta classification to: %d\n", traffic_type);
537
538     card->ta_sampling.traffic_type = traffic_type;
539 }
540
541