diff options
Diffstat (limited to 'drivers/staging/csr/csr_wifi_hip_ta_sampling.c')
-rw-r--r-- | drivers/staging/csr/csr_wifi_hip_ta_sampling.c | 541 |
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; +} + + |