/* SPDX-License-Identifier: GPL-2.0 */ /** \file filter_common.c Common helper functions for filter input/output. */ #include #include #include "filter_cmd.lsg.h" #include "para.h" #include "list.h" #include "sched.h" #include "fd.h" #include "buffer_tree.h" #include "filter.h" #include "error.h" #include "string.h" /** Iterate over all filters. */ #define FOR_EACH_FILTER(j) for (j = 1; FILTER_CMD(j); j++) /** * Obtain a reference to a filter structure. * * \param filter_num Between zero and NUM_SUPPORTED_FILTERS, inclusively. * * \return Pointer to the filter identified by the given filter number, or * NULL if the filter number is out of range. * * \sa filter_name(). */ const struct filter *filter_get(int filter_num) { if (filter_num < 1 || filter_num > LSG_NUM_FILTER_CMD_SUBCOMMANDS) return NULL; return lls_user_data(FILTER_CMD(filter_num)); } static inline bool filter_supported(int filter_num) { return lls_user_data(FILTER_CMD(filter_num)); } /** * Return the name of a filter, given its number. * * \param filter_num See \ref filter_get(). * * \return A pointer to a string literal, or NULL if filter_num is out of * range. The caller must not attempt to call free(3) on the returned pointer. */ const char *filter_name(int filter_num) { if (filter_num < 1 || filter_num > LSG_NUM_FILTER_CMD_SUBCOMMANDS) return NULL; return lls_command_name(FILTER_CMD(filter_num)); } /** * Parse a filter command line and call the corresponding ->setup method. * * \param fa The filter argument. * \param conf Points to filter-specific setup upon successful return. * \param lprp Parsed command line options are returned here. * * Check if the given filter argument starts with the name of a supported * filter, optionally followed by options for this filter. If yes, call the * command line parser of that filter and its ->setup method. * * \return On success, the number of the filter is returned and conf is * initialized to point to the filter configuration as returned by the filter's * ->setup() method, if any. Moreover, *lprp is initialized to contain the * parsed command line options. On errors a negative paraslash error code is * returned. */ int filter_setup(const char *fa, void **conf, struct lls_parse_result **lprp) { int ret, filter_num, argc; char *errctx = NULL, **argv; const struct lls_command *cmd; const struct filter *f; *lprp = NULL; ret = create_argv(fa, " \t\n", &argv); if (ret < 0) return ret; argc = ret; ret = lls(lls_lookup_subcmd(argv[0], filter_cmd_suite, &errctx)); if (ret < 0) goto free_argv; filter_num = ret; cmd = FILTER_CMD(filter_num); if (!filter_supported(filter_num)) { ret = -E_UNSUPPORTED_FILTER; errctx = make_message("bad filter name: %s", lls_command_name(cmd)); goto free_argv; } ret = lls(lls_parse(argc, argv, cmd, lprp, &errctx)); if (ret < 0) goto free_argv; f = filter_get(filter_num); assert(f); *conf = f->setup? f->setup(*lprp) : NULL; ret = filter_num; free_argv: free_argv(argv); if (ret >= 0) return ret; if (errctx) PARA_ERROR_LOG("%s\n", errctx); free(errctx); return ret; } /** * Print help text of each filter to stdout. * * \param detailed Whether to print short or long help. */ void print_filter_helps(bool detailed) { int i, num = 0; printf("\nAvailable filters: "); FOR_EACH_FILTER(i) { if (!filter_supported(i)) continue; if (num > 50) { printf("\n "); num = 0; } num += printf("%s%s", i? " " : "", filter_name(i)); } printf("\n"); FOR_EACH_FILTER(i) { const struct lls_command *cmd = FILTER_CMD(i); char *help; if (!filter_supported(i)) continue; help = detailed? lls_long_help(cmd) : lls_short_help(cmd); if (!help) continue; printf("%s\n", help); free(help); } } /** * Print a short summary of all available filters to stdout. * * For each supported filter, the filter name and the purpose text is printed * in a single line. Since no options are shown, the filter list is more * concise than the text obtained from print_filter_helps(). */ void print_filter_list(void) { int i; printf("Available filters:\n"); FOR_EACH_FILTER(i) { const struct lls_command *cmd = FILTER_CMD(i); if (!filter_supported(i)) continue; printf("%-9s %s\n", filter_name(i), lls_purpose(cmd)); } } /** * Request a minimal timeout if not idle. * * \param s The scheduler instance. * \param context Pointer to the filter node. * * If the buffer tree node of the given filter node has data available (or is * in error state) a minimal I/O timeout is requested from the scheduler. * Otherwise the function does nothing. */ void generic_filter_pre_monitor(struct sched *s, void *context) { struct filter_node *fn = context; if (btr_node_status(fn->btrn, fn->min_iqs, BTR_NT_INTERNAL) != 0) sched_min_delay(s); } #ifdef WORDS_BIGENDIAN #define DECODER_SAMPLE_FORMAT SF_S16_BE #else #define DECODER_SAMPLE_FORMAT SF_S16_LE #endif /** * Execute a btr command for a decoder. * * The buffer tree nodes of the writers ask the parent nodes about sample_rate, * channels count and sample format. This function is called by all decoders to * answer these queries. * * \param cmd The command to be executed by the child node. * \param sample_rate Known to the decoder. * \param channels Known to the decoder. * \param result Ascii representation on the answer is stored here. * * \return Standard. */ int decoder_execute(const char *cmd, unsigned sample_rate, unsigned channels, char **result) { if (!strcmp(cmd, "sample_rate")) { if (sample_rate == 0) return -E_BTR_NAVAIL; *result = make_message("%u", sample_rate); return 1; } if (!strcmp(cmd, "channels")) { if (channels == 0) return -E_BTR_NAVAIL; *result = make_message("%u", channels); return 1; } if (!strcmp(cmd, "sample_format")) { *result = make_message("%d", DECODER_SAMPLE_FORMAT); return 1; } return -ERRNO_TO_PARA_ERROR(ENOTSUP); }