1 /*
2 * Broadcom Dongle Host Driver (DHD)
3 *
4 * Copyright (C) 1999-2018, Broadcom.
5 *
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
11 *
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions
16 * of the license of that module. An independent module is a module which is
17 * not derived from this software. The special exception does not apply to any
18 * modifications of the software.
19 *
20 * Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
23 *
24 * $Id: dhd_csi.c 606280 2015-12-15 05:28:25Z $
25 */
26 #include <osl.h>
27
28 #include <bcmutils.h>
29
30 #include <bcmendian.h>
31 #include <linuxver.h>
32 #include <linux/list.h>
33 #include <linux/sort.h>
34 #include <dngl_stats.h>
35 #include <wlioctl.h>
36
37 #include <bcmevent.h>
38 #include <dhd.h>
39 #include <dhd_dbg.h>
40 #include <dhd_csi.h>
41
42 #define NULL_CHECK(p, s, err) \
43 do { \
44 if (!(p)) { \
45 printf("NULL POINTER (%s) : %s\n", __FUNCTION__, (s)); \
46 err = BCME_ERROR; \
47 return err; \
48 } \
49 } while (0)
50
51 #define TIMESPEC_TO_US(ts) \
52 (((uint64)(ts).tv_sec * USEC_PER_SEC) + (ts).tv_nsec / NSEC_PER_USEC)
53
54 #define NULL_ADDR "\x00\x00\x00\x00\x00\x00"
55
dhd_csi_event_handler(dhd_pub_t * dhd,wl_event_msg_t * event,void * event_data)56 int dhd_csi_event_handler(dhd_pub_t *dhd, wl_event_msg_t *event,
57 void *event_data)
58 {
59 int ret = BCME_OK;
60 bool is_new = TRUE;
61 cfr_dump_data_t *p_event;
62 cfr_dump_list_t *ptr, *next, *new;
63
64 NULL_CHECK(dhd, "dhd is NULL", ret);
65
66 DHD_TRACE(("Enter %s\n", __FUNCTION__));
67
68 if (!event_data) {
69 DHD_ERROR(("%s: event_data is NULL\n", __FUNCTION__));
70 return -EINVAL;
71 }
72 p_event = (cfr_dump_data_t *)event_data;
73
74 /* check if this addr exist */
75 if (!list_empty(&dhd->csi_list)) {
76 list_for_each_entry_safe(ptr, next, &dhd->csi_list, list)
77 {
78 if (bcmp(&ptr->entry.header.peer_macaddr,
79 &p_event->header.peer_macaddr, ETHER_ADDR_LEN) == 0) {
80 int pos = 0, dump_len = 0, remain = 0;
81 is_new = FALSE;
82 DHD_INFO(("CSI data exist\n"));
83 if (p_event->header.status == 0) {
84 bcopy(&p_event->header, &ptr->entry.header,
85 sizeof(cfr_dump_header_t));
86 dump_len = p_event->header.cfr_dump_length;
87 if (dump_len < MAX_EVENT_SIZE) {
88 bcopy(&p_event->data, &ptr->entry.data, dump_len);
89 } else {
90 /* for big csi data */
91 uint8 *p = (uint8 *)&ptr->entry.data;
92 remain = p_event->header.remain_length;
93 if (remain) {
94 pos = dump_len - remain - MAX_EVENT_SIZE;
95 p += pos;
96 bcopy(&p_event->data, p, MAX_EVENT_SIZE);
97 } else {
98 /* copy rest of csi data */
99 pos = dump_len - (dump_len % MAX_EVENT_SIZE);
100 p += pos;
101 bcopy(&p_event->data, p,
102 (dump_len % MAX_EVENT_SIZE));
103 }
104 }
105 return BCME_OK;
106 }
107 }
108 }
109 }
110 if (is_new) {
111 if (dhd->csi_count < MAX_CSI_NUM) {
112 new = (cfr_dump_list_t *)MALLOCZ(dhd->osh, sizeof(cfr_dump_list_t));
113 if (!new) {
114 DHD_ERROR(("Malloc cfr dump list error\n"));
115 return BCME_NOMEM;
116 }
117 bcopy(&p_event->header, &new->entry.header,
118 sizeof(cfr_dump_header_t));
119 DHD_INFO(
120 ("New entry data size %d\n", p_event->header.cfr_dump_length));
121 /* for big csi data */
122 if (p_event->header.remain_length) {
123 DHD_TRACE(("remain %d\n", p_event->header.remain_length));
124 bcopy(&p_event->data, &new->entry.data, MAX_EVENT_SIZE);
125 } else {
126 bcopy(&p_event->data, &new->entry.data,
127 p_event->header.cfr_dump_length);
128 }
129 INIT_LIST_HEAD(&(new->list));
130 list_add_tail(&(new->list), &dhd->csi_list);
131 dhd->csi_count++;
132 } else {
133 DHD_TRACE(("Over maximum CSI Number 8. SKIP it.\n"));
134 }
135 }
136 return ret;
137 }
138
dhd_csi_init(dhd_pub_t * dhd)139 int dhd_csi_init(dhd_pub_t *dhd)
140 {
141 int err = BCME_OK;
142
143 NULL_CHECK(dhd, "dhd is NULL", err);
144 INIT_LIST_HEAD(&dhd->csi_list);
145 dhd->csi_count = 0;
146
147 return err;
148 }
149
dhd_csi_deinit(dhd_pub_t * dhd)150 int dhd_csi_deinit(dhd_pub_t *dhd)
151 {
152 int err = BCME_OK;
153 cfr_dump_list_t *ptr, *next;
154
155 NULL_CHECK(dhd, "dhd is NULL", err);
156
157 if (!list_empty(&dhd->csi_list)) {
158 list_for_each_entry_safe(ptr, next, &dhd->csi_list, list)
159 {
160 list_del(&ptr->list);
161 MFREE(dhd->osh, ptr, sizeof(cfr_dump_list_t));
162 }
163 }
164 return err;
165 }
166
dhd_csi_clean_list(dhd_pub_t * dhd)167 void dhd_csi_clean_list(dhd_pub_t *dhd)
168 {
169 cfr_dump_list_t *ptr, *next;
170 int num = 0;
171
172 if (!dhd) {
173 DHD_ERROR(("NULL POINTER: %s\n", __FUNCTION__));
174 return;
175 }
176
177 if (!list_empty(&dhd->csi_list)) {
178 list_for_each_entry_safe(ptr, next, &dhd->csi_list, list)
179 {
180 if (ptr->entry.header.remain_length == 0) {
181 list_del(&ptr->list);
182 num++;
183 MFREE(dhd->osh, ptr, sizeof(cfr_dump_list_t));
184 }
185 }
186 }
187 dhd->csi_count = 0;
188 DHD_TRACE(("Clean up %d record\n", num));
189 }
190
dhd_csi_dump_list(dhd_pub_t * dhd,char * buf)191 int dhd_csi_dump_list(dhd_pub_t *dhd, char *buf)
192 {
193 int ret = BCME_OK;
194 cfr_dump_list_t *ptr, *next;
195 uint8 *pbuf = buf;
196 int num = 0;
197 int length = 0;
198
199 NULL_CHECK(dhd, "dhd is NULL", ret);
200
201 /* check if this addr exist */
202 if (!list_empty(&dhd->csi_list)) {
203 list_for_each_entry_safe(ptr, next, &dhd->csi_list, list)
204 {
205 if (ptr->entry.header.remain_length) {
206 DHD_ERROR(
207 ("data not ready %d\n", ptr->entry.header.remain_length));
208 continue;
209 }
210 bcopy(&ptr->entry.header, pbuf, sizeof(cfr_dump_header_t));
211 length += sizeof(cfr_dump_header_t);
212 pbuf += sizeof(cfr_dump_header_t);
213 DHD_TRACE(
214 ("Copy data size %d\n", ptr->entry.header.cfr_dump_length));
215 bcopy(&ptr->entry.data, pbuf, ptr->entry.header.cfr_dump_length);
216 length += ptr->entry.header.cfr_dump_length;
217 pbuf += ptr->entry.header.cfr_dump_length;
218 num++;
219 }
220 }
221 DHD_TRACE(("dump %d record %d bytes\n", num, length));
222
223 return length;
224 }
225