| /* |
| * 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; |
| } |
| |