log: Add an implementation of logging
Add the logging header file and implementation with some configuration options to control it. Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
		
							parent
							
								
									c5404b64fb
								
							
						
					
					
						commit
						e9c8d49d54
					
				|  | @ -310,6 +310,13 @@ S:	Maintained | |||
| T:	git git://git.denx.de/u-boot-i2c.git | ||||
| F:	drivers/i2c/ | ||||
| 
 | ||||
| LOGGING | ||||
| M:	Simon Glass <sjg@chromium.org> | ||||
| S:	Maintained | ||||
| T:	git git://git.denx.de/u-boot.git | ||||
| F:	common/log.c | ||||
| F:	cmd/log.c | ||||
| 
 | ||||
| MICROBLAZE | ||||
| M:	Michal Simek <monstr@monstr.eu> | ||||
| S:	Maintained | ||||
|  |  | |||
|  | @ -420,6 +420,62 @@ config SYS_STDIO_DEREGISTER | |||
| 
 | ||||
| endmenu | ||||
| 
 | ||||
| menu "Logging" | ||||
| 
 | ||||
| config LOG | ||||
| 	bool "Enable logging support" | ||||
| 	help | ||||
| 	  This enables support for logging of status and debug messages. These | ||||
| 	  can be displayed on the console, recorded in a memory buffer, or | ||||
| 	  discarded if not needed. Logging supports various categories and | ||||
| 	  levels of severity. | ||||
| 
 | ||||
| config SPL_LOG | ||||
| 	bool "Enable logging support in SPL" | ||||
| 	help | ||||
| 	  This enables support for logging of status and debug messages. These | ||||
| 	  can be displayed on the console, recorded in a memory buffer, or | ||||
| 	  discarded if not needed. Logging supports various categories and | ||||
| 	  levels of severity. | ||||
| 
 | ||||
| config LOG_MAX_LEVEL | ||||
| 	int "Maximum log level to record" | ||||
| 	depends on LOG | ||||
| 	default 5 | ||||
| 	help | ||||
| 	  This selects the maximum log level that will be recorded. Any value | ||||
| 	  higher than this will be ignored. If possible log statements below | ||||
| 	  this level will be discarded at build time. Levels: | ||||
| 
 | ||||
| 	    0 - panic | ||||
| 	    1 - critical | ||||
| 	    2 - error | ||||
| 	    3 - warning | ||||
| 	    4 - note | ||||
| 	    5 - info | ||||
| 	    6 - detail | ||||
| 	    7 - debug | ||||
| 
 | ||||
| config SPL_LOG_MAX_LEVEL | ||||
| 	int "Maximum log level to record in SPL" | ||||
| 	depends on SPL_LOG | ||||
| 	default 3 | ||||
| 	help | ||||
| 	  This selects the maximum log level that will be recorded. Any value | ||||
| 	  higher than this will be ignored. If possible log statements below | ||||
| 	  this level will be discarded at build time. Levels: | ||||
| 
 | ||||
| 	    0 - panic | ||||
| 	    1 - critical | ||||
| 	    2 - error | ||||
| 	    3 - warning | ||||
| 	    4 - note | ||||
| 	    5 - info | ||||
| 	    6 - detail | ||||
| 	    7 - debug | ||||
| 
 | ||||
| endmenu | ||||
| 
 | ||||
| config DEFAULT_FDT_FILE | ||||
| 	string "Default fdt file" | ||||
| 	help | ||||
|  |  | |||
|  | @ -128,5 +128,6 @@ obj-y += cli.o | |||
| obj-$(CONFIG_FSL_DDR_INTERACTIVE) += cli_simple.o cli_readline.o | ||||
| obj-$(CONFIG_CMD_DFU) += dfu.o | ||||
| obj-y += command.o | ||||
| obj-$(CONFIG_$(SPL_)LOG) += log.o | ||||
| obj-y += s_record.o | ||||
| obj-y += xyzModem.o | ||||
|  |  | |||
|  | @ -0,0 +1,244 @@ | |||
| /*
 | ||||
|  * Logging support | ||||
|  * | ||||
|  * Copyright (c) 2017 Google, Inc | ||||
|  * Written by Simon Glass <sjg@chromium.org> | ||||
|  * | ||||
|  * SPDX-License-Identifier:	GPL-2.0+ | ||||
|  */ | ||||
| 
 | ||||
| #include <common.h> | ||||
| #include <log.h> | ||||
| #include <malloc.h> | ||||
| 
 | ||||
| DECLARE_GLOBAL_DATA_PTR; | ||||
| 
 | ||||
| static struct log_device *log_device_find_by_name(const char *drv_name) | ||||
| { | ||||
| 	struct log_device *ldev; | ||||
| 
 | ||||
| 	list_for_each_entry(ldev, &gd->log_head, sibling_node) { | ||||
| 		if (!strcmp(drv_name, ldev->drv->name)) | ||||
| 			return ldev; | ||||
| 	} | ||||
| 
 | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * log_has_cat() - check if a log category exists within a list | ||||
|  * | ||||
|  * @cat_list: List of categories to check, at most LOGF_MAX_CATEGORIES entries | ||||
|  *	long, terminated by LC_END if fewer | ||||
|  * @cat: Category to search for | ||||
|  * @return true if @cat is in @cat_list, else false | ||||
|  */ | ||||
| static bool log_has_cat(enum log_category_t cat_list[], enum log_category_t cat) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < LOGF_MAX_CATEGORIES && cat_list[i] != LOGC_END; i++) { | ||||
| 		if (cat_list[i] == cat) | ||||
| 			return true; | ||||
| 	} | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * log_has_file() - check if a file is with a list | ||||
|  * | ||||
|  * @file_list: List of files to check, separated by comma | ||||
|  * @file: File to check for. This string is matched against the end of each | ||||
|  *	file in the list, i.e. ignoring any preceding path. The list is | ||||
|  *	intended to consist of relative pathnames, e.g. common/main.c,cmd/log.c | ||||
|  * @return true if @file is in @file_list, else false | ||||
|  */ | ||||
| static bool log_has_file(const char *file_list, const char *file) | ||||
| { | ||||
| 	int file_len = strlen(file); | ||||
| 	const char *s, *p; | ||||
| 	int substr_len; | ||||
| 
 | ||||
| 	for (s = file_list; *s; s = p + (*p != '\0')) { | ||||
| 		p = strchrnul(s, ','); | ||||
| 		substr_len = p - s; | ||||
| 		if (file_len >= substr_len && | ||||
| 		    !strncmp(file + file_len - substr_len, s, substr_len)) | ||||
| 			return true; | ||||
| 	} | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * log_passes_filters() - check if a log record passes the filters for a device | ||||
|  * | ||||
|  * @ldev: Log device to check | ||||
|  * @rec: Log record to check | ||||
|  * @return true if @rec is not blocked by the filters in @ldev, false if it is | ||||
|  */ | ||||
| static bool log_passes_filters(struct log_device *ldev, struct log_rec *rec) | ||||
| { | ||||
| 	struct log_filter *filt; | ||||
| 
 | ||||
| 	/* If there are no filters, filter on the default log level */ | ||||
| 	if (list_empty(&ldev->filter_head)) { | ||||
| 		if (rec->level > gd->default_log_level) | ||||
| 			return false; | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	list_for_each_entry(filt, &ldev->filter_head, sibling_node) { | ||||
| 		if (rec->level > filt->max_level) | ||||
| 			continue; | ||||
| 		if ((filt->flags & LOGFF_HAS_CAT) && | ||||
| 		    !log_has_cat(filt->cat_list, rec->cat)) | ||||
| 			continue; | ||||
| 		if (filt->file_list && | ||||
| 		    !log_has_file(filt->file_list, rec->file)) | ||||
| 			continue; | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * log_dispatch() - Send a log record to all log devices for processing | ||||
|  * | ||||
|  * The log record is sent to each log device in turn, skipping those which have | ||||
|  * filters which block the record | ||||
|  * | ||||
|  * @rec: Log record to dispatch | ||||
|  * @return 0 (meaning success) | ||||
|  */ | ||||
| static int log_dispatch(struct log_rec *rec) | ||||
| { | ||||
| 	struct log_device *ldev; | ||||
| 
 | ||||
| 	list_for_each_entry(ldev, &gd->log_head, sibling_node) { | ||||
| 		if (log_passes_filters(ldev, rec)) | ||||
| 			ldev->drv->emit(ldev, rec); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int _log(enum log_category_t cat, enum log_level_t level, const char *file, | ||||
| 	 int line, const char *func, const char *fmt, ...) | ||||
| { | ||||
| 	char buf[CONFIG_SYS_CBSIZE]; | ||||
| 	struct log_rec rec; | ||||
| 	va_list args; | ||||
| 
 | ||||
| 	rec.cat = cat; | ||||
| 	rec.level = level; | ||||
| 	rec.file = file; | ||||
| 	rec.line = line; | ||||
| 	rec.func = func; | ||||
| 	va_start(args, fmt); | ||||
| 	vsnprintf(buf, sizeof(buf), fmt, args); | ||||
| 	va_end(args); | ||||
| 	rec.msg = buf; | ||||
| 	if (!gd || !(gd->flags & GD_FLG_LOG_READY)) { | ||||
| 		if (gd) | ||||
| 			gd->log_drop_count++; | ||||
| 		return -ENOSYS; | ||||
| 	} | ||||
| 	log_dispatch(&rec); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int log_add_filter(const char *drv_name, enum log_category_t cat_list[], | ||||
| 		   enum log_level_t max_level, const char *file_list) | ||||
| { | ||||
| 	struct log_filter *filt; | ||||
| 	struct log_device *ldev; | ||||
| 	int i; | ||||
| 
 | ||||
| 	ldev = log_device_find_by_name(drv_name); | ||||
| 	if (!ldev) | ||||
| 		return -ENOENT; | ||||
| 	filt = (struct log_filter *)calloc(1, sizeof(*filt)); | ||||
| 	if (!filt) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	if (cat_list) { | ||||
| 		filt->flags |= LOGFF_HAS_CAT; | ||||
| 		for (i = 0; ; i++) { | ||||
| 			if (i == ARRAY_SIZE(filt->cat_list)) | ||||
| 				return -ENOSPC; | ||||
| 			filt->cat_list[i] = cat_list[i]; | ||||
| 			if (cat_list[i] == LOGC_END) | ||||
| 				break; | ||||
| 		} | ||||
| 	} | ||||
| 	filt->max_level = max_level; | ||||
| 	if (file_list) { | ||||
| 		filt->file_list = strdup(file_list); | ||||
| 		if (!filt->file_list) | ||||
| 			goto nomem; | ||||
| 	} | ||||
| 	filt->filter_num = ldev->next_filter_num++; | ||||
| 	list_add_tail(&filt->sibling_node, &ldev->filter_head); | ||||
| 
 | ||||
| 	return filt->filter_num; | ||||
| 
 | ||||
| nomem: | ||||
| 	free(filt); | ||||
| 	return -ENOMEM; | ||||
| } | ||||
| 
 | ||||
| int log_remove_filter(const char *drv_name, int filter_num) | ||||
| { | ||||
| 	struct log_filter *filt; | ||||
| 	struct log_device *ldev; | ||||
| 
 | ||||
| 	ldev = log_device_find_by_name(drv_name); | ||||
| 	if (!ldev) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	list_for_each_entry(filt, &ldev->filter_head, sibling_node) { | ||||
| 		if (filt->filter_num == filter_num) { | ||||
| 			list_del(&filt->sibling_node); | ||||
| 			free(filt); | ||||
| 
 | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return -ENOENT; | ||||
| } | ||||
| 
 | ||||
| int log_init(void) | ||||
| { | ||||
| 	struct log_driver *drv = ll_entry_start(struct log_driver, log_driver); | ||||
| 	const int count = ll_entry_count(struct log_driver, log_driver); | ||||
| 	struct log_driver *end = drv + count; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * We cannot add runtime data to the driver since it is likely stored | ||||
| 	 * in rodata. Instead, set up a 'device' corresponding to each driver. | ||||
| 	 * We only support having a single device. | ||||
| 	 */ | ||||
| 	INIT_LIST_HEAD((struct list_head *)&gd->log_head); | ||||
| 	while (drv < end) { | ||||
| 		struct log_device *ldev; | ||||
| 
 | ||||
| 		ldev = calloc(1, sizeof(*ldev)); | ||||
| 		if (!ldev) { | ||||
| 			debug("%s: Cannot allocate memory\n", __func__); | ||||
| 			return -ENOMEM; | ||||
| 		} | ||||
| 		INIT_LIST_HEAD(&ldev->filter_head); | ||||
| 		ldev->drv = drv; | ||||
| 		list_add_tail(&ldev->sibling_node, | ||||
| 			      (struct list_head *)&gd->log_head); | ||||
| 		drv++; | ||||
| 	} | ||||
| 	gd->default_log_level = LOGL_INFO; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -114,6 +114,11 @@ typedef struct global_data { | |||
| 	struct bootstage_data *bootstage;	/* Bootstage information */ | ||||
| 	struct bootstage_data *new_bootstage;	/* Relocated bootstage info */ | ||||
| #endif | ||||
| #ifdef CONFIG_LOG | ||||
| 	int log_drop_count;		/* Number of dropped log messages */ | ||||
| 	int default_log_level;		/* For devices with no filters */ | ||||
| 	struct list_head log_head;	/* List of struct log_device */ | ||||
| #endif | ||||
| } gd_t; | ||||
| #endif | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										242
									
								
								include/log.h
								
								
								
								
							
							
						
						
									
										242
									
								
								include/log.h
								
								
								
								
							|  | @ -10,6 +10,94 @@ | |||
| #ifndef __LOG_H | ||||
| #define __LOG_H | ||||
| 
 | ||||
| #include <dm/uclass-id.h> | ||||
| #include <linux/list.h> | ||||
| 
 | ||||
| /** Log levels supported, ranging from most to least important */ | ||||
| enum log_level_t { | ||||
| 	LOGL_EMERG = 0,		/*U-Boot is unstable */ | ||||
| 	LOGL_ALERT,		/* Action must be taken immediately */ | ||||
| 	LOGL_CRIT,		/* Critical conditions */ | ||||
| 	LOGL_ERR,		/* Error that prevents something from working */ | ||||
| 	LOGL_WARNING,		/* Warning may prevent optimial operation */ | ||||
| 	LOGL_NOTICE,		/* Normal but significant condition, printf() */ | ||||
| 	LOGL_INFO,		/* General information message */ | ||||
| 	LOGL_DEBUG,		/* Basic debug-level message */ | ||||
| 	LOGL_DEBUG_CONTENT,	/* Debug message showing full message content */ | ||||
| 	LOGL_DEBUG_IO,		/* Debug message showing hardware I/O access */ | ||||
| 
 | ||||
| 	LOGL_COUNT, | ||||
| 	LOGL_FIRST = LOGL_EMERG, | ||||
| 	LOGL_MAX = LOGL_DEBUG, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Log categories supported. Most of these correspond to uclasses (i.e. | ||||
|  * enum uclass_id) but there are also some more generic categories | ||||
|  */ | ||||
| enum log_category_t { | ||||
| 	LOGC_FIRST = 0,	/* First part mirrors UCLASS_... */ | ||||
| 
 | ||||
| 	LOGC_NONE = UCLASS_COUNT, | ||||
| 	LOGC_ARCH, | ||||
| 	LOGC_BOARD, | ||||
| 	LOGC_CORE, | ||||
| 	LOGC_DT, | ||||
| 
 | ||||
| 	LOGC_COUNT, | ||||
| 	LOGC_END, | ||||
| }; | ||||
| 
 | ||||
| /* Helper to cast a uclass ID to a log category */ | ||||
| static inline int log_uc_cat(enum uclass_id id) | ||||
| { | ||||
| 	return (enum log_category_t)id; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * _log() - Internal function to emit a new log record | ||||
|  * | ||||
|  * @cat: Category of log record (indicating which subsystem generated it) | ||||
|  * @level: Level of log record (indicating its severity) | ||||
|  * @file: File name of file where log record was generated | ||||
|  * @line: Line number in file where log record was generated | ||||
|  * @func: Function where log record was generated | ||||
|  * @fmt: printf() format string for log record | ||||
|  * @...: Optional parameters, according to the format string @fmt | ||||
|  * @return 0 if log record was emitted, -ve on error | ||||
|  */ | ||||
| int _log(enum log_category_t cat, enum log_level_t level, const char *file, | ||||
| 	 int line, const char *func, const char *fmt, ...); | ||||
| 
 | ||||
| /* Define this at the top of a file to add a prefix to debug messages */ | ||||
| #ifndef pr_fmt | ||||
| #define pr_fmt(fmt) fmt | ||||
| #endif | ||||
| 
 | ||||
| /* Use a default category if this file does not supply one */ | ||||
| #ifndef LOG_CATEGORY | ||||
| #define LOG_CATEGORY LOGC_NONE | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * This header may be including when CONFIG_LOG is disabled, in which case | ||||
|  * CONFIG_LOG_MAX_LEVEL is not defined. Add a check for this. | ||||
|  */ | ||||
| #if CONFIG_IS_ENABLED(LOG) | ||||
| #define _LOG_MAX_LEVEL CONFIG_VAL(LOG_MAX_LEVEL) | ||||
| #else | ||||
| #define _LOG_MAX_LEVEL LOGL_INFO | ||||
| #endif | ||||
| 
 | ||||
| /* Emit a log record if the level is less that the maximum */ | ||||
| #define log(_cat, _level, _fmt, _args...) ({ \ | ||||
| 	int _l = _level; \ | ||||
| 	if (_l <= _LOG_MAX_LEVEL) \ | ||||
| 		_log((enum log_category_t)(_cat), _l, __FILE__, __LINE__, \ | ||||
| 		      __func__, \ | ||||
| 		      pr_fmt(_fmt), ##_args); \ | ||||
| 	}) | ||||
| 
 | ||||
| #ifdef DEBUG | ||||
| #define _DEBUG	1 | ||||
| #else | ||||
|  | @ -22,6 +110,16 @@ | |||
| #define _SPL_BUILD	0 | ||||
| #endif | ||||
| 
 | ||||
| #if !_DEBUG && CONFIG_IS_ENABLED(LOG) | ||||
| 
 | ||||
| #define debug_cond(cond, fmt, args...)			\ | ||||
| 	do {						\ | ||||
| 		if (1)					\ | ||||
| 			log(LOG_CATEGORY, LOGL_DEBUG, fmt, ##args); \ | ||||
| 	} while (0) | ||||
| 
 | ||||
| #else /* _DEBUG */ | ||||
| 
 | ||||
| /*
 | ||||
|  * Output a debug text when condition "cond" is met. The "cond" should be | ||||
|  * computed by a preprocessor in the best case, allowing for the best | ||||
|  | @ -33,6 +131,8 @@ | |||
| 			printf(pr_fmt(fmt), ##args);	\ | ||||
| 	} while (0) | ||||
| 
 | ||||
| #endif /* _DEBUG */ | ||||
| 
 | ||||
| /* Show a message if DEBUG is defined in a file */ | ||||
| #define debug(fmt, args...)			\ | ||||
| 	debug_cond(_DEBUG, fmt, ##args) | ||||
|  | @ -56,4 +156,146 @@ void __assert_fail(const char *assertion, const char *file, unsigned int line, | |||
| 	({ if (!(x) && _DEBUG) \ | ||||
| 		__assert_fail(#x, __FILE__, __LINE__, __func__); }) | ||||
| 
 | ||||
| /**
 | ||||
|  * struct log_rec - a single log record | ||||
|  * | ||||
|  * Holds information about a single record in the log | ||||
|  * | ||||
|  * Members marked as 'not allocated' are stored as pointers and the caller is | ||||
|  * responsible for making sure that the data pointed to is not overwritten. | ||||
|  * Memebers marked as 'allocated' are allocated (e.g. via strdup()) by the log | ||||
|  * system. | ||||
|  * | ||||
|  * @cat: Category, representing a uclass or part of U-Boot | ||||
|  * @level: Severity level, less severe is higher | ||||
|  * @file: Name of file where the log record was generated (not allocated) | ||||
|  * @line: Line number where the log record was generated | ||||
|  * @func: Function where the log record was generated (not allocated) | ||||
|  * @msg: Log message (allocated) | ||||
|  */ | ||||
| struct log_rec { | ||||
| 	enum log_category_t cat; | ||||
| 	enum log_level_t level; | ||||
| 	const char *file; | ||||
| 	int line; | ||||
| 	const char *func; | ||||
| 	const char *msg; | ||||
| }; | ||||
| 
 | ||||
| struct log_device; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct log_driver - a driver which accepts and processes log records | ||||
|  * | ||||
|  * @name: Name of driver | ||||
|  */ | ||||
| struct log_driver { | ||||
| 	const char *name; | ||||
| 	/**
 | ||||
| 	 * emit() - emit a log record | ||||
| 	 * | ||||
| 	 * Called by the log system to pass a log record to a particular driver | ||||
| 	 * for processing. The filter is checked before calling this function. | ||||
| 	 */ | ||||
| 	int (*emit)(struct log_device *ldev, struct log_rec *rec); | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct log_device - an instance of a log driver | ||||
|  * | ||||
|  * Since drivers are set up at build-time we need to have a separate device for | ||||
|  * the run-time aspects of drivers (currently just a list of filters to apply | ||||
|  * to records send to this device). | ||||
|  * | ||||
|  * @next_filter_num: Seqence number of next filter filter added (0=no filters | ||||
|  *	yet). This increments with each new filter on the device, but never | ||||
|  *	decrements | ||||
|  * @drv: Pointer to driver for this device | ||||
|  * @filter_head: List of filters for this device | ||||
|  * @sibling_node: Next device in the list of all devices | ||||
|  */ | ||||
| struct log_device { | ||||
| 	int next_filter_num; | ||||
| 	struct log_driver *drv; | ||||
| 	struct list_head filter_head; | ||||
| 	struct list_head sibling_node; | ||||
| }; | ||||
| 
 | ||||
| enum { | ||||
| 	LOGF_MAX_CATEGORIES = 5,	/* maximum categories per filter */ | ||||
| }; | ||||
| 
 | ||||
| enum log_filter_flags { | ||||
| 	LOGFF_HAS_CAT		= 1 << 0,	/* Filter has a category list */ | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct log_filter - criterial to filter out log messages | ||||
|  * | ||||
|  * @filter_num: Sequence number of this filter.  This is returned when adding a | ||||
|  *	new filter, and must be provided when removing a previously added | ||||
|  *	filter. | ||||
|  * @flags: Flags for this filter (LOGFF_...) | ||||
|  * @cat_list: List of categories to allow (terminated by LOGC_none). If empty | ||||
|  *	then all categories are permitted. Up to LOGF_MAX_CATEGORIES entries | ||||
|  *	can be provided | ||||
|  * @max_level: Maximum log level to allow | ||||
|  * @file_list: List of files to allow, separated by comma. If NULL then all | ||||
|  *	files are permitted | ||||
|  * @sibling_node: Next filter in the list of filters for this log device | ||||
|  */ | ||||
| struct log_filter { | ||||
| 	int filter_num; | ||||
| 	int flags; | ||||
| 	enum log_category_t cat_list[LOGF_MAX_CATEGORIES]; | ||||
| 	enum log_level_t max_level; | ||||
| 	const char *file_list; | ||||
| 	struct list_head sibling_node; | ||||
| }; | ||||
| 
 | ||||
| #define LOG_DRIVER(_name) \ | ||||
| 	ll_entry_declare(struct log_driver, _name, log_driver) | ||||
| 
 | ||||
| /**
 | ||||
|  * log_add_filter() - Add a new filter to a log device | ||||
|  * | ||||
|  * @drv_name: Driver name to add the filter to (since each driver only has a | ||||
|  *	single device) | ||||
|  * @cat_list: List of categories to allow (terminated by LOGC_none). If empty | ||||
|  *	then all categories are permitted. Up to LOGF_MAX_CATEGORIES entries | ||||
|  *	can be provided | ||||
|  * @max_level: Maximum log level to allow | ||||
|  * @file_list: List of files to allow, separated by comma. If NULL then all | ||||
|  *	files are permitted | ||||
|  * @return the sequence number of the new filter (>=0) if the filter was added, | ||||
|  *	or a -ve value on error | ||||
|  */ | ||||
| int log_add_filter(const char *drv_name, enum log_category_t cat_list[], | ||||
| 		   enum log_level_t max_level, const char *file_list); | ||||
| 
 | ||||
| /**
 | ||||
|  * log_remove_filter() - Remove a filter from a log device | ||||
|  * | ||||
|  * @drv_name: Driver name to remove the filter from (since each driver only has | ||||
|  *	a single device) | ||||
|  * @filter_num: Filter number to remove (as returned by log_add_filter()) | ||||
|  * @return 0 if the filter was removed, -ENOENT if either the driver or the | ||||
|  *	filter number was not found | ||||
|  */ | ||||
| int log_remove_filter(const char *drv_name, int filter_num); | ||||
| 
 | ||||
| #if CONFIG_IS_ENABLED(LOG) | ||||
| /**
 | ||||
|  * log_init() - Set up the log system ready for use | ||||
|  * | ||||
|  * @return 0 if OK, -ENOMEM if out of memory | ||||
|  */ | ||||
| int log_init(void); | ||||
| #else | ||||
| static inline int log_init(void) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue