summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTzung-Bi Shih <tzungbi@chromium.org>2019-02-27 13:04:24 +0800
committerCommit Bot <commit-bot@chromium.org>2019-09-19 07:59:05 +0000
commitaa17252161c5cc526555c2f82ad238361f7d22a0 (patch)
treea09f6825289a1fb7fb3c971215557651dcc50460
parent0beadf2ffd8b23e15d1e7c14de8719d0f45c9519 (diff)
downloadchrome-ec-aa17252161c5cc526555c2f82ad238361f7d22a0.tar.gz
audio_codec: add WoV abstract layer
Common logic for Wake-on-Voice. - set hotword detection model - get notifications from the chip - read audio data from the chip - use the audio data to detect hotword - send host event to AP if hotword is detected BRANCH=none BUG=b:122027734, b:123268236 TEST=1. define CONFIG_AUDIO_CODEC in board.h 2. define CONFIG_AUDIO_CODEC_DMIC in board.h 3. define CONFIG_AUDIO_CODEC_DMIC_SOFTWARE_GAIN in board.h 4. define CONFIG_AUDIO_CODEC_DMIC_MAX_SOFTWARE_GAIN in board.h 5. define CONFIG_AUDIO_CODEC_WOV in board.h 6. make BOARD=kukui_scp -j Change-Id: I26f7a8dbf9a6d57b1845fbb0666aa1d8285d9013 Signed-off-by: Tzung-Bi Shih <tzungbi@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1490800
-rw-r--r--common/audio_codec.c10
-rw-r--r--common/audio_codec_wov.c404
-rw-r--r--common/build.mk1
-rw-r--r--include/audio_codec.h75
-rw-r--r--include/config.h15
-rw-r--r--include/ec_commands.h69
6 files changed, 573 insertions, 1 deletions
diff --git a/common/audio_codec.c b/common/audio_codec.c
index 1dcc7e9b16..e21df5c039 100644
--- a/common/audio_codec.c
+++ b/common/audio_codec.c
@@ -10,7 +10,15 @@
#define CPRINTS(format, args...) cprints(CC_AUDIO_CODEC, format, ## args)
-static const uint32_t capabilities;
+static const uint32_t capabilities =
+ 0
+#ifdef CONFIG_AUDIO_CODEC_CAP_WOV_AUDIO_SHM
+ | BIT(EC_CODEC_CAP_WOV_AUDIO_SHM)
+#endif
+#ifdef CONFIG_AUDIO_CODEC_CAP_WOV_LANG_SHM
+ | BIT(EC_CODEC_CAP_WOV_LANG_SHM)
+#endif
+ ;
static struct {
uint8_t cap;
diff --git a/common/audio_codec_wov.c b/common/audio_codec_wov.c
new file mode 100644
index 0000000000..3609967d9d
--- /dev/null
+++ b/common/audio_codec_wov.c
@@ -0,0 +1,404 @@
+/*
+ * Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "audio_codec.h"
+#include "console.h"
+#include "host_command.h"
+#include "sha256.h"
+#include "system.h"
+#include "task.h"
+#include "util.h"
+
+#define CPRINTS(format, args...) cprints(CC_AUDIO_CODEC, format, ## args)
+
+/*
+ * To shorten the variable names, or the following code is likely to greater
+ * than 80 columns.
+ */
+#define AUDIO_BUF_LEN CONFIG_AUDIO_CODEC_WOV_AUDIO_BUF_LEN
+#define LANG_BUF_LEN CONFIG_AUDIO_CODEC_WOV_LANG_BUF_LEN
+
+static uint8_t lang_hash[SHA256_DIGEST_SIZE];
+static uint32_t lang_len;
+
+/*
+ * The variables below are shared between host command and WoV task. This lock
+ * is designed to protect them.
+ */
+static struct mutex lock;
+
+/*
+ * wov_enabled is shared.
+ *
+ * host command task:
+ * - is the only writer
+ * - no need to lock if read
+ */
+static uint8_t wov_enabled;
+
+/*
+ * hotword_detected is shared.
+ */
+static uint8_t hotword_detected;
+
+/*
+ * audio_buf_rp and audio_buf_wp are shared.
+ *
+ * Note that: sample width is 16-bit.
+ *
+ * Typical ring-buffer implementation:
+ * If audio_buf_rp == audio_buf_wp, empty.
+ * If (audio_buf_wp + 2) % buf_len == audio_buf_rp, full.
+ */
+static uint32_t audio_buf_rp, audio_buf_wp;
+
+static int is_buf_full(void)
+{
+ return ((audio_buf_wp + 2) % AUDIO_BUF_LEN) == audio_buf_rp;
+}
+
+static int check_lang_buf(uint8_t *data, uint32_t len, const uint8_t *hash)
+{
+ /*
+ * Note: sizeof(struct sha256_ctx) = 200 bytes
+ * should put into .bss, or stack is likely to overflow (~640 bytes)
+ */
+ static struct sha256_ctx ctx;
+ uint8_t *digest;
+ int i;
+ uint8_t *p = (uint8_t *)audio_codec_wov_lang_buf_addr;
+
+ SHA256_init(&ctx);
+ SHA256_update(&ctx, data, len);
+ digest = SHA256_final(&ctx);
+
+#ifdef DEBUG_AUDIO_CODEC
+ CPRINTS("data=%08x len=%d", data, len);
+ hexdump(digest, SHA256_DIGEST_SIZE);
+#endif
+
+ if (memcmp(digest, hash, SHA256_DIGEST_SIZE) != 0)
+ return EC_ERROR_UNKNOWN;
+
+ for (i = len; i < LANG_BUF_LEN; ++i)
+ if (p[i])
+ return EC_ERROR_UNKNOWN;
+
+ return EC_SUCCESS;
+}
+
+#ifdef CONFIG_AUDIO_CODEC_CAP_WOV_LANG_SHM
+static int wov_set_lang_shm(struct host_cmd_handler_args *args)
+{
+ const struct ec_param_ec_codec_wov *p = args->params;
+ const struct ec_param_ec_codec_wov_set_lang_shm *pp =
+ &p->set_lang_shm_param;
+
+ if (pp->total_len > LANG_BUF_LEN)
+ return EC_RES_INVALID_PARAM;
+ if (wov_enabled)
+ return EC_RES_BUSY;
+
+ if (check_lang_buf((uint8_t *)audio_codec_wov_lang_buf_addr,
+ pp->total_len, pp->hash) != EC_SUCCESS)
+ return EC_RES_ERROR;
+
+ memcpy(lang_hash, pp->hash, sizeof(lang_hash));
+ lang_len = pp->total_len;
+
+ args->response_size = 0;
+ return EC_RES_SUCCESS;
+}
+#else
+static int wov_set_lang(struct host_cmd_handler_args *args)
+{
+ const struct ec_param_ec_codec_wov *p = args->params;
+ const struct ec_param_ec_codec_wov_set_lang *pp = &p->set_lang_param;
+
+ if (pp->total_len > LANG_BUF_LEN)
+ return EC_RES_INVALID_PARAM;
+ if (pp->offset >= LANG_BUF_LEN)
+ return EC_RES_INVALID_PARAM;
+ if (pp->len > ARRAY_SIZE(pp->buf))
+ return EC_RES_INVALID_PARAM;
+ if (pp->offset + pp->len > pp->total_len)
+ return EC_RES_INVALID_PARAM;
+ if (wov_enabled)
+ return EC_RES_BUSY;
+
+ if (!pp->offset)
+ memset((uint8_t *)audio_codec_wov_lang_buf_addr,
+ 0, LANG_BUF_LEN);
+
+ memcpy((uint8_t *)audio_codec_wov_lang_buf_addr + pp->offset,
+ pp->buf, pp->len);
+
+ if (pp->offset + pp->len == pp->total_len) {
+ if (check_lang_buf((uint8_t *)audio_codec_wov_lang_buf_addr,
+ pp->total_len, pp->hash) != EC_SUCCESS)
+ return EC_RES_ERROR;
+
+ memcpy(lang_hash, pp->hash, sizeof(lang_hash));
+ lang_len = pp->total_len;
+ }
+
+ args->response_size = 0;
+ return EC_RES_SUCCESS;
+}
+#endif /* CONFIG_AUDIO_CODEC_CAP_WOV_LANG_SHM */
+
+static int wov_get_lang(struct host_cmd_handler_args *args)
+{
+ struct ec_response_ec_codec_wov_get_lang *r = args->response;
+
+ memcpy(r->hash, lang_hash, sizeof(r->hash));
+
+ args->response_size = sizeof(*r);
+ return EC_RES_SUCCESS;
+}
+
+static int wov_enable(struct host_cmd_handler_args *args)
+{
+ if (wov_enabled)
+ return EC_RES_BUSY;
+
+ if (audio_codec_wov_enable() != EC_SUCCESS)
+ return EC_RES_ERROR;
+
+ mutex_lock(&lock);
+ wov_enabled = 1;
+ hotword_detected = 0;
+ audio_buf_rp = audio_buf_wp = 0;
+ mutex_unlock(&lock);
+
+#ifdef HAS_TASK_WOV
+ task_wake(TASK_ID_WOV);
+#endif
+
+ args->response_size = 0;
+ return EC_RES_SUCCESS;
+}
+
+static int wov_disable(struct host_cmd_handler_args *args)
+{
+ if (!wov_enabled)
+ return EC_RES_BUSY;
+
+ if (audio_codec_wov_disable() != EC_SUCCESS)
+ return EC_RES_ERROR;
+
+ mutex_lock(&lock);
+ wov_enabled = 0;
+ hotword_detected = 0;
+ audio_buf_rp = audio_buf_wp = 0;
+ mutex_unlock(&lock);
+
+ args->response_size = 0;
+ return EC_RES_SUCCESS;
+}
+
+#ifdef CONFIG_AUDIO_CODEC_CAP_WOV_AUDIO_SHM
+static int wov_read_audio_shm(struct host_cmd_handler_args *args)
+{
+ struct ec_response_ec_codec_wov_read_audio_shm *r = args->response;
+
+ if (!wov_enabled)
+ return EC_RES_ACCESS_DENIED;
+
+ mutex_lock(&lock);
+ if (!hotword_detected) {
+ mutex_unlock(&lock);
+ return EC_RES_ACCESS_DENIED;
+ }
+
+ r->offset = audio_buf_rp;
+ if (audio_buf_rp <= audio_buf_wp)
+ r->len = audio_buf_wp - audio_buf_rp;
+ else
+ r->len = AUDIO_BUF_LEN - audio_buf_rp;
+
+ audio_buf_rp += r->len;
+ if (audio_buf_rp == AUDIO_BUF_LEN)
+ audio_buf_rp = 0;
+ mutex_unlock(&lock);
+
+#ifdef DEBUG_AUDIO_CODEC
+ if (!r->len)
+ CPRINTS("underrun detected");
+#endif
+
+ args->response_size = sizeof(*r);
+ return EC_RES_SUCCESS;
+}
+#else
+static int wov_read_audio(struct host_cmd_handler_args *args)
+{
+ struct ec_response_ec_codec_wov_read_audio *r = args->response;
+ uint8_t *p;
+
+ if (!wov_enabled)
+ return EC_RES_ACCESS_DENIED;
+
+ mutex_lock(&lock);
+ if (!hotword_detected) {
+ mutex_unlock(&lock);
+ return EC_RES_ACCESS_DENIED;
+ }
+
+ if (audio_buf_rp <= audio_buf_wp)
+ r->len = audio_buf_wp - audio_buf_rp;
+ else
+ r->len = AUDIO_BUF_LEN - audio_buf_rp;
+ r->len = MIN(sizeof(r->buf), r->len);
+
+ p = (uint8_t *)audio_codec_wov_audio_buf_addr + audio_buf_rp;
+
+ audio_buf_rp += r->len;
+ if (audio_buf_rp == AUDIO_BUF_LEN)
+ audio_buf_rp = 0;
+ mutex_unlock(&lock);
+
+#ifdef DEBUG_AUDIO_CODEC
+ if (!r->len)
+ CPRINTS("underrun detected");
+#endif
+ /*
+ * Note: it is possible to copy corrupted audio data if overrun
+ * happened at the point. To keep it simple and align to SHM mode,
+ * we ignore the case if overrun happened.
+ */
+ memcpy(r->buf, p, r->len);
+
+ args->response_size = sizeof(*r);
+ return EC_RES_SUCCESS;
+}
+#endif /* CONFIG_AUDIO_CODEC_CAP_WOV_AUDIO_SHM */
+
+static int (*sub_cmds[])(struct host_cmd_handler_args *) = {
+#ifdef CONFIG_AUDIO_CODEC_CAP_WOV_LANG_SHM
+ [EC_CODEC_WOV_SET_LANG_SHM] = wov_set_lang_shm,
+#else
+ [EC_CODEC_WOV_SET_LANG] = wov_set_lang,
+#endif
+ [EC_CODEC_WOV_GET_LANG] = wov_get_lang,
+ [EC_CODEC_WOV_ENABLE] = wov_enable,
+ [EC_CODEC_WOV_DISABLE] = wov_disable,
+#ifdef CONFIG_AUDIO_CODEC_CAP_WOV_AUDIO_SHM
+ [EC_CODEC_WOV_READ_AUDIO_SHM] = wov_read_audio_shm,
+#else
+ [EC_CODEC_WOV_READ_AUDIO] = wov_read_audio,
+#endif
+};
+
+#ifdef DEBUG_AUDIO_CODEC
+static char *strcmd[] = {
+#ifdef CONFIG_AUDIO_CODEC_CAP_WOV_LANG_SHM
+ [EC_CODEC_WOV_SET_LANG_SHM] = "EC_CODEC_WOV_SET_LANG_SHM",
+#else
+ [EC_CODEC_WOV_SET_LANG] = "EC_CODEC_WOV_SET_LANG",
+#endif
+ [EC_CODEC_WOV_GET_LANG] = "EC_CODEC_WOV_GET_LANG",
+ [EC_CODEC_WOV_ENABLE] = "EC_CODEC_WOV_ENABLE",
+ [EC_CODEC_WOV_DISABLE] = "EC_CODEC_WOV_DISABLE",
+#ifdef CONFIG_AUDIO_CODEC_CAP_WOV_AUDIO_SHM
+ [EC_CODEC_WOV_READ_AUDIO_SHM] = "EC_CODEC_WOV_READ_AUDIO_SHM",
+#else
+ [EC_CODEC_WOV_READ_AUDIO] = "EC_CODEC_WOV_READ_AUDIO",
+#endif
+};
+BUILD_ASSERT(ARRAY_SIZE(sub_cmds) == ARRAY_SIZE(strcmd));
+#endif
+
+static int wov_host_command(struct host_cmd_handler_args *args)
+{
+ const struct ec_param_ec_codec_wov *p = args->params;
+
+#ifdef DEBUG_AUDIO_CODEC
+ CPRINTS("WoV subcommand: %s", strcmd[p->cmd]);
+#endif
+
+ if (p->cmd < EC_CODEC_WOV_SUBCMD_COUNT && sub_cmds[p->cmd])
+ return sub_cmds[p->cmd](args);
+
+ return EC_RES_INVALID_PARAM;
+}
+DECLARE_HOST_COMMAND(EC_CMD_EC_CODEC_WOV, wov_host_command, EC_VER_MASK(0));
+
+/*
+ * Exported interfaces.
+ */
+void audio_codec_wov_task(void *arg)
+{
+ uint32_t n, req;
+ uint8_t *p;
+
+ while (1) {
+ mutex_lock(&lock);
+ if (!wov_enabled) {
+ mutex_unlock(&lock);
+ task_wait_event(-1);
+ continue;
+ }
+
+
+ /* Clear the buffer if full. */
+ if (is_buf_full()) {
+ audio_buf_wp = audio_buf_rp;
+
+#ifdef DEBUG_AUDIO_CODEC
+ if (hotword_detected)
+ CPRINTS("overrun detected");
+#endif
+ }
+
+ /*
+ * Note: sample width is 16-bit.
+ *
+ * The linear ring buffer wastes one sample bytes to
+ * detect buffer full.
+ *
+ * If buffer is empty, maximum req is BUF_LEN - 2.
+ * If wp > rp, wp can fill to the end of linear buffer.
+ * If wp < rp, wp can fill up to rp - 2.
+ */
+ if (audio_buf_wp == audio_buf_rp)
+ req = AUDIO_BUF_LEN - MAX(audio_buf_wp, 2);
+ else if (audio_buf_wp > audio_buf_rp)
+ req = AUDIO_BUF_LEN - audio_buf_wp;
+ else
+ req = audio_buf_rp - audio_buf_wp - 2;
+
+ p = (uint8_t *)audio_codec_wov_audio_buf_addr + audio_buf_wp;
+ mutex_unlock(&lock);
+
+ n = audio_codec_wov_read(p, req);
+ if (n < 0) {
+ CPRINTS("failed to read: %d", n);
+ break;
+ } else if (n == 0) {
+ if (audio_codec_wov_enable_notifier() != EC_SUCCESS) {
+ CPRINTS("failed to enable_notifier");
+ break;
+ }
+
+ task_wait_event(-1);
+ continue;
+ }
+
+ mutex_lock(&lock);
+ audio_buf_wp += n;
+ if (audio_buf_wp == AUDIO_BUF_LEN)
+ audio_buf_wp = 0;
+ mutex_unlock(&lock);
+
+ /*
+ * Reasons to sleep here:
+ * 1. read the audio data in a fixed pace (10ms)
+ * 2. yield the processor in case of watchdog thought EC crashed
+ */
+ task_wait_event(10 * MSEC);
+ }
+}
diff --git a/common/build.mk b/common/build.mk
index 19a55699d1..4475873eec 100644
--- a/common/build.mk
+++ b/common/build.mk
@@ -32,6 +32,7 @@ common-$(CONFIG_AP_HANG_DETECT)+=ap_hang_detect.o
common-$(CONFIG_AUDIO_CODEC)+=audio_codec.o
common-$(CONFIG_AUDIO_CODEC_DMIC)+=audio_codec_dmic.o
common-$(CONFIG_AUDIO_CODEC_I2S_RX)+=audio_codec_i2s_rx.o
+common-$(CONFIG_AUDIO_CODEC_WOV)+=audio_codec_wov.o
common-$(CONFIG_BACKLIGHT_LID)+=backlight_lid.o
common-$(CONFIG_BASE32)+=base32.o
common-$(CONFIG_DETACHABLE_BASE)+=base_state.o
diff --git a/include/audio_codec.h b/include/audio_codec.h
index af64deb16d..62009e6c73 100644
--- a/include/audio_codec.h
+++ b/include/audio_codec.h
@@ -176,4 +176,79 @@ int audio_codec_i2s_rx_set_daifmt(uint8_t daifmt);
*/
int audio_codec_i2s_rx_set_bclk(uint32_t bclk);
+
+/*
+ * WoV abstract layer
+ */
+
+/*
+ * Enables WoV.
+ *
+ * Returns:
+ * EC_SUCCESS if success.
+ * EC_ERROR_UNKNOWN if internal error.
+ * EC_ERROR_BUSY if has enabled.
+ */
+int audio_codec_wov_enable(void);
+
+/*
+ * Disables WoV.
+ *
+ * Returns:
+ * EC_SUCCESS if success.
+ * EC_ERROR_UNKNOWN if internal error.
+ * EC_ERROR_BUSY if has not enabled.
+ */
+int audio_codec_wov_disable(void);
+
+/*
+ * Reads the WoV audio data from chip.
+ *
+ * @buf is the target pointer to put the data.
+ * @count is the maximum number of bytes to read.
+ *
+ * Returns:
+ * -1 if any errors.
+ * 0 if no data.
+ * >0 if success. The returned value denotes number of bytes read.
+ */
+int32_t audio_codec_wov_read(void *buf, uint32_t count);
+
+/*
+ * Enables notification if WoV audio data is available.
+ *
+ * Returns:
+ * EC_SUCCESS if success.
+ * EC_ERROR_UNKNOWN if internal error.
+ * EC_ERROR_BUSY if has enabled.
+ * EC_ERROR_ACCESS_DENIED if the notifiee has not set.
+ */
+int audio_codec_wov_enable_notifier(void);
+
+/*
+ * Disables WoV data notification.
+ *
+ * Returns:
+ * EC_SUCCESS if success.
+ * EC_ERROR_UNKNOWN if internal error.
+ * EC_ERROR_BUSY if has not enabled.
+ * EC_ERROR_ACCESS_DENIED if the notifiee has not set.
+ */
+int audio_codec_wov_disable_notifier(void);
+
+/*
+ * Audio buffer for 2 seconds S16_LE, 16kHz, mono.
+ */
+extern uintptr_t audio_codec_wov_audio_buf_addr;
+
+/*
+ * Language model buffer for speech-micro. At least 67KB.
+ */
+extern uintptr_t audio_codec_wov_lang_buf_addr;
+
+/*
+ * Task for running WoV.
+ */
+void audio_codec_wov_task(void *arg);
+
#endif
diff --git a/include/config.h b/include/config.h
index 6483704444..bf74c03b4f 100644
--- a/include/config.h
+++ b/include/config.h
@@ -352,6 +352,9 @@
/* Support audio codec. */
#undef CONFIG_AUDIO_CODEC
+/* Audio codec caps. */
+#undef CONFIG_AUDIO_CODEC_CAP_WOV_AUDIO_SHM
+#undef CONFIG_AUDIO_CODEC_CAP_WOV_LANG_SHM
/* Support audio codec on DMIC. */
#undef CONFIG_AUDIO_CODEC_DMIC
/* Support audio codec software gain on DMIC. */
@@ -359,6 +362,13 @@
#undef CONFIG_AUDIO_CODEC_DMIC_MAX_SOFTWARE_GAIN
/* Support audio codec on I2S RX. */
#undef CONFIG_AUDIO_CODEC_I2S_RX
+/* Support audio codec on WoV. */
+#undef CONFIG_AUDIO_CODEC_WOV
+/* Audio codec buffers. */
+#undef CONFIG_AUDIO_CODEC_WOV_AUDIO_BUF_LEN
+#undef CONFIG_AUDIO_CODEC_WOV_AUDIO_BUF_TYPE
+#undef CONFIG_AUDIO_CODEC_WOV_LANG_BUF_LEN
+#undef CONFIG_AUDIO_CODEC_WOV_LANG_BUF_TYPE
/* Allow proprietary communication protocols' extensions. */
#undef CONFIG_EXTENSION_COMMAND
@@ -4942,4 +4952,9 @@
#endif /* CONFIG_USB_PD_DISCHARGE_GPIO */
#endif /* CONFIG_USB_PD_DISCHARGE */
+/* EC Codec Wake-on-Voice related definitions */
+#ifdef CONFIG_AUDIO_CODEC_WOV
+#define CONFIG_SHA256
+#endif
+
#endif /* __CROS_EC_CONFIG_H */
diff --git a/include/ec_commands.h b/include/ec_commands.h
index e553ecdfa9..30783bc1bb 100644
--- a/include/ec_commands.h
+++ b/include/ec_commands.h
@@ -641,6 +641,9 @@ enum host_event_code {
/* Keyboard recovery combo with hardware reinitialization */
EC_HOST_EVENT_KEYBOARD_RECOVERY_HW_REINIT = 30,
+ /* WoV */
+ EC_HOST_EVENT_WOV = 31,
+
/*
* The high bit of the event mask is not used as a host event code. If
* it reads back as set, then the entire event mask should be
@@ -4670,10 +4673,14 @@ enum ec_codec_subcmd {
};
enum ec_codec_cap {
+ EC_CODEC_CAP_WOV_AUDIO_SHM = 0,
+ EC_CODEC_CAP_WOV_LANG_SHM = 1,
EC_CODEC_CAP_LAST = 32,
};
enum ec_codec_shm_id {
+ EC_CODEC_SHM_ID_WOV_AUDIO = 0x0,
+ EC_CODEC_SHM_ID_WOV_LANG = 0x1,
EC_CODEC_SHM_ID_LAST,
};
@@ -4835,6 +4842,68 @@ struct __ec_align4 ec_param_ec_codec_i2s_rx {
};
/*****************************************************************************/
+/* Commands for WoV on audio codec. */
+
+#define EC_CMD_EC_CODEC_WOV 0x00BF
+
+enum ec_codec_wov_subcmd {
+ EC_CODEC_WOV_SET_LANG = 0x0,
+ EC_CODEC_WOV_SET_LANG_SHM = 0x1,
+ EC_CODEC_WOV_GET_LANG = 0x2,
+ EC_CODEC_WOV_ENABLE = 0x3,
+ EC_CODEC_WOV_DISABLE = 0x4,
+ EC_CODEC_WOV_READ_AUDIO = 0x5,
+ EC_CODEC_WOV_READ_AUDIO_SHM = 0x6,
+ EC_CODEC_WOV_SUBCMD_COUNT,
+};
+
+/*
+ * @hash is SHA256 of the whole language model.
+ * @total_len indicates the length of whole language model.
+ * @offset is the cursor from the beginning of the model.
+ * @buf is the packet buffer.
+ * @len denotes how many bytes in the buf.
+ */
+struct __ec_align4 ec_param_ec_codec_wov_set_lang {
+ uint8_t hash[32];
+ uint32_t total_len;
+ uint32_t offset;
+ uint8_t buf[128];
+ uint32_t len;
+};
+
+struct __ec_align4 ec_param_ec_codec_wov_set_lang_shm {
+ uint8_t hash[32];
+ uint32_t total_len;
+};
+
+struct __ec_align4 ec_param_ec_codec_wov {
+ uint8_t cmd; /* enum ec_codec_wov_subcmd */
+ uint8_t reserved[3];
+
+ union {
+ struct ec_param_ec_codec_wov_set_lang
+ set_lang_param;
+ struct ec_param_ec_codec_wov_set_lang_shm
+ set_lang_shm_param;
+ };
+};
+
+struct __ec_align4 ec_response_ec_codec_wov_get_lang {
+ uint8_t hash[32];
+};
+
+struct __ec_align4 ec_response_ec_codec_wov_read_audio {
+ uint8_t buf[128];
+ uint32_t len;
+};
+
+struct __ec_align4 ec_response_ec_codec_wov_read_audio_shm {
+ uint32_t offset;
+ uint32_t len;
+};
+
+/*****************************************************************************/
/* System commands */
/*