// SPDX-License-Identifier: GPL-2.0-only // // bin file builder for cs_dsp KUnit tests. // // Copyright (C) 2024 Cirrus Logic, Inc. and // Cirrus Logic International Semiconductor Ltd. #include #include #include #include #include #include #include #include #include #include /* Buffer large enough for bin file content */ #define CS_DSP_MOCK_BIN_BUF_SIZE 32768 KUNIT_DEFINE_ACTION_WRAPPER(vfree_action_wrapper, vfree, void *) struct cs_dsp_mock_bin_builder { struct cs_dsp_test *test_priv; void *buf; void *write_p; size_t bytes_used; }; /** * cs_dsp_mock_bin_get_firmware() - Get struct firmware wrapper for data. * * @builder: Pointer to struct cs_dsp_mock_bin_builder. * * Return: Pointer to a struct firmware wrapper for the data. */ struct firmware *cs_dsp_mock_bin_get_firmware(struct cs_dsp_mock_bin_builder *builder) { struct firmware *fw; fw = kunit_kzalloc(builder->test_priv->test, sizeof(*fw), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, fw); fw->data = builder->buf; fw->size = builder->bytes_used; return fw; } EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_get_firmware, "FW_CS_DSP_KUNIT_TEST_UTILS"); /** * cs_dsp_mock_bin_add_raw_block() - Add a data block to the bin file. * * @builder: Pointer to struct cs_dsp_mock_bin_builder. * @alg_id: Algorithm ID. * @alg_ver: Algorithm version. * @type: Type of the block. * @offset: Offset. * @payload_data: Pointer to buffer containing the payload data. * @payload_len_bytes: Length of payload data in bytes. */ void cs_dsp_mock_bin_add_raw_block(struct cs_dsp_mock_bin_builder *builder, unsigned int alg_id, unsigned int alg_ver, int type, unsigned int offset, const void *payload_data, size_t payload_len_bytes) { struct wmfw_coeff_item *item; size_t bytes_needed = struct_size_t(struct wmfw_coeff_item, data, payload_len_bytes); KUNIT_ASSERT_TRUE(builder->test_priv->test, (builder->write_p + bytes_needed) < (builder->buf + CS_DSP_MOCK_BIN_BUF_SIZE)); item = builder->write_p; item->offset = cpu_to_le16(offset); item->type = cpu_to_le16(type); item->id = cpu_to_le32(alg_id); item->ver = cpu_to_le32(alg_ver << 8); item->len = cpu_to_le32(payload_len_bytes); if (payload_len_bytes) memcpy(item->data, payload_data, payload_len_bytes); builder->write_p += bytes_needed; builder->bytes_used += bytes_needed; } EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_raw_block, "FW_CS_DSP_KUNIT_TEST_UTILS"); static void cs_dsp_mock_bin_add_name_or_info(struct cs_dsp_mock_bin_builder *builder, const char *info, int type) { size_t info_len = strlen(info); char *tmp = NULL; if (info_len % 4) { /* Create a padded string with length a multiple of 4 */ info_len = round_up(info_len, 4); tmp = kunit_kzalloc(builder->test_priv->test, info_len, GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, tmp); memcpy(tmp, info, info_len); info = tmp; } cs_dsp_mock_bin_add_raw_block(builder, 0, 0, WMFW_INFO_TEXT, 0, info, info_len); kunit_kfree(builder->test_priv->test, tmp); } /** * cs_dsp_mock_bin_add_info() - Add an info block to the bin file. * * @builder: Pointer to struct cs_dsp_mock_bin_builder. * @info: Pointer to info string to be copied into the file. * * The string will be padded to a length that is a multiple of 4 bytes. */ void cs_dsp_mock_bin_add_info(struct cs_dsp_mock_bin_builder *builder, const char *info) { cs_dsp_mock_bin_add_name_or_info(builder, info, WMFW_INFO_TEXT); } EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_info, "FW_CS_DSP_KUNIT_TEST_UTILS"); /** * cs_dsp_mock_bin_add_name() - Add a name block to the bin file. * * @builder: Pointer to struct cs_dsp_mock_bin_builder. * @name: Pointer to name string to be copied into the file. */ void cs_dsp_mock_bin_add_name(struct cs_dsp_mock_bin_builder *builder, const char *name) { cs_dsp_mock_bin_add_name_or_info(builder, name, WMFW_NAME_TEXT); } EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_name, "FW_CS_DSP_KUNIT_TEST_UTILS"); /** * cs_dsp_mock_bin_add_patch() - Add a patch data block to the bin file. * * @builder: Pointer to struct cs_dsp_mock_bin_builder. * @alg_id: Algorithm ID for the patch. * @alg_ver: Algorithm version for the patch. * @mem_region: Memory region for the patch. * @reg_addr_offset: Offset to start of data in register addresses. * @payload_data: Pointer to buffer containing the payload data. * @payload_len_bytes: Length of payload data in bytes. */ void cs_dsp_mock_bin_add_patch(struct cs_dsp_mock_bin_builder *builder, unsigned int alg_id, unsigned int alg_ver, int mem_region, unsigned int reg_addr_offset, const void *payload_data, size_t payload_len_bytes) { /* Payload length must be a multiple of 4 */ KUNIT_ASSERT_EQ(builder->test_priv->test, payload_len_bytes % 4, 0); cs_dsp_mock_bin_add_raw_block(builder, alg_id, alg_ver, mem_region, reg_addr_offset, payload_data, payload_len_bytes); } EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_patch, "FW_CS_DSP_KUNIT_TEST_UTILS"); /** * cs_dsp_mock_bin_init() - Initialize a struct cs_dsp_mock_bin_builder. * * @priv: Pointer to struct cs_dsp_test. * @format_version: Required bin format version. * @fw_version: Firmware version to put in bin file. * * Return: Pointer to created struct cs_dsp_mock_bin_builder. */ struct cs_dsp_mock_bin_builder *cs_dsp_mock_bin_init(struct cs_dsp_test *priv, int format_version, unsigned int fw_version) { struct cs_dsp_mock_bin_builder *builder; struct wmfw_coeff_hdr *hdr; builder = kunit_kzalloc(priv->test, sizeof(*builder), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder); builder->test_priv = priv; builder->buf = vmalloc(CS_DSP_MOCK_BIN_BUF_SIZE); KUNIT_ASSERT_NOT_NULL(priv->test, builder->buf); kunit_add_action_or_reset(priv->test, vfree_action_wrapper, builder->buf); /* Create header */ hdr = builder->buf; memcpy(hdr->magic, "WMDR", sizeof(hdr->magic)); hdr->len = cpu_to_le32(offsetof(struct wmfw_coeff_hdr, data)); hdr->ver = cpu_to_le32(fw_version | (format_version << 24)); hdr->core_ver = cpu_to_le32(((u32)priv->dsp->type << 24) | priv->dsp->rev); builder->write_p = hdr->data; builder->bytes_used = offsetof(struct wmfw_coeff_hdr, data); return builder; } EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_init, "FW_CS_DSP_KUNIT_TEST_UTILS");