Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/firmware/cirrus/test/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 99 kB image not shown  

Quelle  cs_dsp_test_control_rw.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
//
// KUnit tests for cs_dsp.
//
// Copyright (C) 2024 Cirrus Logic, Inc. and
//                    Cirrus Logic International Semiconductor Ltd.

#include <kunit/device.h>
#include <kunit/resource.h>
#include <kunit/test.h>
#include <linux/build_bug.h>
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/firmware/cirrus/cs_dsp_test_utils.h>
#include <linux/firmware/cirrus/wmfw.h>
#include <linux/list.h>
#include <linux/random.h>
#include <linux/regmap.h>

KUNIT_DEFINE_ACTION_WRAPPER(_put_device_wrapper, put_device, struct device *);
KUNIT_DEFINE_ACTION_WRAPPER(_cs_dsp_stop_wrapper, cs_dsp_stop, struct cs_dsp *);
KUNIT_DEFINE_ACTION_WRAPPER(_cs_dsp_remove_wrapper, cs_dsp_remove, struct cs_dsp *);

struct cs_dsp_test_local {
 struct cs_dsp_mock_xm_header *xm_header;
 struct cs_dsp_mock_wmfw_builder *wmfw_builder;
 int wmfw_version;
};

struct cs_dsp_ctl_rw_test_param {
 int mem_type;
 int alg_id;
 unsigned int offs_words;
 unsigned int len_bytes;
 u16 ctl_type;
 u16 flags;
};

static const struct cs_dsp_mock_alg_def cs_dsp_ctl_rw_test_algs[] = {
 {
  .id = 0xfafa,
  .ver = 0x100000,
  .xm_base_words = 60,
  .xm_size_words = 1000,
  .ym_base_words = 0,
  .ym_size_words = 1000,
  .zm_base_words = 0,
  .zm_size_words = 1000,
 },
 {
  .id = 0xb,
  .ver = 0x100001,
  .xm_base_words = 1060,
  .xm_size_words = 1000,
  .ym_base_words = 1000,
  .ym_size_words = 1000,
  .zm_base_words = 1000,
  .zm_size_words = 1000,
 },
 {
  .id = 0x9f1234,
  .ver = 0x100500,
  .xm_base_words = 2060,
  .xm_size_words = 32,
  .ym_base_words = 2000,
  .ym_size_words = 32,
  .zm_base_words = 2000,
  .zm_size_words = 32,
 },
 {
  .id = 0xff00ff,
  .ver = 0x300113,
  .xm_base_words = 2100,
  .xm_size_words = 32,
  .ym_base_words = 2032,
  .ym_size_words = 32,
  .zm_base_words = 2032,
  .zm_size_words = 32,
 },
};

static const struct cs_dsp_mock_coeff_def mock_coeff_template = {
 .shortname = "Dummy Coeff",
 .type = WMFW_CTL_TYPE_BYTES,
 .mem_type = WMFW_ADSP2_YM,
 .flags = WMFW_CTL_FLAG_READABLE | WMFW_CTL_FLAG_WRITEABLE,
 .length_bytes = 4,
};

static int _find_alg_entry(struct kunit *test, unsigned int alg_id)
{
 int i;

 for (i = 0; i < ARRAY_SIZE(cs_dsp_ctl_rw_test_algs); ++i) {
  if (cs_dsp_ctl_rw_test_algs[i].id == alg_id)
   break;
 }

 KUNIT_ASSERT_LT(test, i, ARRAY_SIZE(cs_dsp_ctl_rw_test_algs));

 return i;
}

static int _get_alg_mem_base_words(struct kunit *test, int alg_index, int mem_type)
{
 switch (mem_type) {
 case WMFW_ADSP2_XM:
  return cs_dsp_ctl_rw_test_algs[alg_index].xm_base_words;
 case WMFW_ADSP2_YM:
  return cs_dsp_ctl_rw_test_algs[alg_index].ym_base_words;
 case WMFW_ADSP2_ZM:
  return cs_dsp_ctl_rw_test_algs[alg_index].zm_base_words;
 default:
  KUNIT_FAIL(test, "Bug in test: illegal memory type %d\n", mem_type);
  return 0;
 }
}

static struct cs_dsp_mock_wmfw_builder *_create_dummy_wmfw(struct kunit *test)
{
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp_mock_wmfw_builder *builder;

 builder = cs_dsp_mock_wmfw_init(priv, local->wmfw_version);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, builder);

 /* Init an XM header */
 cs_dsp_mock_wmfw_add_data_block(builder,
     WMFW_ADSP2_XM, 0,
     local->xm_header->blob_data,
     local->xm_header->blob_size_bytes);

 return builder;
}

/*
 * Write to a control while the firmware is running.
 * This should write to the underlying registers.
 */

static void cs_dsp_ctl_write_running(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals, *readback;

 reg_vals = kunit_kzalloc(test, param->len_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 readback = kunit_kzalloc(test, param->len_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 memset(reg_vals, 0, param->len_bytes);
 regmap_raw_write(dsp->regmap, reg, reg_vals, param->len_bytes);

 /* Create control pointing to this data */
 def.flags = param->flags;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = param->len_bytes;

 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);

 /* Start the firmware and add an action to stop it during cleanup */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, _cs_dsp_stop_wrapper, dsp), 0);

 /*
 * Write new data to the control, it should be written to the registers
 * and cs_dsp_coeff_lock_and_write_ctrl() should return 1 to indicate
 * that the control content changed.
 */

 get_random_bytes(reg_vals, param->len_bytes);
 KUNIT_EXPECT_EQ(test,
   cs_dsp_coeff_lock_and_write_ctrl(ctl, 0, reg_vals, param->len_bytes),
   1);
 KUNIT_ASSERT_EQ(test, regmap_raw_read(dsp->regmap, reg, readback, param->len_bytes), 0);
 KUNIT_EXPECT_MEMEQ(test, readback, reg_vals, param->len_bytes);

 /* Drop expected writes and the regmap cache should be clean */
 cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
 cs_dsp_mock_regmap_drop_bytes(priv, reg, param->len_bytes);
 KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Read from a volatile control while the firmware is running.
 * This should return the current state of the underlying registers.
 */

static void cs_dsp_ctl_read_volatile_running(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals, *readback;

 reg_vals = kunit_kzalloc(test, param->len_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 readback = kunit_kzalloc(test, param->len_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 memset(reg_vals, 0, param->len_bytes);
 regmap_raw_write(dsp->regmap, reg, reg_vals, param->len_bytes);

 /* Create control pointing to this data */
 def.flags = param->flags | WMFW_CTL_FLAG_VOLATILE;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = param->len_bytes;

 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);

 /* Start the firmware and add an action to stop it during cleanup */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, _cs_dsp_stop_wrapper, dsp), 0);

 /* Read the control, it should return the current register content */
 KUNIT_EXPECT_EQ(test,
   cs_dsp_coeff_lock_and_read_ctrl(ctl, 0, readback, param->len_bytes),
   0);
 KUNIT_EXPECT_MEMEQ(test, readback, reg_vals, param->len_bytes);

 /*
 * Change the register content and read the control, it should return
 * the new register content
 */

 get_random_bytes(reg_vals, param->len_bytes);
 KUNIT_ASSERT_EQ(test, regmap_raw_write(dsp->regmap, reg, reg_vals, param->len_bytes), 0);
 KUNIT_EXPECT_EQ(test,
   cs_dsp_coeff_lock_and_read_ctrl(ctl, 0, readback, param->len_bytes),
   0);
 KUNIT_EXPECT_MEMEQ(test, readback, reg_vals, param->len_bytes);
}

/*
 * Read from a volatile control before the firmware is started.
 * This should return an error.
 */

static void cs_dsp_ctl_read_volatile_not_started(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals;

 reg_vals = kunit_kzalloc(test, param->len_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 regmap_raw_write(dsp->regmap, reg, reg_vals, param->len_bytes);

 /* Create control pointing to this data */
 def.flags = param->flags | WMFW_CTL_FLAG_VOLATILE;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = param->len_bytes;

 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 /* Read the control, it should return an error */
 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);
 KUNIT_EXPECT_LT(test,
   cs_dsp_coeff_lock_and_read_ctrl(ctl, 0, reg_vals, param->len_bytes),
   0);
}

/*
 * Read from a volatile control after the firmware has stopped.
 * This should return an error.
 */

static void cs_dsp_ctl_read_volatile_stopped(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals;

 reg_vals = kunit_kzalloc(test, param->len_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 regmap_raw_write(dsp->regmap, reg, reg_vals, param->len_bytes);

 /* Create control pointing to this data */
 def.flags = param->flags | WMFW_CTL_FLAG_VOLATILE;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = param->len_bytes;

 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 /* Start and stop the firmware */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 cs_dsp_stop(dsp);

 /* Read the control, it should return an error */
 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);
 KUNIT_EXPECT_LT(test,
   cs_dsp_coeff_lock_and_read_ctrl(ctl, 0, reg_vals, param->len_bytes),
   0);
}

/*
 * Read from a volatile control after the DSP has been powered down.
 * This should return an error.
 */

static void cs_dsp_ctl_read_volatile_stopped_powered_down(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals;

 reg_vals = kunit_kzalloc(test, param->len_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 regmap_raw_write(dsp->regmap, reg, reg_vals, param->len_bytes);

 /* Create control pointing to this data */
 def.flags = param->flags | WMFW_CTL_FLAG_VOLATILE;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = param->len_bytes;

 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 /* Start and stop the firmware then power down */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 cs_dsp_stop(dsp);
 cs_dsp_power_down(dsp);

 /* Read the control, it should return an error */
 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);
 KUNIT_EXPECT_LT(test,
   cs_dsp_coeff_lock_and_read_ctrl(ctl, 0, reg_vals, param->len_bytes),
   0);
}

/*
 * Read from a volatile control when a different firmware is currently
 * loaded into the DSP.
 * Should return an error.
 */

static void cs_dsp_ctl_read_volatile_not_current_loaded_fw(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 struct cs_dsp_mock_wmfw_builder *builder2 = _create_dummy_wmfw(test);
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals;

 reg_vals = kunit_kmalloc(test, param->len_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 /* Create some DSP data to be read into the control cache */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 regmap_raw_write(dsp->regmap, reg, reg_vals, param->len_bytes);

 /* Create control pointing to this data */
 def.flags = param->flags | WMFW_CTL_FLAG_VOLATILE;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = param->len_bytes;

 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 /* Power-up DSP */
 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 /* Power-down DSP then power-up with a different firmware */
 cs_dsp_power_down(dsp);
 wmfw = cs_dsp_mock_wmfw_get_firmware(builder2);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw2", NULL, NULL, "mbc.vss"), 0);

 /* Read the control, it should return an error */
 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);
 KUNIT_EXPECT_LT(test,
   cs_dsp_coeff_lock_and_read_ctrl(ctl, 0, reg_vals, param->len_bytes),
   0);
}

/*
 * Read from a volatile control when a different firmware is currently
 * running.
 * Should return an error.
 */

static void cs_dsp_ctl_read_volatile_not_current_running_fw(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 struct cs_dsp_mock_wmfw_builder *builder2 = _create_dummy_wmfw(test);
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals;

 reg_vals = kunit_kmalloc(test, param->len_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 /* Create some DSP data to be read into the control cache */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 regmap_raw_write(dsp->regmap, reg, reg_vals, param->len_bytes);

 /* Create control pointing to this data */
 def.flags = param->flags | WMFW_CTL_FLAG_VOLATILE;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = param->len_bytes;

 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 /* Power-up DSP */
 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 /* Power-down DSP then power-up with a different firmware */
 cs_dsp_power_down(dsp);
 wmfw = cs_dsp_mock_wmfw_get_firmware(builder2);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw2", NULL, NULL, "mbc.vss"), 0);

 /* Start the firmware and add an action to stop it during cleanup */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, _cs_dsp_stop_wrapper, dsp), 0);

 /* Read the control, it should return an error */
 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);
 KUNIT_EXPECT_LT(test,
   cs_dsp_coeff_lock_and_read_ctrl(ctl, 0, reg_vals, param->len_bytes),
   0);
}

/*
 * Write to a volatile control before the firmware is started.
 * This should return an error.
 */

static void cs_dsp_ctl_write_volatile_not_started(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals;

 reg_vals = kunit_kzalloc(test, param->len_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 regmap_raw_write(dsp->regmap, reg, reg_vals, param->len_bytes);

 /* Create control pointing to this data */
 def.flags = param->flags | WMFW_CTL_FLAG_VOLATILE;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = param->len_bytes;

 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 /* Drop expected writes and the regmap cache should be clean */
 cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
 cs_dsp_mock_regmap_drop_bytes(priv, reg, param->len_bytes);

 /* Write the control, it should return an error */
 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);
 KUNIT_EXPECT_LT(test,
   cs_dsp_coeff_lock_and_write_ctrl(ctl, 0, reg_vals, param->len_bytes),
   0);

 /* Should not have been any writes to registers */
 KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write to a volatile control after the firmware has stopped.
 * This should return an error.
 */

static void cs_dsp_ctl_write_volatile_stopped(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals;

 reg_vals = kunit_kzalloc(test, param->len_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 regmap_raw_write(dsp->regmap, reg, reg_vals, param->len_bytes);

 /* Create control pointing to this data */
 def.flags = param->flags | WMFW_CTL_FLAG_VOLATILE;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = param->len_bytes;

 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 /* Start and stop the firmware */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 cs_dsp_stop(dsp);

 /* Drop expected writes and the regmap cache should be clean */
 cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
 cs_dsp_mock_regmap_drop_bytes(priv, reg, param->len_bytes);

 /* Write the control, it should return an error */
 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);
 KUNIT_EXPECT_LT(test,
   cs_dsp_coeff_lock_and_write_ctrl(ctl, 0, reg_vals, param->len_bytes),
   0);

 /* Should not have been any writes to registers */
 KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write to a volatile control after the DSP has been powered down.
 * This should return an error.
 */

static void cs_dsp_ctl_write_volatile_stopped_powered_down(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals;

 reg_vals = kunit_kzalloc(test, param->len_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 regmap_raw_write(dsp->regmap, reg, reg_vals, param->len_bytes);

 /* Create control pointing to this data */
 def.flags = param->flags | WMFW_CTL_FLAG_VOLATILE;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = param->len_bytes;

 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 /* Start and stop the firmware then power down */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 cs_dsp_stop(dsp);
 cs_dsp_power_down(dsp);

 /* Drop expected writes and the regmap cache should be clean */
 cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
 cs_dsp_mock_regmap_drop_bytes(priv, reg, param->len_bytes);

 /* Write the control, it should return an error */
 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);
 KUNIT_EXPECT_LT(test,
   cs_dsp_coeff_lock_and_write_ctrl(ctl, 0, reg_vals, param->len_bytes),
   0);

 /* Should not have been any writes to registers */
 KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write to a volatile control when a different firmware is currently
 * loaded into the DSP.
 * Should return an error.
 */

static void cs_dsp_ctl_write_volatile_not_current_loaded_fw(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 struct cs_dsp_mock_wmfw_builder *builder2 = _create_dummy_wmfw(test);
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals;

 reg_vals = kunit_kmalloc(test, param->len_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 /* Create some DSP data to be read into the control cache */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 regmap_raw_write(dsp->regmap, reg, reg_vals, param->len_bytes);

 /* Create control pointing to this data */
 def.flags = param->flags | WMFW_CTL_FLAG_VOLATILE;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = param->len_bytes;

 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 /* Power-up DSP */
 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 /* Power-down DSP then power-up with a different firmware */
 cs_dsp_power_down(dsp);
 wmfw = cs_dsp_mock_wmfw_get_firmware(builder2);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw2", NULL, NULL, "mbc.vss"), 0);

 /* Drop expected writes and the regmap cache should be clean */
 cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
 cs_dsp_mock_regmap_drop_bytes(priv, reg, param->len_bytes);

 /* Write the control, it should return an error */
 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);
 KUNIT_EXPECT_LT(test,
   cs_dsp_coeff_lock_and_write_ctrl(ctl, 0, reg_vals, param->len_bytes),
   0);

 /* Should not have been any writes to registers */
 KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write to a volatile control when a different firmware is currently
 * running.
 * Should return an error.
 */

static void cs_dsp_ctl_write_volatile_not_current_running_fw(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 struct cs_dsp_mock_wmfw_builder *builder2 = _create_dummy_wmfw(test);
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals;

 reg_vals = kunit_kmalloc(test, param->len_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 /* Create some DSP data to be read into the control cache */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 regmap_raw_write(dsp->regmap, reg, reg_vals, param->len_bytes);

 /* Create control pointing to this data */
 def.flags = param->flags | WMFW_CTL_FLAG_VOLATILE;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = param->len_bytes;

 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 /* Power-up DSP */
 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 /* Power-down DSP then power-up with a different firmware */
 cs_dsp_power_down(dsp);
 wmfw = cs_dsp_mock_wmfw_get_firmware(builder2);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw2", NULL, NULL, "mbc.vss"), 0);

 /* Start the firmware and add an action to stop it during cleanup */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, _cs_dsp_stop_wrapper, dsp), 0);

 /* Drop expected writes and the regmap cache should be clean */
 cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
 cs_dsp_mock_regmap_drop_bytes(priv, reg, param->len_bytes);

 /* Write the control, it should return an error */
 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);
 KUNIT_EXPECT_LT(test,
   cs_dsp_coeff_lock_and_write_ctrl(ctl, 0, reg_vals, param->len_bytes),
   0);

 /* Should not have been any writes to registers */
 KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Read from an offset into the control data. Should return only the
 * portion of data from the offset position.
 */

static void cs_dsp_ctl_read_with_seek(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals, *readback;
 unsigned int seek_words;

 def.flags = param->flags;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = 48;

 reg_vals = kunit_kmalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 readback = kunit_kzalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 get_random_bytes(reg_vals, def.length_bytes);
 regmap_raw_write(dsp->regmap, reg, reg_vals, def.length_bytes);

 /* Create control pointing to this data */
 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);

 /* Start the firmware and add an action to stop it during cleanup */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, _cs_dsp_stop_wrapper, dsp), 0);

 for (seek_words = 1; seek_words < (def.length_bytes / sizeof(u32)); seek_words++) {
  unsigned int len_bytes = def.length_bytes - (seek_words * sizeof(u32));

  KUNIT_EXPECT_EQ(test,
    cs_dsp_coeff_lock_and_read_ctrl(ctl, seek_words,
        readback, len_bytes),
    0);
  KUNIT_EXPECT_MEMEQ(test, readback, ®_vals[seek_words], len_bytes);
 }
}

/*
 * Read from an offset into the control cache. Should return only the
 * portion of data from the offset position.
 * Same as cs_dsp_ctl_read_with_seek() except the control is cached
 * and the firmware is not running.
 */

static void cs_dsp_ctl_read_cache_with_seek(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals, *readback;
 unsigned int seek_words;

 def.flags = param->flags;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = 48;

 reg_vals = kunit_kmalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 readback = kunit_kzalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 get_random_bytes(reg_vals, def.length_bytes);
 regmap_raw_write(dsp->regmap, reg, reg_vals, def.length_bytes);

 /* Create control pointing to this data */
 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);

 /* Start and stop the firmware so the read will come from the cache */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 cs_dsp_stop(dsp);

 for (seek_words = 1; seek_words < (def.length_bytes / sizeof(u32)); seek_words++) {
  unsigned int len_bytes = def.length_bytes - (seek_words * sizeof(u32));

  KUNIT_EXPECT_EQ(test,
    cs_dsp_coeff_lock_and_read_ctrl(ctl, seek_words,
        readback, len_bytes),
    0);
  KUNIT_EXPECT_MEMEQ(test, readback, ®_vals[seek_words], len_bytes);
 }
}

/*
 * Read less than the full length of data from a control. Should return
 * only the requested number of bytes.
 */

static void cs_dsp_ctl_read_truncated(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals, *readback;
 unsigned int len_bytes;

 def.flags = param->flags;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = 48;

 reg_vals = kunit_kmalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 readback = kunit_kzalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 get_random_bytes(reg_vals, def.length_bytes);
 regmap_raw_write(dsp->regmap, reg, reg_vals, def.length_bytes);

 /* Create control pointing to this data */
 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);

 /* Start the firmware and add an action to stop it during cleanup */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, _cs_dsp_stop_wrapper, dsp), 0);

 /* Reads are only allowed to be a multiple of the DSP word length */
 for (len_bytes = sizeof(u32); len_bytes < def.length_bytes; len_bytes += sizeof(u32)) {
  memset(readback, 0, def.length_bytes);
  KUNIT_EXPECT_EQ(test,
    cs_dsp_coeff_lock_and_read_ctrl(ctl, 0, readback, len_bytes),
    0);
  KUNIT_EXPECT_MEMEQ(test, readback, reg_vals, len_bytes);
  KUNIT_EXPECT_MEMNEQ(test,
        (u8 *)readback + len_bytes,
        (u8 *)reg_vals + len_bytes,
        def.length_bytes - len_bytes);
 }
}

/*
 * Read less than the full length of data from a cached control.
 * Should return only the requested number of bytes.
 * Same as cs_dsp_ctl_read_truncated() except the control is cached
 * and the firmware is not running.
 */

static void cs_dsp_ctl_read_cache_truncated(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals, *readback;
 unsigned int len_bytes;

 def.flags = param->flags;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = 48;

 reg_vals = kunit_kmalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 readback = kunit_kzalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 get_random_bytes(reg_vals, def.length_bytes);
 regmap_raw_write(dsp->regmap, reg, reg_vals, def.length_bytes);

 /* Create control pointing to this data */
 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);

 /* Start and stop the firmware so the read will come from the cache */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 cs_dsp_stop(dsp);

 /* Reads are only allowed to be a multiple of the DSP word length */
 for (len_bytes = sizeof(u32); len_bytes < def.length_bytes; len_bytes += sizeof(u32)) {
  memset(readback, 0, def.length_bytes);
  KUNIT_EXPECT_EQ(test,
    cs_dsp_coeff_lock_and_read_ctrl(ctl, 0, readback, len_bytes),
    0);
  KUNIT_EXPECT_MEMEQ(test, readback, reg_vals, len_bytes);
  KUNIT_EXPECT_MEMNEQ(test,
        (u8 *)readback + len_bytes,
        (u8 *)reg_vals + len_bytes,
        def.length_bytes - len_bytes);
 }
}

/*
 * Write to an offset into the control data. Should only change the
 * portion of data from the offset position.
 */

static void cs_dsp_ctl_write_with_seek(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals, *readback, *new_data;
 unsigned int seek_words;

 def.flags = param->flags;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = 48;

 reg_vals = kunit_kmalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 readback = kunit_kzalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);

 new_data = kunit_kmalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_data);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 get_random_bytes(reg_vals, def.length_bytes);
 regmap_raw_write(dsp->regmap, reg, reg_vals, def.length_bytes);

 /* Create control pointing to this data */
 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);

 /* Start the firmware and add an action to stop it during cleanup */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, _cs_dsp_stop_wrapper, dsp), 0);

 for (seek_words = 1; seek_words < (def.length_bytes / sizeof(u32)); seek_words++) {
  unsigned int len_bytes = def.length_bytes - (seek_words * sizeof(u32));

  /* Reset the register values to the test data */
  regmap_raw_write(dsp->regmap, reg, reg_vals, def.length_bytes);

  get_random_bytes(new_data, def.length_bytes);
  KUNIT_EXPECT_EQ(test,
    cs_dsp_coeff_lock_and_write_ctrl(ctl, seek_words,
         new_data, len_bytes),
    1);
  KUNIT_ASSERT_EQ(test, regmap_raw_read(dsp->regmap, reg, readback, def.length_bytes),
    0);
  /* Initial portion of readback should be unchanged */
  KUNIT_EXPECT_MEMEQ(test, readback, reg_vals, seek_words * sizeof(u32));
  KUNIT_EXPECT_MEMEQ(test, &readback[seek_words], new_data, len_bytes);
 }
}

/*
 * Write to an offset into the control cache. Should only change the
 * portion of data from the offset position.
 * Same as cs_dsp_ctl_write_with_seek() except the control is cached
 * and the firmware is not running.
 */

static void cs_dsp_ctl_write_cache_with_seek(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals, *readback, *new_data;
 unsigned int seek_words;

 def.flags = param->flags;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = 48;

 reg_vals = kunit_kmalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 readback = kunit_kmalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);

 new_data = kunit_kmalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_data);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 get_random_bytes(reg_vals, def.length_bytes);
 regmap_raw_write(dsp->regmap, reg, reg_vals, def.length_bytes);

 /* Create control pointing to this data */
 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);

 /* Start and stop the firmware so the read will come from the cache */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 cs_dsp_stop(dsp);

 for (seek_words = 1; seek_words < (def.length_bytes / sizeof(u32)); seek_words++) {
  unsigned int len_bytes = def.length_bytes - (seek_words * sizeof(u32));

  /* Reset the cache to the test data */
  KUNIT_EXPECT_GE(test,
    cs_dsp_coeff_lock_and_write_ctrl(ctl, 0, reg_vals,
         def.length_bytes),
    0);

  get_random_bytes(new_data, def.length_bytes);
  KUNIT_EXPECT_EQ(test,
    cs_dsp_coeff_lock_and_write_ctrl(ctl, seek_words,
         new_data, len_bytes),
    1);

  memset(readback, 0, def.length_bytes);
  KUNIT_EXPECT_EQ(test,
    cs_dsp_coeff_lock_and_read_ctrl(ctl, 0, readback,
        def.length_bytes),
    0);
  /* Initial portion of readback should be unchanged */
  KUNIT_EXPECT_MEMEQ(test, readback, reg_vals, seek_words * sizeof(u32));
  KUNIT_EXPECT_MEMEQ(test, &readback[seek_words], new_data, len_bytes);
 }
}

/*
 * Write less than the full length of data to a control. Should only
 * change the requested number of bytes.
 */

static void cs_dsp_ctl_write_truncated(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals, *readback, *new_data;
 unsigned int len_bytes;

 def.flags = param->flags;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = 48;

 reg_vals = kunit_kmalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 readback = kunit_kmalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);

 new_data = kunit_kmalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_data);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 get_random_bytes(reg_vals, def.length_bytes);
 regmap_raw_write(dsp->regmap, reg, reg_vals, def.length_bytes);

 /* Create control pointing to this data */
 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);

 /* Start the firmware and add an action to stop it during cleanup */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, _cs_dsp_stop_wrapper, dsp), 0);

 /* Writes are only allowed to be a multiple of the DSP word length */
 for (len_bytes = sizeof(u32); len_bytes < def.length_bytes; len_bytes += sizeof(u32)) {
  /* Reset the register values to the test data */
  regmap_raw_write(dsp->regmap, reg, reg_vals, def.length_bytes);

  get_random_bytes(new_data, def.length_bytes);
  KUNIT_EXPECT_EQ(test,
    cs_dsp_coeff_lock_and_write_ctrl(ctl, 0, new_data, len_bytes),
    1);

  memset(readback, 0, def.length_bytes);
  KUNIT_ASSERT_EQ(test, regmap_raw_read(dsp->regmap, reg, readback, def.length_bytes),
    0);
  KUNIT_EXPECT_MEMEQ(test, readback, new_data, len_bytes);
  KUNIT_EXPECT_MEMEQ(test,
       (u8 *)readback + len_bytes,
       (u8 *)reg_vals + len_bytes,
       def.length_bytes - len_bytes);
 }
}

/*
 * Write less than the full length of data to a cached control.
 * Should only change the requested number of bytes.
 * Same as cs_dsp_ctl_write_truncated() except the control is cached
 * and the firmware is not running.
 */

static void cs_dsp_ctl_write_cache_truncated(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals, *readback, *new_data;
 unsigned int len_bytes;

 def.flags = param->flags;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = 48;

 reg_vals = kunit_kmalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 readback = kunit_kmalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);

 new_data = kunit_kmalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_data);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 get_random_bytes(reg_vals, def.length_bytes);
 regmap_raw_write(dsp->regmap, reg, reg_vals, def.length_bytes);

 /* Create control pointing to this data */
 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);

 /* Start and stop the firmware so the read will come from the cache */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 cs_dsp_stop(dsp);

 /* Writes are only allowed to be a multiple of the DSP word length */
 for (len_bytes = sizeof(u32); len_bytes < def.length_bytes; len_bytes += sizeof(u32)) {
  /* Reset the cache to the test data */
  KUNIT_EXPECT_GE(test,
    cs_dsp_coeff_lock_and_write_ctrl(ctl, 0, reg_vals,
         def.length_bytes),
    0);

  get_random_bytes(new_data, def.length_bytes);
  KUNIT_EXPECT_EQ(test,
    cs_dsp_coeff_lock_and_write_ctrl(ctl, 0, new_data, len_bytes),
    1);

  memset(readback, 0, def.length_bytes);
  KUNIT_EXPECT_EQ(test,
    cs_dsp_coeff_lock_and_read_ctrl(ctl, 0, readback,
        def.length_bytes),
    0);
  KUNIT_EXPECT_MEMEQ(test, readback, new_data, len_bytes);
  KUNIT_EXPECT_MEMEQ(test,
       (u8 *)readback + len_bytes,
       (u8 *)reg_vals + len_bytes,
       def.length_bytes - len_bytes);
 }
}

/*
 * Read from an offset that is beyond the end of the control data.
 * Should return an error.
 */

static void cs_dsp_ctl_read_with_seek_oob(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals;
 unsigned int seek_words;

 def.flags = param->flags;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = param->len_bytes;

 reg_vals = kunit_kzalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 regmap_raw_write(dsp->regmap, reg, reg_vals, def.length_bytes);

 /* Create control pointing to this data */
 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);

 /* Start the firmware and add an action to stop it during cleanup */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, _cs_dsp_stop_wrapper, dsp), 0);

 seek_words = def.length_bytes / sizeof(u32);
 KUNIT_EXPECT_LT(test,
   cs_dsp_coeff_lock_and_read_ctrl(ctl, seek_words,
       reg_vals, def.length_bytes),
   0);

 if (!(def.flags & WMFW_CTL_FLAG_VOLATILE)) {
  /* Stop firmware and repeat the read from the cache */
  kunit_release_action(test, _cs_dsp_stop_wrapper, dsp);
  KUNIT_ASSERT_FALSE(test, dsp->running);

  KUNIT_EXPECT_LT(test,
    cs_dsp_coeff_lock_and_read_ctrl(ctl, seek_words,
        reg_vals, def.length_bytes),
    0);
 }
}

/*
 * Read more data than the length of the control data.
 * Should return an error.
 */

static void cs_dsp_ctl_read_with_length_overflow(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals;

 def.flags = param->flags;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = param->len_bytes;

 reg_vals = kunit_kzalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 regmap_raw_write(dsp->regmap, reg, reg_vals, def.length_bytes);

 /* Create control pointing to this data */
 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);

 /* Start the firmware and add an action to stop it during cleanup */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, _cs_dsp_stop_wrapper, dsp), 0);

 KUNIT_EXPECT_LT(test,
   cs_dsp_coeff_lock_and_read_ctrl(ctl, 0, reg_vals, def.length_bytes + 1),
   0);

 if (!(def.flags & WMFW_CTL_FLAG_VOLATILE)) {
  /* Stop firmware and repeat the read from the cache */
  kunit_release_action(test, _cs_dsp_stop_wrapper, dsp);
  KUNIT_ASSERT_FALSE(test, dsp->running);

  KUNIT_EXPECT_LT(test,
    cs_dsp_coeff_lock_and_read_ctrl(ctl, 0, reg_vals,
        def.length_bytes + 1),
    0);
 }
}

/*
 * Read with a seek and length that ends beyond the end of control data.
 * Should return an error.
 */

static void cs_dsp_ctl_read_with_seek_and_length_oob(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals;

 def.flags = param->flags;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = param->len_bytes;

 reg_vals = kunit_kzalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 regmap_raw_write(dsp->regmap, reg, reg_vals, def.length_bytes);

 /* Create control pointing to this data */
 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);

 /* Start the firmware and add an action to stop it during cleanup */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, _cs_dsp_stop_wrapper, dsp), 0);

 /*
 * Read full control length but at a start offset of 1 so that
 * offset + length exceeds the length of the control.
 */

 KUNIT_EXPECT_LT(test,
   cs_dsp_coeff_lock_and_read_ctrl(ctl, 1, reg_vals, def.length_bytes),
   0);

 if (!(def.flags & WMFW_CTL_FLAG_VOLATILE)) {
  /* Stop firmware and repeat the read from the cache */
  kunit_release_action(test, _cs_dsp_stop_wrapper, dsp);
  KUNIT_ASSERT_FALSE(test, dsp->running);

  KUNIT_EXPECT_LT(test,
    cs_dsp_coeff_lock_and_read_ctrl(ctl, 1, reg_vals,
        def.length_bytes),
    0);
 }
}

/*
 * Write to an offset that is beyond the end of the control data.
 * Should return an error without touching any registers.
 */

static void cs_dsp_ctl_write_with_seek_oob(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
 int alg_idx = _find_alg_entry(test, param->alg_id);
 unsigned int reg, alg_base_words;
 struct cs_dsp_coeff_ctl *ctl;
 struct firmware *wmfw;
 u32 *reg_vals;
 unsigned int seek_words;

 def.flags = param->flags;
 def.mem_type = param->mem_type;
 def.offset_dsp_words = param->offs_words;
 def.length_bytes = param->len_bytes;

 reg_vals = kunit_kzalloc(test, def.length_bytes, GFP_KERNEL);
 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, reg_vals);

 /* Create some initial register content */
 alg_base_words = _get_alg_mem_base_words(test, alg_idx, param->mem_type);
 reg = cs_dsp_mock_base_addr_for_mem(priv, param->mem_type);
 reg += (alg_base_words + param->offs_words) *
  cs_dsp_mock_reg_addr_inc_per_unpacked_word(priv);
 regmap_raw_write(dsp->regmap, reg, reg_vals, def.length_bytes);

 /* Create control pointing to this data */
 cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
           cs_dsp_ctl_rw_test_algs[alg_idx].id,
           "dummyalg", NULL);
 cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
 cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);

 wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
 KUNIT_ASSERT_EQ(test, cs_dsp_power_up(dsp, wmfw, "mock_fw", NULL, NULL, "misc"), 0);

 ctl = list_first_entry_or_null(&dsp->ctl_list, struct cs_dsp_coeff_ctl, list);
 KUNIT_ASSERT_NOT_NULL(test, ctl);

 /* Start the firmware and add an action to stop it during cleanup */
 KUNIT_ASSERT_EQ(test, cs_dsp_run(dsp), 0);
 KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, _cs_dsp_stop_wrapper, dsp), 0);

 /* Drop expected writes and the regmap cache should be clean */
 cs_dsp_mock_xm_header_drop_from_regmap_cache(priv);
 cs_dsp_mock_regmap_drop_bytes(priv, reg, param->len_bytes);

 get_random_bytes(reg_vals, def.length_bytes);
 seek_words = def.length_bytes / sizeof(u32);
 KUNIT_EXPECT_LT(test,
   cs_dsp_coeff_lock_and_write_ctrl(ctl, seek_words,
        reg_vals, def.length_bytes),
   0);

 if (!(def.flags & WMFW_CTL_FLAG_VOLATILE)) {
  /* Stop firmware and repeat the write to the cache */
  kunit_release_action(test, _cs_dsp_stop_wrapper, dsp);
  KUNIT_ASSERT_FALSE(test, dsp->running);

  get_random_bytes(reg_vals, def.length_bytes);
  KUNIT_EXPECT_LT(test,
    cs_dsp_coeff_lock_and_write_ctrl(ctl, seek_words,
         reg_vals, def.length_bytes),
    0);
 }

 /* Check that it didn't write any registers */
 KUNIT_EXPECT_FALSE(test, cs_dsp_mock_regmap_is_dirty(priv, true));
}

/*
 * Write more data than the length of the control data.
 * Should return an error.
 */

static void cs_dsp_ctl_write_with_length_overflow(struct kunit *test)
{
 const struct cs_dsp_ctl_rw_test_param *param = test->param_value;
 struct cs_dsp_test *priv = test->priv;
 struct cs_dsp_test_local *local = priv->local;
 struct cs_dsp *dsp = priv->dsp;
 struct cs_dsp_mock_coeff_def def = mock_coeff_template;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=94 G=93

¤ Dauer der Verarbeitung: 0.73 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.