Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.4.
  1/*
  2 * Copyright 2012-15 Advanced Micro Devices, Inc.
  3 *
  4 * Permission is hereby granted, free of charge, to any person obtaining a
  5 * copy of this software and associated documentation files (the "Software"),
  6 * to deal in the Software without restriction, including without limitation
  7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8 * and/or sell copies of the Software, and to permit persons to whom the
  9 * Software is furnished to do so, subject to the following conditions:
 10 *
 11 * The above copyright notice and this permission notice shall be included in
 12 * all copies or substantial portions of the Software.
 13 *
 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 20 * OTHER DEALINGS IN THE SOFTWARE.
 21 *
 22 * Authors: AMD
 23 *
 24 */
 25#include "dm_services.h"
 26#include "include/logger_interface.h"
 27#include "logger.h"
 28
 29
 30#define NUM_ELEMENTS(a) (sizeof(a) / sizeof((a)[0]))
 31
 32static const struct dc_log_type_info log_type_info_tbl[] = {
 33		{LOG_ERROR,                 "Error"},
 34		{LOG_WARNING,               "Warning"},
 35		{LOG_DEBUG,		    "Debug"},
 36		{LOG_DC,                    "DC_Interface"},
 37		{LOG_SURFACE,               "Surface"},
 38		{LOG_HW_HOTPLUG,            "HW_Hotplug"},
 39		{LOG_HW_LINK_TRAINING,      "HW_LKTN"},
 40		{LOG_HW_SET_MODE,           "HW_Mode"},
 41		{LOG_HW_RESUME_S3,          "HW_Resume"},
 42		{LOG_HW_AUDIO,              "HW_Audio"},
 43		{LOG_HW_HPD_IRQ,            "HW_HPDIRQ"},
 44		{LOG_MST,                   "MST"},
 45		{LOG_SCALER,                "Scaler"},
 46		{LOG_BIOS,                  "BIOS"},
 47		{LOG_BANDWIDTH_CALCS,       "BWCalcs"},
 48		{LOG_BANDWIDTH_VALIDATION,  "BWValidation"},
 49		{LOG_I2C_AUX,               "I2C_AUX"},
 50		{LOG_SYNC,                  "Sync"},
 51		{LOG_BACKLIGHT,             "Backlight"},
 52		{LOG_FEATURE_OVERRIDE,      "Override"},
 53		{LOG_DETECTION_EDID_PARSER, "Edid"},
 54		{LOG_DETECTION_DP_CAPS,     "DP_Caps"},
 55		{LOG_RESOURCE,              "Resource"},
 56		{LOG_DML,                   "DML"},
 57		{LOG_EVENT_MODE_SET,        "Mode"},
 58		{LOG_EVENT_DETECTION,       "Detect"},
 59		{LOG_EVENT_LINK_TRAINING,   "LKTN"},
 60		{LOG_EVENT_LINK_LOSS,       "LinkLoss"},
 61		{LOG_EVENT_UNDERFLOW,       "Underflow"},
 62		{LOG_IF_TRACE,              "InterfaceTrace"},
 63		{LOG_DTN,                   "DTN"},
 64		{LOG_PROFILING,             "Profiling"}
 65};
 66
 67
 68/* ----------- Object init and destruction ----------- */
 69static bool construct(struct dc_context *ctx, struct dal_logger *logger,
 70		      uint32_t log_mask)
 71{
 72	/* malloc buffer and init offsets */
 73	logger->log_buffer_size = DAL_LOGGER_BUFFER_MAX_SIZE;
 74	logger->log_buffer = kcalloc(logger->log_buffer_size, sizeof(char),
 75				     GFP_KERNEL);
 76	if (!logger->log_buffer)
 77		return false;
 78
 79	/* Initialize both offsets to start of buffer (empty) */
 80	logger->buffer_read_offset = 0;
 81	logger->buffer_write_offset = 0;
 82
 83	logger->open_count = 0;
 84
 85	logger->flags.bits.ENABLE_CONSOLE = 1;
 86	logger->flags.bits.ENABLE_BUFFER = 0;
 87
 88	logger->ctx = ctx;
 89
 90	logger->mask = log_mask;
 91
 92	return true;
 93}
 94
 95static void destruct(struct dal_logger *logger)
 96{
 97	if (logger->log_buffer) {
 98		kfree(logger->log_buffer);
 99		logger->log_buffer = NULL;
100	}
101}
102
103struct dal_logger *dal_logger_create(struct dc_context *ctx, uint32_t log_mask)
104{
105	/* malloc struct */
106	struct dal_logger *logger = kzalloc(sizeof(struct dal_logger),
107					    GFP_KERNEL);
108
109	if (!logger)
110		return NULL;
111	if (!construct(ctx, logger, log_mask)) {
112		kfree(logger);
113		return NULL;
114	}
115
116	return logger;
117}
118
119uint32_t dal_logger_destroy(struct dal_logger **logger)
120{
121	if (logger == NULL || *logger == NULL)
122		return 1;
123	destruct(*logger);
124	kfree(*logger);
125	*logger = NULL;
126
127	return 0;
128}
129
130/* ------------------------------------------------------------------------ */
131
132
133static bool dal_logger_should_log(
134	struct dal_logger *logger,
135	enum dc_log_type log_type)
136{
137	if (logger->mask & (1 << log_type))
138		return true;
139
140	return false;
141}
142
143static void log_to_debug_console(struct log_entry *entry)
144{
145	struct dal_logger *logger = entry->logger;
146
147	if (logger->flags.bits.ENABLE_CONSOLE == 0)
148		return;
149
150	if (entry->buf_offset) {
151		switch (entry->type) {
152		case LOG_ERROR:
153			dm_error("%s", entry->buf);
154			break;
155		default:
156			dm_output_to_console("%s", entry->buf);
157			break;
158		}
159	}
160}
161
162/* Print everything unread existing in log_buffer to debug console*/
163void dm_logger_flush_buffer(struct dal_logger *logger, bool should_warn)
164{
165	char *string_start = &logger->log_buffer[logger->buffer_read_offset];
166
167	if (should_warn)
168		dm_output_to_console(
169			"---------------- FLUSHING LOG BUFFER ----------------\n");
170	while (logger->buffer_read_offset < logger->buffer_write_offset) {
171
172		if (logger->log_buffer[logger->buffer_read_offset] == '\0') {
173			dm_output_to_console("%s", string_start);
174			string_start = logger->log_buffer + logger->buffer_read_offset + 1;
175		}
176		logger->buffer_read_offset++;
177	}
178	if (should_warn)
179		dm_output_to_console(
180			"-------------- END FLUSHING LOG BUFFER --------------\n\n");
181}
182
183static void log_to_internal_buffer(struct log_entry *entry)
184{
185
186	uint32_t size = entry->buf_offset;
187	struct dal_logger *logger = entry->logger;
188
189	if (logger->flags.bits.ENABLE_BUFFER == 0)
190		return;
191
192	if (logger->log_buffer == NULL)
193		return;
194
195	if (size > 0 && size < logger->log_buffer_size) {
196
197		int buffer_space = logger->log_buffer_size -
198				logger->buffer_write_offset;
199
200		if (logger->buffer_write_offset == logger->buffer_read_offset) {
201			/* Buffer is empty, start writing at beginning */
202			buffer_space = logger->log_buffer_size;
203			logger->buffer_write_offset = 0;
204			logger->buffer_read_offset = 0;
205		}
206
207		if (buffer_space > size) {
208			/* No wrap around, copy 'size' bytes
209			 * from 'entry->buf' to 'log_buffer'
210			 */
211			memmove(logger->log_buffer +
212					logger->buffer_write_offset,
213					entry->buf, size);
214			logger->buffer_write_offset += size;
215
216		} else {
217			/* Not enough room remaining, we should flush
218			 * existing logs */
219
220			/* Flush existing unread logs to console */
221			dm_logger_flush_buffer(logger, true);
222
223			/* Start writing to beginning of buffer */
224			memmove(logger->log_buffer, entry->buf, size);
225			logger->buffer_write_offset = size;
226			logger->buffer_read_offset = 0;
227		}
228
229	}
230}
231
232static void log_heading(struct log_entry *entry)
233{
234	int j;
235
236	for (j = 0; j < NUM_ELEMENTS(log_type_info_tbl); j++) {
237
238		const struct dc_log_type_info *info = &log_type_info_tbl[j];
239
240		if (info->type == entry->type)
241			dm_logger_append(entry, "[%s]\t", info->name);
242	}
243}
244
245static void append_entry(
246		struct log_entry *entry,
247		char *buffer,
248		uint32_t buf_size)
249{
250	if (!entry->buf ||
251		entry->buf_offset + buf_size > entry->max_buf_bytes
252	) {
253		BREAK_TO_DEBUGGER();
254		return;
255	}
256
257	/* Todo: check if off by 1 byte due to \0 anywhere */
258	memmove(entry->buf + entry->buf_offset, buffer, buf_size);
259	entry->buf_offset += buf_size;
260}
261
262/* ------------------------------------------------------------------------ */
263
264/* Warning: Be careful that 'msg' is null terminated and the total size is
265 * less than DAL_LOGGER_BUFFER_MAX_LOG_LINE_SIZE (256) including '\0'
266 */
267void dm_logger_write(
268	struct dal_logger *logger,
269	enum dc_log_type log_type,
270	const char *msg,
271	...)
272{
273	if (logger && dal_logger_should_log(logger, log_type)) {
274		uint32_t size;
275		va_list args;
276		char buffer[LOG_MAX_LINE_SIZE];
277		struct log_entry entry;
278
279		va_start(args, msg);
280
281		entry.logger = logger;
282
283		entry.buf = buffer;
284
285		entry.buf_offset = 0;
286		entry.max_buf_bytes = DAL_LOGGER_BUFFER_MAX_SIZE * sizeof(char);
287
288		entry.type = log_type;
289
290		log_heading(&entry);
291
292		size = dm_log_to_buffer(
293			buffer, LOG_MAX_LINE_SIZE - 1, msg, args);
294
295		buffer[entry.buf_offset + size] = '\0';
296		entry.buf_offset += size + 1;
297
298		/* --Flush log_entry buffer-- */
299		/* print to kernel console */
300		log_to_debug_console(&entry);
301		/* log internally for dsat */
302		log_to_internal_buffer(&entry);
303
304		va_end(args);
305	}
306}
307
308/* Same as dm_logger_write, except without open() and close(), which must
309 * be done separately.
310 */
311void dm_logger_append(
312	struct log_entry *entry,
313	const char *msg,
314	...)
315{
316	va_list args;
317
318	va_start(args, msg);
319	dm_logger_append_va(entry, msg, args);
320	va_end(args);
321}
322
323void dm_logger_append_va(
324	struct log_entry *entry,
325	const char *msg,
326	va_list args)
327{
328	struct dal_logger *logger;
329
330	if (!entry) {
331		BREAK_TO_DEBUGGER();
332		return;
333	}
334
335	logger = entry->logger;
336
337	if (logger && logger->open_count > 0 &&
338		dal_logger_should_log(logger, entry->type)) {
339
340		uint32_t size;
341		char buffer[LOG_MAX_LINE_SIZE];
342
343		size = dm_log_to_buffer(
344			buffer, LOG_MAX_LINE_SIZE, msg, args);
345
346		if (size < LOG_MAX_LINE_SIZE - 1) {
347			append_entry(entry, buffer, size);
348		} else {
349			append_entry(entry, "LOG_ERROR, line too long\n", 27);
350		}
351	}
352}
353
354void dm_logger_open(
355		struct dal_logger *logger,
356		struct log_entry *entry, /* out */
357		enum dc_log_type log_type)
358{
359	if (!entry) {
360		BREAK_TO_DEBUGGER();
361		return;
362	}
363
364	entry->type = log_type;
365	entry->logger = logger;
366
367	entry->buf = kzalloc(DAL_LOGGER_BUFFER_MAX_SIZE * sizeof(char),
368			     GFP_KERNEL);
369
370	entry->buf_offset = 0;
371	entry->max_buf_bytes = DAL_LOGGER_BUFFER_MAX_SIZE * sizeof(char);
372
373	logger->open_count++;
374
375	log_heading(entry);
376}
377
378void dm_logger_close(struct log_entry *entry)
379{
380	struct dal_logger *logger = entry->logger;
381
382	if (logger && logger->open_count > 0) {
383		logger->open_count--;
384	} else {
385		BREAK_TO_DEBUGGER();
386		goto cleanup;
387	}
388
389	/* --Flush log_entry buffer-- */
390	/* print to kernel console */
391	log_to_debug_console(entry);
392	/* log internally for dsat */
393	log_to_internal_buffer(entry);
394
395	/* TODO: Write end heading */
396
397cleanup:
398	if (entry->buf) {
399		kfree(entry->buf);
400		entry->buf = NULL;
401		entry->buf_offset = 0;
402		entry->max_buf_bytes = 0;
403	}
404}