blob: e6ed59c82746727c1fd30f14e7afd2a4aefac288 [file] [log] [blame]
/*
* Copyright (c) 2012-2014, 2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/**=========================================================================
\file vos_packet.c
\brief virtual Operating System Services (vOSS) network Packet APIs
Network Protocol packet/buffer support interfaces
========================================================================*/
/* $Header$ */
/*--------------------------------------------------------------------------
Include Files
------------------------------------------------------------------------*/
#include <vos_packet.h>
#include <i_vos_packet.h>
#include <vos_timer.h>
#include <vos_trace.h>
#include <wlan_hdd_main.h>
#include "adf_nbuf.h"
#include "vos_memory.h"
#include "adf_os_mem.h"
#include <linux/rtc.h>
/* Protocol specific packet tracking feature */
#define VOS_PKT_TRAC_ETH_TYPE_OFFSET 12
#define VOS_PKT_TRAC_IP_OFFSET 14
#define VOS_PKT_TRAC_IP_HEADER_SIZE 20
#define VOS_PKT_TRAC_DHCP_SRV_PORT 67
#define VOS_PKT_TRAC_DHCP_CLI_PORT 68
#define VOS_PKT_TRAC_EAPOL_ETH_TYPE 0x888E
#define VOS_PKT_TRAC_ARP_ETH_TYPE 0x0806
#ifdef QCA_PKT_PROTO_TRACE
#define VOS_PKT_TRAC_MAX_STRING_LEN 40
#define VOS_PKT_TRAC_MAX_TRACE_BUF 50
#define VOS_PKT_TRAC_MAX_STRING_BUF 64
/* protocol Storage Structure */
typedef struct
{
v_U32_t order;
v_TIME_t event_sec_time;
v_TIME_t event_msec_time;
char event_string[VOS_PKT_TRAC_MAX_STRING_LEN];
} vos_pkt_proto_trace_t;
vos_pkt_proto_trace_t *trace_buffer = NULL;
unsigned int trace_buffer_order = 0;
unsigned int trace_dump_order = 0;
spinlock_t trace_buffer_lock;
#endif /* QCA_PKT_PROTO_TRACE */
/**
* vos_pkt_return_packet Free the voss Packet
* @ vos Packet
*/
VOS_STATUS vos_pkt_return_packet(vos_pkt_t *packet)
{
// Validate the input parameter pointer
if (unlikely(packet == NULL)) {
return VOS_STATUS_E_INVAL;
}
/* Free up the Adf nbuf */
adf_nbuf_free(packet->pkt_buf);
packet->pkt_buf = NULL;
/* Free up the Rx packet */
vos_mem_free(packet);
return VOS_STATUS_SUCCESS;
}
/**--------------------------------------------------------------------------
\brief vos_pkt_get_packet_length() - Get packet length for a voss Packet
This API returns the total length of the data in a voss Packet.
\param pPacket - the voss Packet to get the packet length from.
\param pPacketSize - location to return the total size of the data contained
in the voss Packet.
\return
\sa
---------------------------------------------------------------------------*/
VOS_STATUS vos_pkt_get_packet_length( vos_pkt_t *pPacket,
v_U16_t *pPacketSize )
{
// Validate the parameter pointers
if (unlikely((pPacket == NULL) || (pPacketSize == NULL)) ||
(pPacket->pkt_buf == NULL))
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
"VPKT [%d]: NULL pointer", __LINE__);
return VOS_STATUS_E_INVAL;
}
// return the requested information
*pPacketSize = adf_nbuf_len(pPacket->pkt_buf);
return VOS_STATUS_SUCCESS;
}
/*
* TODO: Remove below later since all the below
* definitions are not required for Host
* driver 2.0 (still references from HDD and
* other layers are yet to be removed)
*/
VOS_STATUS vos_pkt_get_available_buffer_pool (VOS_PKT_TYPE pktType,
v_SIZE_t *vosFreeBuffer)
{
return VOS_STATUS_SUCCESS;
}
VOS_STATUS vos_pkt_get_os_packet( vos_pkt_t *pPacket,
v_VOID_t **ppOSPacket,
v_BOOL_t clearOSPacket )
{
return VOS_STATUS_SUCCESS;
}
VOS_STATUS vos_pkt_wrap_data_packet( vos_pkt_t **ppPacket,
VOS_PKT_TYPE pktType,
v_VOID_t *pOSPacket,
vos_pkt_get_packet_callback callback,
v_VOID_t *userData )
{
return VOS_STATUS_SUCCESS;
}
VOS_STATUS vos_pkt_set_os_packet( vos_pkt_t *pPacket,
v_VOID_t *pOSPacket )
{
return VOS_STATUS_SUCCESS;
}
VOS_STATUS vos_pkt_get_timestamp( vos_pkt_t *pPacket,
v_TIME_t* pTstamp )
{
return VOS_STATUS_SUCCESS;
}
VOS_STATUS vos_pkt_walk_packet_chain( vos_pkt_t *pPacket,
vos_pkt_t **ppChainedPacket,
v_BOOL_t unchainPacket )
{
return VOS_STATUS_SUCCESS;
}
VOS_STATUS vos_pkt_peek_data( vos_pkt_t *pPacket,
v_SIZE_t pktOffset,
v_VOID_t **ppPacketData,
v_SIZE_t numBytes )
{
return VOS_STATUS_SUCCESS;
}
VOS_STATUS vos_pkt_get_packet( vos_pkt_t **ppPacket,
VOS_PKT_TYPE pktType,
v_SIZE_t dataSize,
v_SIZE_t numPackets,
v_BOOL_t zeroBuffer,
vos_pkt_get_packet_callback callback,
v_VOID_t *userData )
{
return VOS_STATUS_SUCCESS;
}
VOS_STATUS vos_pkt_reserve_head( vos_pkt_t *pPacket,
v_VOID_t **ppData,
v_SIZE_t dataSize )
{
return VOS_STATUS_SUCCESS;
}
VOS_STATUS vos_pkt_pop_head( vos_pkt_t *pPacket,
v_VOID_t *pData,
v_SIZE_t dataSize )
{
return VOS_STATUS_SUCCESS;
}
VOS_STATUS vos_pkt_push_head( vos_pkt_t *pPacket,
v_VOID_t *pData,
v_SIZE_t dataSize )
{
return VOS_STATUS_SUCCESS;
}
v_VOID_t vos_pkt_set_user_data_ptr( vos_pkt_t *pPacket,
VOS_PKT_USER_DATA_ID userID,
v_VOID_t *pUserData )
{
return;
}
v_VOID_t vos_pkt_get_user_data_ptr( vos_pkt_t *pPacket,
VOS_PKT_USER_DATA_ID userID,
v_VOID_t **ppUserData )
{
return;
}
VOS_STATUS vos_pkt_extract_data( vos_pkt_t *pPacket,
v_SIZE_t pktOffset,
v_VOID_t *pOutputBuffer,
v_SIZE_t *pOutputBufferSize )
{
return VOS_STATUS_SUCCESS;
}
/*---------------------------------------------------------------------------
* brief vos_pkt_get_proto_type() -
Find protoco type from packet contents
* skb Packet Pointer
* tracking_map packet type want to track
* dot11_type, type of dot11 frame
---------------------------------------------------------------------------*/
v_U8_t vos_pkt_get_proto_type
(
struct sk_buff *skb,
v_U8_t tracking_map,
v_U8_t dot11_type
)
{
v_U8_t pkt_proto_type = 0;
if (dot11_type)
{
if (dot11_type == (VOS_PKT_TRAC_TYPE_MGMT_ACTION & tracking_map))
pkt_proto_type = VOS_PKT_TRAC_TYPE_MGMT_ACTION;
/* Protocol type map */
return pkt_proto_type;
}
/* EAPOL Tracking enabled */
if (VOS_PKT_TRAC_TYPE_EAPOL & tracking_map)
{
if (adf_nbuf_is_eapol_pkt(skb)) {
pkt_proto_type = VOS_PKT_TRAC_TYPE_EAPOL;
return pkt_proto_type;
}
}
/* DHCP Tracking enabled */
if (VOS_PKT_TRAC_TYPE_DHCP & tracking_map)
{
if (adf_nbuf_is_dhcp_pkt(skb)) {
pkt_proto_type = VOS_PKT_TRAC_TYPE_DHCP;
return pkt_proto_type;
}
}
/* ARP Tracking enabled */
if (VOS_PKT_TRAC_TYPE_ARP & tracking_map) {
if (adf_nbuf_is_ipv4_arp_pkt(skb)) {
pkt_proto_type = VOS_PKT_TRAC_TYPE_ARP;
return pkt_proto_type;
}
}
/* IPV6 NS Tracking enabled */
if (VOS_PKT_TRAC_TYPE_NS & tracking_map) {
if (adf_nbuf_is_icmpv6_pkt(skb)) {
if (adf_nbuf_get_icmpv6_subtype(skb) ==
ADF_PROTO_ICMPV6_NS) {
pkt_proto_type = VOS_PKT_TRAC_TYPE_NS;
return pkt_proto_type;
}
}
}
/* IPV6 NA Tracking enabled */
if (VOS_PKT_TRAC_TYPE_NA & tracking_map) {
if (adf_nbuf_is_icmpv6_pkt(skb)) {
if (adf_nbuf_get_icmpv6_subtype(skb) ==
ADF_PROTO_ICMPV6_NA) {
pkt_proto_type = VOS_PKT_TRAC_TYPE_NA;
return pkt_proto_type;
}
}
}
/* Protocol type map */
return pkt_proto_type;
}
#ifdef QCA_PKT_PROTO_TRACE
/**
* vos_pkt_trace_buf_update - Update storage buffer with interested event string
* @event_string: A string for packet type or outstanding event
*/
void vos_pkt_trace_buf_update
(
char *event_string
)
{
v_U32_t slot;
struct timeval tv;
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"%s %d, %s", __func__, __LINE__, event_string);
if (!trace_buffer) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"trace_buffer is already free");
return;
}
SPIN_LOCK_BH(&trace_buffer_lock);
slot = trace_buffer_order % VOS_PKT_TRAC_MAX_TRACE_BUF;
trace_buffer[slot].order = trace_buffer_order;
trace_buffer_order++;
SPIN_UNLOCK_BH(&trace_buffer_lock);
do_gettimeofday(&tv);
trace_buffer[slot].event_sec_time = tv.tv_sec;
trace_buffer[slot].event_msec_time = tv.tv_usec;
strlcpy(trace_buffer[slot].event_string, event_string,
sizeof(trace_buffer[slot].event_string));
return;
}
/**
* vos_pkt_trace_dump_slot_buf() - Helper function to dump pkt trace
* @slot: index
*
* Return: none
*/
void vos_pkt_trace_dump_slot_buf(int slot)
{
struct rtc_time tm;
unsigned long local_time;
local_time = (u32)(trace_buffer[slot].event_sec_time -
(sys_tz.tz_minuteswest * 60));
rtc_time_to_tm(local_time, &tm);
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%5d : [%02d:%02d:%02d.%06lu] : %s",
trace_buffer[slot].order,
tm.tm_hour, tm.tm_min, tm.tm_sec,
trace_buffer[slot].event_sec_time,
trace_buffer[slot].event_string);
}
/**
* vos_pkt_trace_buf_dump - Dump stored information into kernel log
*/
void vos_pkt_trace_buf_dump(void)
{
uint32_t i, latest_idx = trace_buffer_order;
int slot;
if (!trace_buffer || !latest_idx) {
VOS_TRACE(VOS_MODULE_ID_SYS, VOS_TRACE_LEVEL_INFO,
"trace_buffer is already free trace_buffer_order: %d",
trace_buffer_order);
return;
}
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"PACKET TRACE DUMP START Current Timestamp %u",
(unsigned int)vos_timer_get_system_time());
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"ORDER : RTC TIME : EVT");
if (VOS_PKT_TRAC_MAX_TRACE_BUF > latest_idx) {
/*
* Scenario: Number of trace records less than MAX,
* Circular buffer not overwritten.
*/
for (slot = latest_idx - 1; slot >= 0 &&
slot > trace_dump_order; slot--)
vos_pkt_trace_dump_slot_buf(slot);
} else {
/*
* Scenario: Number of trace records exceeded MAX,
* Circular buffer is overwritten.
*/
for (i = 0; (i < VOS_PKT_TRAC_MAX_TRACE_BUF) &&
(latest_idx - i - 1 > trace_dump_order); i++) {
slot = ((latest_idx - i - 1) %
VOS_PKT_TRAC_MAX_TRACE_BUF);
vos_pkt_trace_dump_slot_buf(slot);
}
}
trace_dump_order = latest_idx - 1;
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"PACKET TRACE DUMP END");
return;
}
/**
* vos_pkt_proto_trace_init - Initialize protocol trace functionality and
* allocate required resources
*/
void vos_pkt_proto_trace_init
(
void
)
{
/* Init spin lock to protect global memory */
spin_lock_init(&trace_buffer_lock);
trace_buffer_order = 0;
trace_buffer = vos_mem_malloc(
VOS_PKT_TRAC_MAX_TRACE_BUF * sizeof(vos_pkt_proto_trace_t));
vos_mem_zero((void *)trace_buffer,
VOS_PKT_TRAC_MAX_TRACE_BUF * sizeof(vos_pkt_proto_trace_t));
/* Register callback function to NBUF
* Lower layer event also will be reported to here */
adf_nbuf_reg_trace_cb(vos_pkt_trace_buf_update);
return;
}
/*---------------------------------------------------------------------------
* brief vos_pkt_proto_trace_close() -
Free required resource
---------------------------------------------------------------------------*/
void vos_pkt_proto_trace_close
(
void
)
{
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
"%s %d", __func__, __LINE__);
SPIN_LOCK_BH(&trace_buffer_lock);
vos_mem_free(trace_buffer);
trace_buffer = NULL;
SPIN_UNLOCK_BH(&trace_buffer_lock);
return;
}
#endif /* QCA_PKT_PROTO_TRACE */