1 /*
2 * Copyright (c) 2013-2018, 2020-2021 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for
6 * any purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 /*
21 * DOC: Roaming debug log operations routines and global data
22 */
23
24 #include <qdf_types.h>
25 #include <qdf_atomic.h>
26 #include <qdf_mem.h>
27 #include <qdf_time.h>
28 #include <qdf_trace.h>
29 #include <qdf_module.h>
30 #include <wlan_cmn.h>
31 #include "wlan_roam_debug.h"
32
33 #ifdef FEATURE_ROAM_DEBUG
34 static void wlan_roam_rec_print(struct wlan_roam_debug_rec *dbg_rec,
35 uint32_t idx, uint32_t delta,
36 bool to_kernel);
37
38 static void wlan_conn_rec_print(struct wlan_roam_debug_rec *dbg_rec,
39 uint32_t idx, uint32_t delta,
40 bool to_kernel);
41
42 #ifdef WLAN_LOGGING_BUFFERS_DYNAMICALLY
43 static struct wlan_roam_debug_info *global_wlan_roam_debug_table;
44
45 /**
46 * wlan_roam_debug_init() - Allocate log buffer dynamically
47 *
48 * Return: none
49 */
wlan_roam_debug_init(void)50 void wlan_roam_debug_init(void)
51 {
52 uint8_t i;
53 global_wlan_roam_debug_table = qdf_mem_valloc(
54 sizeof(struct wlan_roam_debug_info) * REC_MAX);
55
56 QDF_BUG(global_wlan_roam_debug_table);
57
58 if (global_wlan_roam_debug_table) {
59 for (i = 0; i < REC_MAX; i++) {
60 qdf_atomic_init(&global_wlan_roam_debug_table[i].index);
61 global_wlan_roam_debug_table[i].num_max_rec =
62 WLAN_ROAM_DEBUG_MAX_REC;
63 if (i == REC_ROAM)
64 global_wlan_roam_debug_table[i].rec_print =
65 wlan_roam_rec_print;
66 else
67 global_wlan_roam_debug_table[i].rec_print =
68 wlan_conn_rec_print;
69 }
70 }
71 }
72
73 qdf_export_symbol(wlan_roam_debug_init);
74
wlan_roam_debug_get_table(wlan_rec_type type)75 static inline struct wlan_roam_debug_info *wlan_roam_debug_get_table(
76 wlan_rec_type type)
77 {
78 if (type >= REC_MAX)
79 return NULL;
80 return &global_wlan_roam_debug_table[type];
81 }
82
83 /**
84 * wlan_roam_debug_deinit() - Free log buffer allocated dynamically
85 *
86 * Return: none
87 */
wlan_roam_debug_deinit(void)88 void wlan_roam_debug_deinit(void)
89 {
90 qdf_mem_vfree(global_wlan_roam_debug_table);
91 global_wlan_roam_debug_table = NULL;
92 }
93
94 qdf_export_symbol(wlan_roam_debug_deinit);
95 #else /* WLAN_LOGGING_BUFFERS_DYNAMICALLY */
96 /*
97 * wlan roam debug log is stored in this global structure. It can be accessed
98 * without requiring any psoc or vdev context. It will be accessible in
99 * the crash dump without having to dereference complex stack traces.
100 */
101 static struct wlan_roam_debug_info global_wlan_roam_debug_table[REC_MAX] = {
102 [REC_ROAM] = {{ 0 },
103 .num_max_rec = WLAN_ROAM_DEBUG_MAX_REC,
104 .rec_print = wlan_roam_rec_print},
105 [REC_CONN] = {{ 0 },
106 .num_max_rec = WLAN_ROAM_DEBUG_MAX_REC,
107 .rec_print = wlan_conn_rec_print},
108 };
109
wlan_roam_debug_get_table(wlan_rec_type type)110 static inline struct wlan_roam_debug_info *wlan_roam_debug_get_table(
111 wlan_rec_type type)
112 {
113 if (type >= REC_MAX)
114 return NULL;
115 return &global_wlan_roam_debug_table[type];
116 }
117 #endif /* WLAN_LOGGING_BUFFERS_DYNAMICALLY */
118
119 /**
120 * wlan_roam_next_debug_log_index() - atomically increment and wrap around index
121 * @index: address of index to increment
122 * @size: wrap around this value
123 *
124 * Return: new value of index
125 */
wlan_roam_next_debug_log_index(qdf_atomic_t * index,int size)126 static int wlan_roam_next_debug_log_index(qdf_atomic_t *index, int size)
127 {
128 int i = qdf_atomic_inc_return(index);
129
130 if (i == WLAN_ROAM_DEBUG_MAX_REC)
131 qdf_atomic_sub(WLAN_ROAM_DEBUG_MAX_REC, index);
132 while (i >= size)
133 i -= WLAN_ROAM_DEBUG_MAX_REC;
134
135 return i;
136 }
137
138 /**
139 * wlan_roam_debug_log() - Add a debug log entry to wlan roam debug records
140 * @vdev_id: vdev identifier
141 * @op: operation identifier
142 * @peer_id: peer id
143 * @mac_addr: mac address of peer, can be NULL
144 * @peer_obj: peer object address, can be NULL
145 * @arg1: extra argument #1
146 * @arg2: extra argument #2
147 *
148 * Return: none
149 */
wlan_roam_debug_log(uint8_t vdev_id,uint8_t op,uint16_t peer_id,void * mac_addr,void * peer_obj,uint32_t arg1,uint32_t arg2)150 void wlan_roam_debug_log(uint8_t vdev_id, uint8_t op,
151 uint16_t peer_id, void *mac_addr,
152 void *peer_obj, uint32_t arg1, uint32_t arg2)
153 {
154 wlan_rec_debug_log(REC_ROAM, vdev_id, op, peer_id, mac_addr,
155 peer_obj, arg1, arg2);
156 }
157
158 qdf_export_symbol(wlan_roam_debug_log);
159
wlan_rec_debug_log(wlan_rec_type rec_type,uint8_t vdev_id,uint8_t op,uint16_t peer_id,const void * mac_addr,void * peer_obj,uint32_t arg1,uint32_t arg2)160 void wlan_rec_debug_log(wlan_rec_type rec_type, uint8_t vdev_id, uint8_t op,
161 uint16_t peer_id, const void *mac_addr,
162 void *peer_obj, uint32_t arg1, uint32_t arg2)
163 {
164 uint32_t i;
165 struct wlan_roam_debug_info *dbg_tbl;
166 struct wlan_roam_debug_rec *rec;
167
168 dbg_tbl = wlan_roam_debug_get_table(rec_type);
169 if (!dbg_tbl)
170 return;
171
172 i = wlan_roam_next_debug_log_index(
173 &dbg_tbl->index,
174 WLAN_ROAM_DEBUG_MAX_REC);
175 rec = &dbg_tbl->rec[i];
176 rec->time = qdf_get_log_timestamp();
177 rec->operation = op;
178 rec->vdev_id = vdev_id;
179 rec->peer_id = peer_id;
180 if (mac_addr)
181 qdf_mem_copy(rec->mac_addr.bytes, mac_addr,
182 QDF_MAC_ADDR_SIZE);
183 else
184 qdf_mem_zero(rec->mac_addr.bytes,
185 QDF_MAC_ADDR_SIZE);
186 rec->peer_obj = peer_obj;
187 rec->arg1 = arg1;
188 rec->arg2 = arg2;
189 }
190
191 qdf_export_symbol(wlan_rec_debug_log);
192
193 /**
194 * wlan_roam_debug_string() - convert operation value to printable string
195 * @op: operation identifier
196 *
197 * Return: printable string for the operation
198 */
wlan_roam_debug_string(uint32_t op)199 static char *wlan_roam_debug_string(uint32_t op)
200 {
201 switch (op) {
202 case DEBUG_PEER_CREATE_SEND:
203 return "peer create send";
204 case DEBUG_PEER_CREATE_RESP:
205 return "peer create resp_event";
206 case DEBUG_PEER_DELETE_SEND:
207 return "peer delete send";
208 case DEBUG_PEER_DELETE_RESP:
209 return "peer delete resp_event";
210 case DEBUG_PEER_MAP_EVENT:
211 return "peer map event";
212 case DEBUG_PEER_UNMAP_EVENT:
213 return "peer unmap event";
214 case DEBUG_PEER_UNREF_DELETE:
215 return "peer unref delete";
216 case DEBUG_DELETING_PEER_OBJ:
217 return "peer obj deleted";
218 case DEBUG_ROAM_SYNCH_IND:
219 return "roam synch ind event";
220 case DEBUG_ROAM_SYNCH_CNF:
221 return "roam sync conf sent";
222 case DEBUG_ROAM_SYNCH_FAIL:
223 return "roam sync fail event";
224 case DEBUG_ROAM_EVENT:
225 return "roam event";
226 case DEBUG_WOW_ROAM_EVENT:
227 return "wow wakeup roam event";
228 case DEBUG_BUS_SUSPEND:
229 return "host suspend";
230 case DEBUG_BUS_RESUME:
231 return "host wakeup";
232 case DEBUG_WOW_REASON:
233 return "wow wakeup reason";
234 case DEBUG_CONN_CONNECTING:
235 return "conn";
236 case DEBUG_CONN_ASSOCIATION:
237 return "assoc";
238 case DEBUG_CONN_CONNECT_RESULT:
239 return "cnrlt";
240 case DEBUG_CONN_ROAMING:
241 return "roaming";
242 case DEBUG_CONN_ROAMED:
243 return "roamed";
244 case DEBUG_CONN_ROAMED_IND:
245 return "rmind";
246 case DEBUG_CONN_DISCONNECT:
247 return "disc";
248 case DEBUG_CONN_DISCONNECT_HANDLER:
249 return "dishdr";
250 case DEBUG_CONN_DISCONNECT_IND:
251 return "disind";
252 case DEBUG_CONN_RSO:
253 return "rso";
254 default:
255 return "unknown";
256 }
257 }
258
wlan_roam_rec_print(struct wlan_roam_debug_rec * dbg_rec,uint32_t idx,uint32_t delta,bool to_kernel)259 void wlan_roam_rec_print(struct wlan_roam_debug_rec *dbg_rec,
260 uint32_t idx, uint32_t delta,
261 bool to_kernel)
262 {
263 roam_debug("index = %5d timestamp = 0x%016llx delta ms = %-12u",
264 idx, dbg_rec->time, delta);
265 roam_debug("info = %-24s vdev_id = %-3d mac addr = "QDF_MAC_ADDR_FMT,
266 wlan_roam_debug_string(dbg_rec->operation),
267 (int8_t)dbg_rec->vdev_id,
268 QDF_MAC_ADDR_REF(dbg_rec->mac_addr.bytes));
269 roam_debug("peer obj = 0x%pK peer_id = %-4d", dbg_rec->peer_obj,
270 (int8_t)dbg_rec->peer_id);
271 roam_debug("arg1 = 0x%-8x arg2 = 0x%-8x", dbg_rec->arg1,
272 dbg_rec->arg2);
273 }
274
wlan_conn_rec_print(struct wlan_roam_debug_rec * dbg_rec,uint32_t idx,uint32_t delta,bool to_kernel)275 void wlan_conn_rec_print(struct wlan_roam_debug_rec *dbg_rec,
276 uint32_t idx, uint32_t delta,
277 bool to_kernel)
278 {
279 if (to_kernel) {
280 roam_info("i %d ti 0x%08llx ms %u vdv %d %s a1 0x%x a2 0x%x "QDF_MAC_ADDR_FMT,
281 idx, dbg_rec->time, delta, (int8_t)dbg_rec->vdev_id,
282 wlan_roam_debug_string(dbg_rec->operation),
283 dbg_rec->arg1, dbg_rec->arg2,
284 QDF_MAC_ADDR_REF(dbg_rec->mac_addr.bytes));
285 } else {
286 roam_debug("i %d ti 0x%08llx ms %u vdv %d %s a1 0x%x a2 0x%x "QDF_MAC_ADDR_FMT,
287 idx, dbg_rec->time, delta, (int8_t)dbg_rec->vdev_id,
288 wlan_roam_debug_string(dbg_rec->operation),
289 dbg_rec->arg1, dbg_rec->arg2,
290 QDF_MAC_ADDR_REF(dbg_rec->mac_addr.bytes));
291 }
292 }
293
294 /**
295 * wlan_rec_debug_dump_table() - Print the wlan roam debug log records
296 * print all the valid debug records in the order of timestamp
297 *
298 * Return: none
299 */
wlan_rec_debug_dump_table(wlan_rec_type rec_type,uint32_t count,bool to_kernel)300 void wlan_rec_debug_dump_table(wlan_rec_type rec_type, uint32_t count,
301 bool to_kernel)
302 {
303 uint32_t i;
304 int32_t current_index;
305 struct wlan_roam_debug_info *dbg_tbl;
306 struct wlan_roam_debug_rec *dbg_rec;
307 uint64_t startt = 0;
308 uint32_t delta;
309
310 #define DEBUG_CLOCK_TICKS_PER_MSEC 19200
311 if (count > WLAN_ROAM_DEBUG_MAX_REC)
312 count = WLAN_ROAM_DEBUG_MAX_REC;
313 dbg_tbl = wlan_roam_debug_get_table(rec_type);
314 if (!dbg_tbl)
315 return;
316
317 current_index = qdf_atomic_read(&dbg_tbl->index);
318 if (current_index < 0) {
319 roam_debug("No records to dump");
320 return;
321 }
322 roam_debug("dump %d rec type %d idx %d", count, rec_type,
323 current_index);
324
325 i = (current_index + WLAN_ROAM_DEBUG_MAX_REC - count) %
326 WLAN_ROAM_DEBUG_MAX_REC;
327 do {
328 /* wrap around */
329 i = (i + 1) % WLAN_ROAM_DEBUG_MAX_REC;
330 dbg_rec = &dbg_tbl->rec[i];
331 /* skip unused entry */
332 if (dbg_rec->time == 0)
333 continue;
334 if (count == 0)
335 break;
336 count--;
337
338 if (startt == 0)
339 startt = dbg_rec->time;
340 /*
341 * Divide by 19200 == right shift 8 bits, then divide by 75
342 * 32 bit computation keeps both 32 and 64 bit compilers happy.
343 * The value will roll over after approx. 33554 seconds.
344 */
345 delta = (uint32_t) (((dbg_rec->time - startt) >> 8) &
346 0xffffffff);
347 delta = delta / (DEBUG_CLOCK_TICKS_PER_MSEC >> 8);
348
349 if (dbg_tbl->rec_print)
350 dbg_tbl->rec_print(dbg_rec, i, delta, to_kernel);
351 } while (i != current_index);
352 }
353
354 qdf_export_symbol(wlan_rec_debug_dump_table);
355
356 /**
357 * wlan_roam_debug_dump_table() - Print the wlan roam debug log records
358 * print all the valid debug records in the order of timestamp
359 *
360 * Return: none
361 */
wlan_roam_debug_dump_table(void)362 void wlan_roam_debug_dump_table(void)
363 {
364 wlan_rec_debug_dump_table(REC_ROAM, WLAN_ROAM_DEBUG_MAX_REC, false);
365 }
366 qdf_export_symbol(wlan_roam_debug_dump_table);
367
368 #endif /* FEATURE_ROAM_DEBUG */
369