/* | |
* Broadcom Dongle Host Driver (DHD) | |
* | |
* Copyright (C) 1999-2018, Broadcom. | |
* | |
* Unless you and Broadcom execute a separate written software license | |
* agreement governing use of this software, this software is licensed to you | |
* under the terms of the GNU General Public License version 2 (the "GPL"), | |
* available at http://www.broadcom.com/licenses/GPLv2.php, with the | |
* following added to such license: | |
* | |
* As a special exception, the copyright holders of this software give you | |
* permission to link this software with independent modules, and to copy and | |
* distribute the resulting executable under terms of your choice, provided that | |
* you also meet, for each linked independent module, the terms and conditions of | |
* the license of that module. An independent module is a module which is not | |
* derived from this software. The special exception does not apply to any | |
* modifications of the software. | |
* | |
* Notwithstanding the above, under no circumstances may you combine this | |
* software in any way with any other Broadcom software provided under a license | |
* other than the GPL, without Broadcom's express prior written consent. | |
* | |
* $Id: dhd_csi.c 606280 2015-12-15 05:28:25Z $ | |
*/ | |
#include <osl.h> | |
#include <bcmutils.h> | |
#include <bcmendian.h> | |
#include <linuxver.h> | |
#include <linux/list.h> | |
#include <linux/sort.h> | |
#include <dngl_stats.h> | |
#include <wlioctl.h> | |
#include <bcmevent.h> | |
#include <dhd.h> | |
#include <dhd_dbg.h> | |
#include <dhd_csi.h> | |
#define NULL_CHECK(p, s, err) \ | |
do { \ | |
if (!(p)) { \ | |
printf("NULL POINTER (%s) : %s\n", __FUNCTION__, (s)); \ | |
err = BCME_ERROR; \ | |
return err; \ | |
} \ | |
} while (0) | |
#define TIMESPEC_TO_US(ts) (((uint64)(ts).tv_sec * USEC_PER_SEC) + \ | |
(ts).tv_nsec / NSEC_PER_USEC) | |
#define NULL_ADDR "\x00\x00\x00\x00\x00\x00" | |
int | |
dhd_csi_event_handler(dhd_pub_t *dhd, wl_event_msg_t *event, void *event_data) | |
{ | |
int ret = BCME_OK; | |
bool is_new = TRUE; | |
cfr_dump_data_t *p_event; | |
cfr_dump_list_t *ptr, *next, *new; | |
NULL_CHECK(dhd, "dhd is NULL", ret); | |
DHD_TRACE(("Enter %s\n", __FUNCTION__)); | |
if (!event_data) { | |
DHD_ERROR(("%s: event_data is NULL\n", __FUNCTION__)); | |
return -EINVAL; | |
} | |
p_event = (cfr_dump_data_t *)event_data; | |
/* check if this addr exist */ | |
if (!list_empty(&dhd->csi_list)) { | |
list_for_each_entry_safe(ptr, next, &dhd->csi_list, list) { | |
if (bcmp(&ptr->entry.header.peer_macaddr, &p_event->header.peer_macaddr, | |
ETHER_ADDR_LEN) == 0) { | |
int pos = 0, dump_len = 0, remain = 0; | |
is_new = FALSE; | |
DHD_INFO(("CSI data exist\n")); | |
if (p_event->header.status == 0) { | |
bcopy(&p_event->header, &ptr->entry.header, sizeof(cfr_dump_header_t)); | |
dump_len = p_event->header.cfr_dump_length; | |
if (dump_len < MAX_EVENT_SIZE) { | |
bcopy(&p_event->data, &ptr->entry.data, dump_len); | |
} else { | |
/* for big csi data */ | |
uint8 *p = (uint8 *)&ptr->entry.data; | |
remain = p_event->header.remain_length; | |
if (remain) { | |
pos = dump_len - remain - MAX_EVENT_SIZE; | |
p += pos; | |
bcopy(&p_event->data, p, MAX_EVENT_SIZE); | |
} | |
/* copy rest of csi data */ | |
else { | |
pos = dump_len - (dump_len % MAX_EVENT_SIZE); | |
p += pos; | |
bcopy(&p_event->data, p, (dump_len % MAX_EVENT_SIZE)); | |
} | |
} | |
return BCME_OK; | |
} | |
} | |
} | |
} | |
if (is_new) { | |
if (dhd->csi_count < MAX_CSI_NUM) { | |
new = (cfr_dump_list_t *)MALLOCZ(dhd->osh, sizeof(cfr_dump_list_t)); | |
if (!new){ | |
DHD_ERROR(("Malloc cfr dump list error\n")); | |
return BCME_NOMEM; | |
} | |
bcopy(&p_event->header, &new->entry.header, sizeof(cfr_dump_header_t)); | |
DHD_INFO(("New entry data size %d\n", p_event->header.cfr_dump_length)); | |
/* for big csi data */ | |
if (p_event->header.remain_length) { | |
DHD_TRACE(("remain %d\n", p_event->header.remain_length)); | |
bcopy(&p_event->data, &new->entry.data, MAX_EVENT_SIZE); | |
} | |
else | |
bcopy(&p_event->data, &new->entry.data, p_event->header.cfr_dump_length); | |
INIT_LIST_HEAD(&(new->list)); | |
list_add_tail(&(new->list), &dhd->csi_list); | |
dhd->csi_count++; | |
} | |
else { | |
DHD_TRACE(("Over maximum CSI Number 8. SKIP it.\n")); | |
} | |
} | |
return ret; | |
} | |
int | |
dhd_csi_init(dhd_pub_t *dhd) | |
{ | |
int err = BCME_OK; | |
NULL_CHECK(dhd, "dhd is NULL", err); | |
INIT_LIST_HEAD(&dhd->csi_list); | |
dhd->csi_count = 0; | |
return err; | |
} | |
int | |
dhd_csi_deinit(dhd_pub_t *dhd) | |
{ | |
int err = BCME_OK; | |
cfr_dump_list_t *ptr, *next; | |
NULL_CHECK(dhd, "dhd is NULL", err); | |
if (!list_empty(&dhd->csi_list)) { | |
list_for_each_entry_safe(ptr, next, &dhd->csi_list, list) { | |
list_del(&ptr->list); | |
MFREE(dhd->osh, ptr, sizeof(cfr_dump_list_t)); | |
} | |
} | |
return err; | |
} | |
void | |
dhd_csi_clean_list(dhd_pub_t *dhd) | |
{ | |
cfr_dump_list_t *ptr, *next; | |
int num = 0; | |
if (!dhd) { | |
DHD_ERROR(("NULL POINTER: %s\n", __FUNCTION__)); | |
return; | |
} | |
if (!list_empty(&dhd->csi_list)) { | |
list_for_each_entry_safe(ptr, next, &dhd->csi_list, list) { | |
if (0 == ptr->entry.header.remain_length) { | |
list_del(&ptr->list); | |
num++; | |
MFREE(dhd->osh, ptr, sizeof(cfr_dump_list_t)); | |
} | |
} | |
} | |
dhd->csi_count = 0; | |
DHD_TRACE(("Clean up %d record\n", num)); | |
} | |
int | |
dhd_csi_dump_list(dhd_pub_t *dhd, char *buf) | |
{ | |
int ret = BCME_OK; | |
cfr_dump_list_t *ptr, *next; | |
uint8 * pbuf = buf; | |
int num = 0; | |
int length = 0; | |
NULL_CHECK(dhd, "dhd is NULL", ret); | |
/* check if this addr exist */ | |
if (!list_empty(&dhd->csi_list)) { | |
list_for_each_entry_safe(ptr, next, &dhd->csi_list, list) { | |
if (ptr->entry.header.remain_length) { | |
DHD_ERROR(("data not ready %d\n", ptr->entry.header.remain_length)); | |
continue; | |
} | |
bcopy(&ptr->entry.header, pbuf, sizeof(cfr_dump_header_t)); | |
length += sizeof(cfr_dump_header_t); | |
pbuf += sizeof(cfr_dump_header_t); | |
DHD_TRACE(("Copy data size %d\n", ptr->entry.header.cfr_dump_length)); | |
bcopy(&ptr->entry.data, pbuf, ptr->entry.header.cfr_dump_length); | |
length += ptr->entry.header.cfr_dump_length; | |
pbuf += ptr->entry.header.cfr_dump_length; | |
num++; | |
} | |
} | |
DHD_TRACE(("dump %d record %d bytes\n", num, length)); | |
return length; | |
} | |