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