summaryrefslogtreecommitdiffstats
path: root/drivers/staging/csr/csr_wifi_hip_ta_sampling.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/csr/csr_wifi_hip_ta_sampling.c')
-rw-r--r--drivers/staging/csr/csr_wifi_hip_ta_sampling.c541
1 files changed, 541 insertions, 0 deletions
diff --git a/drivers/staging/csr/csr_wifi_hip_ta_sampling.c b/drivers/staging/csr/csr_wifi_hip_ta_sampling.c
new file mode 100644
index 000000000000..f1df36aa87e7
--- /dev/null
+++ b/drivers/staging/csr/csr_wifi_hip_ta_sampling.c
@@ -0,0 +1,541 @@
+/*****************************************************************************
+
+ (c) Cambridge Silicon Radio Limited 2012
+ All rights reserved and confidential information of CSR
+
+ Refer to LICENSE.txt included with this source for details
+ on the license terms.
+
+*****************************************************************************/
+
+/*
+ * ---------------------------------------------------------------------------
+ * FILE: csr_wifi_hip_ta_sampling.c
+ *
+ * PURPOSE:
+ * The traffic analysis sampling module.
+ * This gathers data which is sent to the SME and used to analyse
+ * the traffic behaviour.
+ *
+ * Provides:
+ * unifi_ta_sampling_init - Initialise the internal state
+ * unifi_ta_sample - Sampling function, call this for every data packet
+ *
+ * Calls these external functions which must be provided:
+ * unifi_ta_indicate_sampling - Pass sample data to the SME.
+ * unifi_ta_indicate_protocol - Report certain data packet types to the SME.
+ * ---------------------------------------------------------------------------
+ */
+
+#include "csr_wifi_hip_card_sdio.h"
+
+/* Maximum number of Tx frames we store each CYCLE_1, for detecting period */
+#define TA_MAX_INTERVALS_IN_C1 100
+
+/* Number of intervals in CYCLE_1 (one second), for detecting periodic */
+/* Must match size of unifi_TrafficStats.intervals - 1 */
+#define TA_INTERVALS_NUM 10
+
+/* Step (in msecs) between intervals, for detecting periodic */
+/* We are only interested in periods up to 100ms, i.e. between beacons */
+/* This is correct for TA_INTERVALS_NUM=10 */
+#define TA_INTERVALS_STEP 10
+
+
+enum ta_frame_identity
+{
+ TA_FRAME_UNKNOWN,
+ TA_FRAME_ETHERNET_UNINTERESTING,
+ TA_FRAME_ETHERNET_INTERESTING
+};
+
+
+#define TA_ETHERNET_TYPE_OFFSET 6
+#define TA_LLC_HEADER_SIZE 8
+#define TA_IP_TYPE_OFFSET 17
+#define TA_UDP_SOURCE_PORT_OFFSET 28
+#define TA_UDP_DEST_PORT_OFFSET (TA_UDP_SOURCE_PORT_OFFSET + 2)
+#define TA_BOOTP_CLIENT_MAC_ADDR_OFFSET 64
+#define TA_DHCP_MESSAGE_TYPE_OFFSET 278
+#define TA_DHCP_MESSAGE_TYPE_ACK 0x05
+#define TA_PROTO_TYPE_IP 0x0800
+#define TA_PROTO_TYPE_EAP 0x888E
+#define TA_PROTO_TYPE_WAI 0x8864
+#define TA_PROTO_TYPE_ARP 0x0806
+#define TA_IP_TYPE_TCP 0x06
+#define TA_IP_TYPE_UDP 0x11
+#define TA_UDP_PORT_BOOTPC 0x0044
+#define TA_UDP_PORT_BOOTPS 0x0043
+#define TA_EAPOL_TYPE_OFFSET 9
+#define TA_EAPOL_TYPE_START 0x01
+
+#define snap_802_2 0xAAAA0300
+#define oui_rfc1042 0x00000000
+#define oui_8021h 0x0000f800
+static const u8 aironet_snap[5] = { 0x00, 0x40, 0x96, 0x00, 0x00 };
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * ta_detect_protocol
+ *
+ * Internal only.
+ * Detects a specific protocol in a frame and indicates a TA event.
+ *
+ * Arguments:
+ * ta The pointer to the TA module.
+ * direction The direction of the frame (tx or rx).
+ * data Pointer to the structure that contains the data.
+ *
+ * Returns:
+ * None
+ * ---------------------------------------------------------------------------
+ */
+static enum ta_frame_identity ta_detect_protocol(card_t *card, CsrWifiRouterCtrlProtocolDirection direction,
+ const bulk_data_desc_t *data,
+ const u8 *saddr,
+ const u8 *sta_macaddr)
+{
+ ta_data_t *tad = &card->ta_sampling;
+ u16 proto;
+ u16 source_port, dest_port;
+ CsrWifiMacAddress srcAddress;
+ u32 snap_hdr, oui_hdr;
+
+ if (data->data_length < TA_LLC_HEADER_SIZE)
+ {
+ return TA_FRAME_UNKNOWN;
+ }
+
+ snap_hdr = (((u32)data->os_data_ptr[0]) << 24) |
+ (((u32)data->os_data_ptr[1]) << 16) |
+ (((u32)data->os_data_ptr[2]) << 8);
+ if (snap_hdr != snap_802_2)
+ {
+ return TA_FRAME_UNKNOWN;
+ }
+
+ if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_CUSTOM)
+ {
+ /*
+ * Here we would use the custom filter to detect interesting frames.
+ */
+ }
+
+ oui_hdr = (((u32)data->os_data_ptr[3]) << 24) |
+ (((u32)data->os_data_ptr[4]) << 16) |
+ (((u32)data->os_data_ptr[5]) << 8);
+ if ((oui_hdr == oui_rfc1042) || (oui_hdr == oui_8021h))
+ {
+ proto = (data->os_data_ptr[TA_ETHERNET_TYPE_OFFSET] * 256) +
+ data->os_data_ptr[TA_ETHERNET_TYPE_OFFSET + 1];
+
+ /* The only interesting IP frames are the DHCP */
+ if (proto == TA_PROTO_TYPE_IP)
+ {
+ if (data->data_length > TA_IP_TYPE_OFFSET)
+ {
+ if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_CUSTOM)
+ {
+ ta_l4stats_t *ta_l4stats = &tad->ta_l4stats;
+ u8 l4proto = data->os_data_ptr[TA_IP_TYPE_OFFSET];
+
+ if (l4proto == TA_IP_TYPE_TCP)
+ {
+ if (direction == CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_TX)
+ {
+ ta_l4stats->txTcpBytesCount += data->data_length;
+ }
+ else
+ {
+ ta_l4stats->rxTcpBytesCount += data->data_length;
+ }
+ }
+ else if (l4proto == TA_IP_TYPE_UDP)
+ {
+ if (direction == CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_TX)
+ {
+ ta_l4stats->txUdpBytesCount += data->data_length;
+ }
+ else
+ {
+ ta_l4stats->rxUdpBytesCount += data->data_length;
+ }
+ }
+ }
+
+ /* detect DHCP frames */
+ if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_DHCP)
+ {
+ /* DHCP frames are UDP frames with BOOTP ports */
+ if (data->os_data_ptr[TA_IP_TYPE_OFFSET] == TA_IP_TYPE_UDP)
+ {
+ if (data->data_length > TA_UDP_DEST_PORT_OFFSET)
+ {
+ source_port = (data->os_data_ptr[TA_UDP_SOURCE_PORT_OFFSET] * 256) +
+ data->os_data_ptr[TA_UDP_SOURCE_PORT_OFFSET + 1];
+ dest_port = (data->os_data_ptr[TA_UDP_DEST_PORT_OFFSET] * 256) +
+ data->os_data_ptr[TA_UDP_DEST_PORT_OFFSET + 1];
+
+ if (((source_port == TA_UDP_PORT_BOOTPC) && (dest_port == TA_UDP_PORT_BOOTPS)) ||
+ ((source_port == TA_UDP_PORT_BOOTPS) && (dest_port == TA_UDP_PORT_BOOTPC)))
+ {
+ /* The DHCP should have at least a message type (request, ack, nack, etc) */
+ if (data->data_length > TA_DHCP_MESSAGE_TYPE_OFFSET + 6)
+ {
+ UNIFI_MAC_ADDRESS_COPY(srcAddress.a, saddr);
+
+ if (direction == CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_TX)
+ {
+ unifi_ta_indicate_protocol(card->ospriv,
+ CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_DHCP,
+ direction,
+ &srcAddress);
+ return TA_FRAME_ETHERNET_UNINTERESTING;
+ }
+
+ /* DHCPACK is a special indication */
+ if (UNIFI_MAC_ADDRESS_CMP(data->os_data_ptr + TA_BOOTP_CLIENT_MAC_ADDR_OFFSET, sta_macaddr) == TRUE)
+ {
+ if (data->os_data_ptr[TA_DHCP_MESSAGE_TYPE_OFFSET] == TA_DHCP_MESSAGE_TYPE_ACK)
+ {
+ unifi_ta_indicate_protocol(card->ospriv,
+ CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_DHCP_ACK,
+ direction,
+ &srcAddress);
+ }
+ else
+ {
+ unifi_ta_indicate_protocol(card->ospriv,
+ CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_DHCP,
+ direction,
+ &srcAddress);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return TA_FRAME_ETHERNET_INTERESTING;
+ }
+
+ /* detect protocol type EAPOL or WAI (treated as equivalent here) */
+ if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_EAPOL)
+ {
+ if (TA_PROTO_TYPE_EAP == proto || TA_PROTO_TYPE_WAI == proto)
+ {
+ if ((TA_PROTO_TYPE_WAI == proto) || (direction != CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_TX) ||
+ (data->os_data_ptr[TA_EAPOL_TYPE_OFFSET] == TA_EAPOL_TYPE_START))
+ {
+ UNIFI_MAC_ADDRESS_COPY(srcAddress.a, saddr);
+ unifi_ta_indicate_protocol(card->ospriv,
+ CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_EAPOL,
+ direction, &srcAddress);
+ }
+ return TA_FRAME_ETHERNET_UNINTERESTING;
+ }
+ }
+
+ /* detect protocol type 0x0806 (ARP) */
+ if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_ARP)
+ {
+ if (proto == TA_PROTO_TYPE_ARP)
+ {
+ UNIFI_MAC_ADDRESS_COPY(srcAddress.a, saddr);
+ unifi_ta_indicate_protocol(card->ospriv,
+ CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_ARP,
+ direction, &srcAddress);
+ return TA_FRAME_ETHERNET_UNINTERESTING;
+ }
+ }
+
+ return TA_FRAME_ETHERNET_INTERESTING;
+ }
+ else if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_AIRONET)
+ {
+ /* detect Aironet frames */
+ if (!memcmp(data->os_data_ptr + 3, aironet_snap, 5))
+ {
+ UNIFI_MAC_ADDRESS_COPY(srcAddress.a, saddr);
+ unifi_ta_indicate_protocol(card->ospriv, CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_AIRONET,
+ direction, &srcAddress);
+ }
+ }
+
+ return TA_FRAME_ETHERNET_UNINTERESTING;
+} /* ta_detect_protocol() */
+
+
+static void tas_reset_data(ta_data_t *tad)
+{
+ s16 i;
+
+ for (i = 0; i < (TA_INTERVALS_NUM + 1); i++)
+ {
+ tad->stats.intervals[i] = 0;
+ }
+
+ tad->stats.rxFramesNum = 0;
+ tad->stats.txFramesNum = 0;
+ tad->stats.rxBytesCount = 0;
+ tad->stats.txBytesCount = 0;
+ tad->stats.rxMeanRate = 0;
+
+ tad->rx_sum_rate = 0;
+
+ tad->ta_l4stats.rxTcpBytesCount = 0;
+ tad->ta_l4stats.txTcpBytesCount = 0;
+ tad->ta_l4stats.rxUdpBytesCount = 0;
+ tad->ta_l4stats.txUdpBytesCount = 0;
+} /* tas_reset_data() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * API.
+ * unifi_ta_sampling_init
+ *
+ * (Re)Initialise the Traffic Analysis sampling module.
+ * Resets the counters and timestamps.
+ *
+ * Arguments:
+ * tad Pointer to a ta_data_t structure containing the
+ * context for this device instance.
+ * drv_priv An opaque pointer that the TA sampling module will
+ * pass in call-outs.
+ *
+ * Returns:
+ * None.
+ * ---------------------------------------------------------------------------
+ */
+void unifi_ta_sampling_init(card_t *card)
+{
+ (void)unifi_ta_configure(card, CSR_WIFI_ROUTER_CTRL_TRAFFIC_CONFIG_TYPE_RESET, NULL);
+
+ card->ta_sampling.packet_filter = CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_NONE;
+ card->ta_sampling.traffic_type = CSR_WIFI_ROUTER_CTRL_TRAFFIC_TYPE_OCCASIONAL;
+} /* unifi_ta_sampling_init() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * API.
+ * unifi_ta_sample
+ *
+ * Sample a data frame for the TA module.
+ * This function stores all the useful information it can extract from
+ * the frame and detects any specific protocols.
+ *
+ * Arguments:
+ * tad The pointer to the TA sampling context struct.
+ * direction The direction of the frame (rx, tx)
+ * data Pointer to the frame data
+ * saddr Source MAC address of frame.
+ * timestamp Time (in msecs) that the frame was received.
+ * rate Reported data rate for the rx frame (0 for tx frames)
+ *
+ * Returns:
+ * None
+ * ---------------------------------------------------------------------------
+ */
+void unifi_ta_sample(card_t *card,
+ CsrWifiRouterCtrlProtocolDirection direction,
+ const bulk_data_desc_t *data,
+ const u8 *saddr,
+ const u8 *sta_macaddr,
+ u32 timestamp,
+ u16 rate)
+{
+ ta_data_t *tad = &card->ta_sampling;
+ enum ta_frame_identity identity;
+ u32 time_delta;
+
+
+
+ /* Step1: Check for specific frames */
+ if (tad->packet_filter != CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_NONE)
+ {
+ identity = ta_detect_protocol(card, direction, data, saddr, sta_macaddr);
+ }
+ else
+ {
+ identity = TA_FRAME_ETHERNET_INTERESTING;
+ }
+
+
+ /* Step2: Update the information in the current record */
+ if (direction == CSR_WIFI_ROUTER_CTRL_PROTOCOL_DIRECTION_RX)
+ {
+ /* Update the Rx packet count and the throughput count */
+ tad->stats.rxFramesNum++;
+ tad->stats.rxBytesCount += data->data_length;
+
+ /* Accumulate packet Rx rates for later averaging */
+ tad->rx_sum_rate += rate;
+ }
+ else
+ {
+ if (identity == TA_FRAME_ETHERNET_INTERESTING)
+ {
+ /*
+ * Store the period between the last and the current frame.
+ * There is not point storing more than TA_MAX_INTERVALS_IN_C1 periods,
+ * the traffic will be bursty or continuous.
+ */
+ if (tad->stats.txFramesNum < TA_MAX_INTERVALS_IN_C1)
+ {
+ u32 interval;
+ u32 index_in_intervals;
+
+ interval = timestamp - tad->tx_last_ts;
+ tad->tx_last_ts = timestamp;
+ index_in_intervals = (interval + TA_INTERVALS_STEP / 2 - 1) / TA_INTERVALS_STEP;
+
+ /* If the interval is interesting, update the t1_intervals count */
+ if (index_in_intervals <= TA_INTERVALS_NUM)
+ {
+ unifi_trace(card->ospriv, UDBG5,
+ "unifi_ta_sample: TX interval=%d index=%d\n",
+ interval, index_in_intervals);
+ tad->stats.intervals[index_in_intervals]++;
+ }
+ }
+ }
+
+ /* Update the Tx packet count... */
+ tad->stats.txFramesNum++;
+ /* ... and the number of bytes for throughput. */
+ tad->stats.txBytesCount += data->data_length;
+ }
+
+ /*
+ * If more than one second has elapsed since the last report, send
+ * another one.
+ */
+ /* Unsigned subtraction handles wrap-around from 0xFFFFFFFF to 0 */
+ time_delta = timestamp - tad->last_indication_time;
+ if (time_delta >= 1000)
+ {
+ /*
+ * rxFramesNum can be flashed in tas_reset_data() by another thread.
+ * Use a temp to avoid division by zero.
+ */
+ u32 temp_rxFramesNum;
+ temp_rxFramesNum = tad->stats.rxFramesNum;
+
+ /* Calculate this interval's mean frame Rx rate from the sum */
+ if (temp_rxFramesNum)
+ {
+ tad->stats.rxMeanRate = tad->rx_sum_rate / temp_rxFramesNum;
+ }
+ unifi_trace(card->ospriv, UDBG5,
+ "unifi_ta_sample: RX fr=%lu, r=%u, sum=%lu, av=%lu\n",
+ tad->stats.rxFramesNum, rate,
+ tad->rx_sum_rate, tad->stats.rxMeanRate);
+
+ /*
+ * Send the information collected in the stats struct
+ * to the SME and reset the counters.
+ */
+ if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_CUSTOM)
+ {
+ u32 rxTcpThroughput = tad->ta_l4stats.rxTcpBytesCount / time_delta;
+ u32 txTcpThroughput = tad->ta_l4stats.txTcpBytesCount / time_delta;
+ u32 rxUdpThroughput = tad->ta_l4stats.rxUdpBytesCount / time_delta;
+ u32 txUdpThroughput = tad->ta_l4stats.txUdpBytesCount / time_delta;
+
+ unifi_ta_indicate_l4stats(card->ospriv,
+ rxTcpThroughput,
+ txTcpThroughput,
+ rxUdpThroughput,
+ txUdpThroughput
+ );
+ }
+ unifi_ta_indicate_sampling(card->ospriv, &tad->stats);
+ tas_reset_data(tad);
+ tad->last_indication_time = timestamp;
+ }
+} /* unifi_ta_sample() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * External API.
+ * unifi_ta_configure
+ *
+ * Configures the TA module parameters.
+ *
+ * Arguments:
+ * ta The pointer to the TA module.
+ * config_type The type of the configuration request
+ * config Pointer to the configuration parameters.
+ *
+ * Returns:
+ * CSR_RESULT_SUCCESS on success, CSR error code otherwise
+ * ---------------------------------------------------------------------------
+ */
+CsrResult unifi_ta_configure(card_t *card,
+ CsrWifiRouterCtrlTrafficConfigType config_type,
+ const CsrWifiRouterCtrlTrafficConfig *config)
+{
+ ta_data_t *tad = &card->ta_sampling;
+
+ /* Reinitialise our data when we are reset */
+ if (config_type == CSR_WIFI_ROUTER_CTRL_TRAFFIC_CONFIG_TYPE_RESET)
+ {
+ /* Reset the stats to zero */
+ tas_reset_data(tad);
+
+ /* Reset the timer variables */
+ tad->tx_last_ts = 0;
+ tad->last_indication_time = 0;
+
+ return CSR_RESULT_SUCCESS;
+ }
+
+ if (config_type == CSR_WIFI_ROUTER_CTRL_TRAFFIC_CONFIG_TYPE_FILTER)
+ {
+ tad->packet_filter = config->packetFilter;
+
+ if (tad->packet_filter & CSR_WIFI_ROUTER_CTRL_TRAFFIC_PACKET_TYPE_CUSTOM)
+ {
+ tad->custom_filter = config->customFilter;
+ }
+
+ return CSR_RESULT_SUCCESS;
+ }
+
+ return CSR_RESULT_SUCCESS;
+} /* unifi_ta_configure() */
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * External API.
+ * unifi_ta_classification
+ *
+ * Configures the current TA classification.
+ *
+ * Arguments:
+ * ta The pointer to the TA module.
+ * traffic_type The classification type
+ * period The traffic period if the type is periodic
+ *
+ * Returns:
+ * None
+ * ---------------------------------------------------------------------------
+ */
+void unifi_ta_classification(card_t *card,
+ CsrWifiRouterCtrlTrafficType traffic_type,
+ u16 period)
+{
+ unifi_trace(card->ospriv, UDBG3,
+ "Changed current ta classification to: %d\n", traffic_type);
+
+ card->ta_sampling.traffic_type = traffic_type;
+}
+
+