From 9fd0185c3b40239d1144b0a484445b10d05ce7b2 Mon Sep 17 00:00:00 2001 From: Benjamin Lemouzy Date: Fri, 5 May 2023 08:56:09 +0200 Subject: [PATCH] drivers: audio: add Audio Codec shell commands Add shell commands to start, stop and set properties of an Audio Codec device. Signed-off-by: Benjamin Lemouzy --- drivers/audio/CMakeLists.txt | 1 + drivers/audio/Kconfig | 7 ++ drivers/audio/codec_shell.c | 197 +++++++++++++++++++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 drivers/audio/codec_shell.c diff --git a/drivers/audio/CMakeLists.txt b/drivers/audio/CMakeLists.txt index b1802f9cf26..98544d6218c 100644 --- a/drivers/audio/CMakeLists.txt +++ b/drivers/audio/CMakeLists.txt @@ -7,3 +7,4 @@ zephyr_library_sources_ifdef(CONFIG_AUDIO_MPXXDTYY mpxxdtyy.c) zephyr_library_sources_ifdef(CONFIG_AUDIO_MPXXDTYY mpxxdtyy-i2s.c) zephyr_library_sources_ifdef(CONFIG_AUDIO_DMIC_NRFX_PDM dmic_nrfx_pdm.c) zephyr_library_sources_ifdef(CONFIG_AUDIO_TAS6422DAC tas6422dac.c) +zephyr_library_sources_ifdef(CONFIG_AUDIO_CODEC_SHELL codec_shell.c) diff --git a/drivers/audio/Kconfig b/drivers/audio/Kconfig index a45e003f80a..308bbb3172a 100644 --- a/drivers/audio/Kconfig +++ b/drivers/audio/Kconfig @@ -25,6 +25,13 @@ config AUDIO_CODEC_INIT_PRIORITY help Audio codec device driver initialization priority. +config AUDIO_CODEC_SHELL + bool "Audio Codec shell" + default y + depends on SHELL + help + Enable the Audio Codec shell with Audio Codec related commands. + module = AUDIO_CODEC module-str = audio codec source "subsys/logging/Kconfig.template.log_config" diff --git a/drivers/audio/codec_shell.c b/drivers/audio/codec_shell.c new file mode 100644 index 00000000000..6fb47d11184 --- /dev/null +++ b/drivers/audio/codec_shell.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2023 Centralp + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define CODEC_START_HELP \ + "Start output audio playback. Syntax:\n" \ + "" + +#define CODEC_STOP_HELP \ + "Stop output audio playback. Syntax:\n" \ + "" + +#define CODEC_SET_PROP_HELP \ + "Set a codec property. Syntax:\n" \ + " " + +#define CODEC_APPLY_PROP_HELP \ + "Apply any cached properties. Syntax:\n" \ + "" + +static const char *const codec_property_name[] = { + [AUDIO_PROPERTY_OUTPUT_VOLUME] = "volume", + [AUDIO_PROPERTY_OUTPUT_MUTE] = "mute", +}; + +static const char *const codec_channel_name[] = { + [AUDIO_CHANNEL_FRONT_LEFT] = "front_left", + [AUDIO_CHANNEL_FRONT_RIGHT] = "front_right", + [AUDIO_CHANNEL_LFE] = "lfe", + [AUDIO_CHANNEL_FRONT_CENTER] = "front_center", + [AUDIO_CHANNEL_REAR_LEFT] = "rear_left", + [AUDIO_CHANNEL_REAR_RIGHT] = "rear_right", + [AUDIO_CHANNEL_REAR_CENTER] = "rear_center", + [AUDIO_CHANNEL_SIDE_LEFT] = "side_left", + [AUDIO_CHANNEL_SIDE_RIGHT] = "side_right", + [AUDIO_CHANNEL_ALL] = "all", +}; + +struct args_index { + uint8_t device; + uint8_t property; + uint8_t channel; + uint8_t value; +}; + +static const struct args_index args_indx = { + .device = 1, + .property = 2, + .channel = 3, + .value = 4, +}; + +static int parse_named_int(const char *name, const char *keystack[], size_t count) +{ + char *endptr; + int i; + + /* Attempt to parse name as a number first */ + i = strtoul(name, &endptr, 0); + if (*endptr == '\0') { + return i; + } + + /* Name is not a number, look it up */ + for (i = 0; i < count; i++) { + if (strcmp(name, keystack[i]) == 0) { + return i; + } + } + + return -ENOTSUP; +} + +static int cmd_start(const struct shell *sh, size_t argc, char *argv[]) +{ + const struct device *dev; + + dev = device_get_binding(argv[args_indx.device]); + if (!dev) { + shell_error(sh, "Audio Codec device not found"); + return -ENODEV; + } + audio_codec_start_output(dev); + + return 0; +} + +static int cmd_stop(const struct shell *sh, size_t argc, char *argv[]) +{ + const struct device *dev; + + dev = device_get_binding(argv[args_indx.device]); + if (!dev) { + shell_error(sh, "Audio Codec device not found"); + return -ENODEV; + } + audio_codec_stop_output(dev); + + return 0; +} + +static int cmd_set_prop(const struct shell *sh, size_t argc, char *argv[]) +{ + const struct device *dev; + int property; + int channel; + long value; + char *endptr; + audio_property_value_t property_value; + + dev = device_get_binding(argv[args_indx.device]); + if (!dev) { + shell_error(sh, "Audio Codec device not found"); + return -ENODEV; + } + + property = parse_named_int(argv[args_indx.property], codec_property_name, + ARRAY_SIZE(codec_property_name)); + if (property < 0) { + shell_error(sh, "Property '%s' unknown", argv[args_indx.property]); + return -EINVAL; + } + + channel = parse_named_int(argv[args_indx.channel], codec_channel_name, + ARRAY_SIZE(codec_channel_name)); + if (channel < 0) { + shell_error(sh, "Channel '%s' unknown", argv[args_indx.channel]); + return -EINVAL; + } + + value = strtol(argv[args_indx.value], &endptr, 0); + if (*endptr != '\0') { + return -EINVAL; + } + if (value > INT32_MAX || value < INT32_MIN) { + return -EINVAL; + } + switch (property) { + case AUDIO_PROPERTY_OUTPUT_VOLUME: + property_value.vol = value; + break; + case AUDIO_PROPERTY_OUTPUT_MUTE: + property_value.mute = value; + break; + default: + return -EINVAL; + } + + return audio_codec_set_property(dev, property, channel, property_value); +} + +static int cmd_apply_prop(const struct shell *sh, size_t argc, char *argv[]) +{ + const struct device *dev; + + dev = device_get_binding(argv[args_indx.device]); + if (!dev) { + shell_error(sh, "Audio Codec device not found"); + return -ENODEV; + } + + return audio_codec_apply_properties(dev); +} + +/* Device name autocompletion support */ +static void device_name_get(size_t idx, struct shell_static_entry *entry) +{ + const struct device *dev = shell_device_lookup(idx, NULL); + + entry->syntax = (dev != NULL) ? dev->name : NULL; + entry->handler = NULL; + entry->help = NULL; + entry->subcmd = NULL; +} + +SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get); + +/* clang-format off */ +SHELL_STATIC_SUBCMD_SET_CREATE(sub_codec, + SHELL_CMD_ARG(start, &dsub_device_name, CODEC_START_HELP, cmd_start, + 2, 0), + SHELL_CMD_ARG(stop, &dsub_device_name, CODEC_STOP_HELP, cmd_stop, + 2, 0), + SHELL_CMD_ARG(set_prop, &dsub_device_name, CODEC_SET_PROP_HELP, cmd_set_prop, + 5, 0), + SHELL_CMD_ARG(apply_prop, &dsub_device_name, CODEC_APPLY_PROP_HELP, cmd_apply_prop, + 2, 0), + SHELL_SUBCMD_SET_END +); +/* clang-format on */ + +SHELL_CMD_REGISTER(codec, &sub_codec, "Audio Codec commands", NULL);