1 // SPDX-License-Identifier: GPL-2.0-only 2 // 3 // bin file builder for cs_dsp KUnit tests. 4 // 5 // Copyright (C) 2024 Cirrus Logic, Inc. and 6 // Cirrus Logic International Semiconductor Ltd. 7 8 #include <kunit/resource.h> 9 #include <kunit/test.h> 10 #include <linux/firmware/cirrus/cs_dsp.h> 11 #include <linux/firmware/cirrus/cs_dsp_test_utils.h> 12 #include <linux/firmware/cirrus/wmfw.h> 13 #include <linux/firmware.h> 14 #include <linux/math.h> 15 #include <linux/overflow.h> 16 #include <linux/string.h> 17 #include <linux/vmalloc.h> 18 19 /* Buffer large enough for bin file content */ 20 #define CS_DSP_MOCK_BIN_BUF_SIZE 32768 21 22 KUNIT_DEFINE_ACTION_WRAPPER(vfree_action_wrapper, vfree, void *) 23 24 struct cs_dsp_mock_bin_builder { 25 struct cs_dsp_test *test_priv; 26 void *buf; 27 void *write_p; 28 size_t bytes_used; 29 }; 30 31 /** 32 * cs_dsp_mock_bin_get_firmware() - Get struct firmware wrapper for data. 33 * 34 * @builder: Pointer to struct cs_dsp_mock_bin_builder. 35 * 36 * Return: Pointer to a struct firmware wrapper for the data. 37 */ 38 struct firmware *cs_dsp_mock_bin_get_firmware(struct cs_dsp_mock_bin_builder *builder) 39 { 40 struct firmware *fw; 41 42 fw = kunit_kzalloc(builder->test_priv->test, sizeof(*fw), GFP_KERNEL); 43 KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, fw); 44 45 fw->data = builder->buf; 46 fw->size = builder->bytes_used; 47 48 return fw; 49 } 50 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_get_firmware, "FW_CS_DSP_KUNIT_TEST_UTILS"); 51 52 /** 53 * cs_dsp_mock_bin_add_raw_block() - Add a data block to the bin file. 54 * 55 * @builder: Pointer to struct cs_dsp_mock_bin_builder. 56 * @alg_id: Algorithm ID. 57 * @alg_ver: Algorithm version. 58 * @type: Type of the block. 59 * @offset: 16-bit offset. 60 * @offset32: 32-bit offset (sample rate on V1 and V2 file formats). 61 * @payload_data: Pointer to buffer containing the payload data. 62 * @payload_len_bytes: Length of payload data in bytes. 63 */ 64 void cs_dsp_mock_bin_add_raw_block(struct cs_dsp_mock_bin_builder *builder, 65 unsigned int alg_id, unsigned int alg_ver, 66 int type, u16 offset, u32 offset32, 67 const void *payload_data, size_t payload_len_bytes) 68 { 69 struct wmfw_coeff_item *item; 70 size_t bytes_needed = struct_size_t(struct wmfw_coeff_item, data, payload_len_bytes); 71 72 KUNIT_ASSERT_TRUE(builder->test_priv->test, 73 (builder->write_p + bytes_needed) < 74 (builder->buf + CS_DSP_MOCK_BIN_BUF_SIZE)); 75 76 item = builder->write_p; 77 78 item->offset = cpu_to_le16(offset); 79 item->offset32 = cpu_to_le32(offset32); 80 item->type = cpu_to_le16(type); 81 item->id = cpu_to_le32(alg_id); 82 item->ver = cpu_to_le32(alg_ver << 8); 83 item->len = cpu_to_le32(payload_len_bytes); 84 85 if (payload_len_bytes) 86 memcpy(item->data, payload_data, payload_len_bytes); 87 88 builder->write_p += bytes_needed; 89 builder->bytes_used += bytes_needed; 90 } 91 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_raw_block, "FW_CS_DSP_KUNIT_TEST_UTILS"); 92 93 static void cs_dsp_mock_bin_add_name_or_info(struct cs_dsp_mock_bin_builder *builder, 94 const char *info, int type) 95 { 96 size_t info_len = strlen(info); 97 char *tmp = NULL; 98 99 if (info_len % 4) { 100 /* Create a padded string with length a multiple of 4 */ 101 size_t copy_len = info_len; 102 info_len = round_up(info_len, 4); 103 tmp = kunit_kzalloc(builder->test_priv->test, info_len, GFP_KERNEL); 104 KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, tmp); 105 memcpy(tmp, info, copy_len); 106 info = tmp; 107 } 108 109 cs_dsp_mock_bin_add_raw_block(builder, 0, 0, WMFW_INFO_TEXT, 0, 0, info, info_len); 110 kunit_kfree(builder->test_priv->test, tmp); 111 } 112 113 /** 114 * cs_dsp_mock_bin_add_info() - Add an info block to the bin file. 115 * 116 * @builder: Pointer to struct cs_dsp_mock_bin_builder. 117 * @info: Pointer to info string to be copied into the file. 118 * 119 * The string will be padded to a length that is a multiple of 4 bytes. 120 */ 121 void cs_dsp_mock_bin_add_info(struct cs_dsp_mock_bin_builder *builder, 122 const char *info) 123 { 124 cs_dsp_mock_bin_add_name_or_info(builder, info, WMFW_INFO_TEXT); 125 } 126 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_info, "FW_CS_DSP_KUNIT_TEST_UTILS"); 127 128 /** 129 * cs_dsp_mock_bin_add_name() - Add a name block to the bin file. 130 * 131 * @builder: Pointer to struct cs_dsp_mock_bin_builder. 132 * @name: Pointer to name string to be copied into the file. 133 */ 134 void cs_dsp_mock_bin_add_name(struct cs_dsp_mock_bin_builder *builder, 135 const char *name) 136 { 137 cs_dsp_mock_bin_add_name_or_info(builder, name, WMFW_NAME_TEXT); 138 } 139 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_name, "FW_CS_DSP_KUNIT_TEST_UTILS"); 140 141 /** 142 * cs_dsp_mock_bin_add_patch() - Add a patch data block to the bin file. 143 * 144 * @builder: Pointer to struct cs_dsp_mock_bin_builder. 145 * @alg_id: Algorithm ID for the patch. 146 * @alg_ver: Algorithm version for the patch. 147 * @mem_region: Memory region for the patch. 148 * @reg_addr_offset: Offset to start of data in register addresses. 149 * @payload_data: Pointer to buffer containing the payload data. 150 * @payload_len_bytes: Length of payload data in bytes. 151 */ 152 void cs_dsp_mock_bin_add_patch(struct cs_dsp_mock_bin_builder *builder, 153 unsigned int alg_id, unsigned int alg_ver, 154 int mem_region, unsigned int reg_addr_offset, 155 const void *payload_data, size_t payload_len_bytes) 156 { 157 /* Payload length must be a multiple of 4 */ 158 KUNIT_ASSERT_EQ(builder->test_priv->test, payload_len_bytes % 4, 0); 159 160 cs_dsp_mock_bin_add_raw_block(builder, alg_id, alg_ver, 161 mem_region, (u16)reg_addr_offset, 0, 162 payload_data, payload_len_bytes); 163 } 164 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_patch, "FW_CS_DSP_KUNIT_TEST_UTILS"); 165 166 /** 167 * cs_dsp_mock_bin_add_patch_off32() - Add a patch data block with 32-bit offset. 168 * 169 * @builder: Pointer to struct cs_dsp_mock_bin_builder. 170 * @alg_id: Algorithm ID for the patch. 171 * @alg_ver: Algorithm version for the patch. 172 * @mem_region: Memory region for the patch. 173 * @reg_addr_offset: Offset to start of data in register addresses. 174 * @payload_data: Pointer to buffer containing the payload data. 175 * @payload_len_bytes: Length of payload data in bytes. 176 */ 177 void cs_dsp_mock_bin_add_patch_off32(struct cs_dsp_mock_bin_builder *builder, 178 unsigned int alg_id, unsigned int alg_ver, 179 int mem_region, unsigned int reg_addr_offset, 180 const void *payload_data, size_t payload_len_bytes) 181 { 182 /* Payload length must be a multiple of 4 */ 183 KUNIT_ASSERT_EQ(builder->test_priv->test, payload_len_bytes % 4, 0); 184 185 /* Mark the block as using the 32-bit offset */ 186 mem_region |= 0xf400; 187 188 cs_dsp_mock_bin_add_raw_block(builder, alg_id, alg_ver, 189 mem_region, 0, reg_addr_offset, 190 payload_data, payload_len_bytes); 191 } 192 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_patch_off32, "FW_CS_DSP_KUNIT_TEST_UTILS"); 193 194 /** 195 * cs_dsp_mock_bin_init() - Initialize a struct cs_dsp_mock_bin_builder. 196 * 197 * @priv: Pointer to struct cs_dsp_test. 198 * @format_version: Required bin format version. 199 * @fw_version: Firmware version to put in bin file. 200 * 201 * Return: Pointer to created struct cs_dsp_mock_bin_builder. 202 */ 203 struct cs_dsp_mock_bin_builder *cs_dsp_mock_bin_init(struct cs_dsp_test *priv, 204 int format_version, 205 unsigned int fw_version) 206 { 207 struct cs_dsp_mock_bin_builder *builder; 208 struct wmfw_coeff_hdr *hdr; 209 210 KUNIT_ASSERT_LE(priv->test, format_version, 0xff); 211 KUNIT_ASSERT_LE(priv->test, fw_version, 0xffffff); 212 213 builder = kunit_kzalloc(priv->test, sizeof(*builder), GFP_KERNEL); 214 KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder); 215 builder->test_priv = priv; 216 217 builder->buf = vmalloc(CS_DSP_MOCK_BIN_BUF_SIZE); 218 KUNIT_ASSERT_NOT_NULL(priv->test, builder->buf); 219 kunit_add_action_or_reset(priv->test, vfree_action_wrapper, builder->buf); 220 221 /* Create header */ 222 hdr = builder->buf; 223 memcpy(hdr->magic, "WMDR", sizeof(hdr->magic)); 224 hdr->len = cpu_to_le32(offsetof(struct wmfw_coeff_hdr, data)); 225 hdr->ver = cpu_to_le32(fw_version | (format_version << 24)); 226 hdr->core_ver = cpu_to_le32(((u32)priv->dsp->type << 24) | priv->dsp->rev); 227 228 builder->write_p = hdr->data; 229 builder->bytes_used = offsetof(struct wmfw_coeff_hdr, data); 230 231 return builder; 232 } 233 EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_init, "FW_CS_DSP_KUNIT_TEST_UTILS"); 234