1d5ac70f0Sopenharmony_ci/* 2d5ac70f0Sopenharmony_ci Copyright(c) 2014-2015 Intel Corporation 3d5ac70f0Sopenharmony_ci All rights reserved. 4d5ac70f0Sopenharmony_ci 5d5ac70f0Sopenharmony_ci This library is free software; you can redistribute it and/or modify 6d5ac70f0Sopenharmony_ci it under the terms of the GNU Lesser General Public License as 7d5ac70f0Sopenharmony_ci published by the Free Software Foundation; either version 2.1 of 8d5ac70f0Sopenharmony_ci the License, or (at your option) any later version. 9d5ac70f0Sopenharmony_ci 10d5ac70f0Sopenharmony_ci This program is distributed in the hope that it will be useful, 11d5ac70f0Sopenharmony_ci but WITHOUT ANY WARRANTY; without even the implied warranty of 12d5ac70f0Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13d5ac70f0Sopenharmony_ci GNU Lesser General Public License for more details. 14d5ac70f0Sopenharmony_ci 15d5ac70f0Sopenharmony_ci Authors: Mengdong Lin <mengdong.lin@intel.com> 16d5ac70f0Sopenharmony_ci Yao Jin <yao.jin@intel.com> 17d5ac70f0Sopenharmony_ci Liam Girdwood <liam.r.girdwood@linux.intel.com> 18d5ac70f0Sopenharmony_ci*/ 19d5ac70f0Sopenharmony_ci 20d5ac70f0Sopenharmony_ci#include "tplg_local.h" 21d5ac70f0Sopenharmony_ci 22d5ac70f0Sopenharmony_ci/* write a block, track the position */ 23d5ac70f0Sopenharmony_cistatic ssize_t twrite(snd_tplg_t *tplg, void *data, size_t data_size) 24d5ac70f0Sopenharmony_ci{ 25d5ac70f0Sopenharmony_ci if (tplg->bin_pos + data_size > tplg->bin_size) 26d5ac70f0Sopenharmony_ci return -EIO; 27d5ac70f0Sopenharmony_ci memcpy(tplg->bin + tplg->bin_pos, data, data_size); 28d5ac70f0Sopenharmony_ci tplg->bin_pos += data_size; 29d5ac70f0Sopenharmony_ci return data_size; 30d5ac70f0Sopenharmony_ci} 31d5ac70f0Sopenharmony_ci 32d5ac70f0Sopenharmony_ci/* write out block header to output file */ 33d5ac70f0Sopenharmony_cistatic ssize_t write_block_header(snd_tplg_t *tplg, unsigned int type, 34d5ac70f0Sopenharmony_ci unsigned int vendor_type, 35d5ac70f0Sopenharmony_ci unsigned int version, unsigned int index, 36d5ac70f0Sopenharmony_ci size_t payload_size, int count) 37d5ac70f0Sopenharmony_ci{ 38d5ac70f0Sopenharmony_ci struct snd_soc_tplg_hdr hdr; 39d5ac70f0Sopenharmony_ci 40d5ac70f0Sopenharmony_ci memset(&hdr, 0, sizeof(hdr)); 41d5ac70f0Sopenharmony_ci hdr.magic = SND_SOC_TPLG_MAGIC; 42d5ac70f0Sopenharmony_ci hdr.abi = SND_SOC_TPLG_ABI_VERSION; 43d5ac70f0Sopenharmony_ci hdr.type = type; 44d5ac70f0Sopenharmony_ci hdr.vendor_type = vendor_type; 45d5ac70f0Sopenharmony_ci hdr.version = version; 46d5ac70f0Sopenharmony_ci hdr.payload_size = payload_size; 47d5ac70f0Sopenharmony_ci hdr.index = index; 48d5ac70f0Sopenharmony_ci hdr.size = sizeof(hdr); 49d5ac70f0Sopenharmony_ci hdr.count = count; 50d5ac70f0Sopenharmony_ci 51d5ac70f0Sopenharmony_ci /* make sure file offset is aligned with the calculated HDR offset */ 52d5ac70f0Sopenharmony_ci if (tplg->bin_pos != tplg->next_hdr_pos) { 53d5ac70f0Sopenharmony_ci SNDERR("New header is at offset 0x%zx but file" 54d5ac70f0Sopenharmony_ci " offset 0x%zx is %s by %ld bytes", 55d5ac70f0Sopenharmony_ci tplg->next_hdr_pos, tplg->bin_pos, 56d5ac70f0Sopenharmony_ci tplg->bin_pos > tplg->next_hdr_pos ? "ahead" : "behind", 57d5ac70f0Sopenharmony_ci tplg->bin_pos - tplg->next_hdr_pos); 58d5ac70f0Sopenharmony_ci return -EINVAL; 59d5ac70f0Sopenharmony_ci } 60d5ac70f0Sopenharmony_ci 61d5ac70f0Sopenharmony_ci tplg_log(tplg, 'B', tplg->bin_pos, 62d5ac70f0Sopenharmony_ci "header index %d type %d count %d size 0x%lx/%ld vendor %d " 63d5ac70f0Sopenharmony_ci "version %d", index, type, count, 64d5ac70f0Sopenharmony_ci (long unsigned int)payload_size, (long int)payload_size, 65d5ac70f0Sopenharmony_ci vendor_type, version); 66d5ac70f0Sopenharmony_ci 67d5ac70f0Sopenharmony_ci tplg->next_hdr_pos += hdr.payload_size + sizeof(hdr); 68d5ac70f0Sopenharmony_ci 69d5ac70f0Sopenharmony_ci return twrite(tplg, &hdr, sizeof(hdr)); 70d5ac70f0Sopenharmony_ci} 71d5ac70f0Sopenharmony_ci 72d5ac70f0Sopenharmony_cistatic int write_elem_block(snd_tplg_t *tplg, 73d5ac70f0Sopenharmony_ci struct list_head *base, size_t size, 74d5ac70f0Sopenharmony_ci int tplg_type, const char *obj_name) 75d5ac70f0Sopenharmony_ci{ 76d5ac70f0Sopenharmony_ci struct list_head *pos, *sub_pos, *sub_base; 77d5ac70f0Sopenharmony_ci struct tplg_elem *elem, *elem_next; 78d5ac70f0Sopenharmony_ci size_t total_size = 0, count = 0, block_size = 0; 79d5ac70f0Sopenharmony_ci ssize_t ret, wsize; 80d5ac70f0Sopenharmony_ci 81d5ac70f0Sopenharmony_ci sub_base = base; 82d5ac70f0Sopenharmony_ci list_for_each(pos, base) { 83d5ac70f0Sopenharmony_ci /* find elems with the same index to make a block */ 84d5ac70f0Sopenharmony_ci elem = list_entry(pos, struct tplg_elem, list); 85d5ac70f0Sopenharmony_ci 86d5ac70f0Sopenharmony_ci if (elem->compound_elem) 87d5ac70f0Sopenharmony_ci continue; 88d5ac70f0Sopenharmony_ci 89d5ac70f0Sopenharmony_ci elem_next = list_entry(pos->next, struct tplg_elem, list); 90d5ac70f0Sopenharmony_ci block_size += elem->size; 91d5ac70f0Sopenharmony_ci count++; 92d5ac70f0Sopenharmony_ci 93d5ac70f0Sopenharmony_ci if ((pos->next == base) || (elem_next->index != elem->index)) { 94d5ac70f0Sopenharmony_ci /* write header for the block */ 95d5ac70f0Sopenharmony_ci ret = write_block_header(tplg, tplg_type, elem->vendor_type, 96d5ac70f0Sopenharmony_ci tplg->version, elem->index, block_size, count); 97d5ac70f0Sopenharmony_ci if (ret < 0) { 98d5ac70f0Sopenharmony_ci SNDERR("failed to write %s block %d", 99d5ac70f0Sopenharmony_ci obj_name, ret); 100d5ac70f0Sopenharmony_ci return ret; 101d5ac70f0Sopenharmony_ci } 102d5ac70f0Sopenharmony_ci 103d5ac70f0Sopenharmony_ci /* write elems for the block */ 104d5ac70f0Sopenharmony_ci list_for_each(sub_pos, sub_base) { 105d5ac70f0Sopenharmony_ci elem = list_entry(sub_pos, struct tplg_elem, list); 106d5ac70f0Sopenharmony_ci /* compound elems have already been copied to other elems */ 107d5ac70f0Sopenharmony_ci if (elem->compound_elem) 108d5ac70f0Sopenharmony_ci continue; 109d5ac70f0Sopenharmony_ci 110d5ac70f0Sopenharmony_ci if (elem->type != SND_TPLG_TYPE_DAPM_GRAPH) 111d5ac70f0Sopenharmony_ci tplg_log(tplg, 'B', tplg->bin_pos, 112d5ac70f0Sopenharmony_ci "%s '%s': write %d bytes", 113d5ac70f0Sopenharmony_ci obj_name, elem->id, elem->size); 114d5ac70f0Sopenharmony_ci else 115d5ac70f0Sopenharmony_ci tplg_log(tplg, 'B', tplg->bin_pos, 116d5ac70f0Sopenharmony_ci "%s '%s -> %s -> %s': write %d bytes", 117d5ac70f0Sopenharmony_ci obj_name, elem->route->source, 118d5ac70f0Sopenharmony_ci elem->route->control, 119d5ac70f0Sopenharmony_ci elem->route->sink, elem->size); 120d5ac70f0Sopenharmony_ci 121d5ac70f0Sopenharmony_ci wsize = twrite(tplg, elem->obj, elem->size); 122d5ac70f0Sopenharmony_ci if (wsize < 0) 123d5ac70f0Sopenharmony_ci return size; 124d5ac70f0Sopenharmony_ci 125d5ac70f0Sopenharmony_ci total_size += wsize; 126d5ac70f0Sopenharmony_ci /* get to the end of sub list */ 127d5ac70f0Sopenharmony_ci if (sub_pos == pos) 128d5ac70f0Sopenharmony_ci break; 129d5ac70f0Sopenharmony_ci } 130d5ac70f0Sopenharmony_ci /* the last elem of the current sub list as the head of 131d5ac70f0Sopenharmony_ci next sub list*/ 132d5ac70f0Sopenharmony_ci sub_base = pos; 133d5ac70f0Sopenharmony_ci count = 0; 134d5ac70f0Sopenharmony_ci block_size = 0; 135d5ac70f0Sopenharmony_ci } 136d5ac70f0Sopenharmony_ci } 137d5ac70f0Sopenharmony_ci 138d5ac70f0Sopenharmony_ci /* make sure we have written the correct size */ 139d5ac70f0Sopenharmony_ci if (total_size != size) { 140d5ac70f0Sopenharmony_ci SNDERR("size mismatch. Expected %zu wrote %zu", 141d5ac70f0Sopenharmony_ci size, total_size); 142d5ac70f0Sopenharmony_ci return -EIO; 143d5ac70f0Sopenharmony_ci } 144d5ac70f0Sopenharmony_ci 145d5ac70f0Sopenharmony_ci return 0; 146d5ac70f0Sopenharmony_ci} 147d5ac70f0Sopenharmony_ci 148d5ac70f0Sopenharmony_cistatic size_t calc_manifest_size(snd_tplg_t *tplg) 149d5ac70f0Sopenharmony_ci{ 150d5ac70f0Sopenharmony_ci return sizeof(struct snd_soc_tplg_hdr) + 151d5ac70f0Sopenharmony_ci sizeof(tplg->manifest) + 152d5ac70f0Sopenharmony_ci tplg->manifest.priv.size; 153d5ac70f0Sopenharmony_ci} 154d5ac70f0Sopenharmony_ci 155d5ac70f0Sopenharmony_cistatic size_t calc_real_size(struct list_head *base) 156d5ac70f0Sopenharmony_ci{ 157d5ac70f0Sopenharmony_ci struct list_head *pos; 158d5ac70f0Sopenharmony_ci struct tplg_elem *elem, *elem_next; 159d5ac70f0Sopenharmony_ci size_t size = 0; 160d5ac70f0Sopenharmony_ci 161d5ac70f0Sopenharmony_ci list_for_each(pos, base) { 162d5ac70f0Sopenharmony_ci 163d5ac70f0Sopenharmony_ci elem = list_entry(pos, struct tplg_elem, list); 164d5ac70f0Sopenharmony_ci 165d5ac70f0Sopenharmony_ci /* compound elems have already been copied to other elems */ 166d5ac70f0Sopenharmony_ci if (elem->compound_elem) 167d5ac70f0Sopenharmony_ci continue; 168d5ac70f0Sopenharmony_ci 169d5ac70f0Sopenharmony_ci if (elem->size <= 0) 170d5ac70f0Sopenharmony_ci continue; 171d5ac70f0Sopenharmony_ci 172d5ac70f0Sopenharmony_ci size += elem->size; 173d5ac70f0Sopenharmony_ci 174d5ac70f0Sopenharmony_ci elem_next = list_entry(pos->next, struct tplg_elem, list); 175d5ac70f0Sopenharmony_ci 176d5ac70f0Sopenharmony_ci if ((pos->next == base) || (elem_next->index != elem->index)) 177d5ac70f0Sopenharmony_ci size += sizeof(struct snd_soc_tplg_hdr); 178d5ac70f0Sopenharmony_ci } 179d5ac70f0Sopenharmony_ci 180d5ac70f0Sopenharmony_ci return size; 181d5ac70f0Sopenharmony_ci} 182d5ac70f0Sopenharmony_ci 183d5ac70f0Sopenharmony_cistatic size_t calc_block_size(struct list_head *base) 184d5ac70f0Sopenharmony_ci{ 185d5ac70f0Sopenharmony_ci struct list_head *pos; 186d5ac70f0Sopenharmony_ci struct tplg_elem *elem; 187d5ac70f0Sopenharmony_ci size_t size = 0; 188d5ac70f0Sopenharmony_ci 189d5ac70f0Sopenharmony_ci list_for_each(pos, base) { 190d5ac70f0Sopenharmony_ci 191d5ac70f0Sopenharmony_ci elem = list_entry(pos, struct tplg_elem, list); 192d5ac70f0Sopenharmony_ci 193d5ac70f0Sopenharmony_ci /* compound elems have already been copied to other elems */ 194d5ac70f0Sopenharmony_ci if (elem->compound_elem) 195d5ac70f0Sopenharmony_ci continue; 196d5ac70f0Sopenharmony_ci 197d5ac70f0Sopenharmony_ci size += elem->size; 198d5ac70f0Sopenharmony_ci } 199d5ac70f0Sopenharmony_ci 200d5ac70f0Sopenharmony_ci return size; 201d5ac70f0Sopenharmony_ci} 202d5ac70f0Sopenharmony_ci 203d5ac70f0Sopenharmony_ci/* write the manifest including its private data */ 204d5ac70f0Sopenharmony_cistatic ssize_t write_manifest_data(snd_tplg_t *tplg) 205d5ac70f0Sopenharmony_ci{ 206d5ac70f0Sopenharmony_ci ssize_t ret; 207d5ac70f0Sopenharmony_ci 208d5ac70f0Sopenharmony_ci /* write the header for this block */ 209d5ac70f0Sopenharmony_ci ret = write_block_header(tplg, SND_SOC_TPLG_TYPE_MANIFEST, 0, 210d5ac70f0Sopenharmony_ci tplg->version, 0, 211d5ac70f0Sopenharmony_ci sizeof(tplg->manifest) + tplg->manifest.priv.size, 1); 212d5ac70f0Sopenharmony_ci if (ret < 0) { 213d5ac70f0Sopenharmony_ci SNDERR("failed to write manifest block"); 214d5ac70f0Sopenharmony_ci return ret; 215d5ac70f0Sopenharmony_ci } 216d5ac70f0Sopenharmony_ci 217d5ac70f0Sopenharmony_ci tplg_log(tplg, 'B', tplg->bin_pos, "manifest: write %d bytes", 218d5ac70f0Sopenharmony_ci sizeof(tplg->manifest)); 219d5ac70f0Sopenharmony_ci ret = twrite(tplg, &tplg->manifest, sizeof(tplg->manifest)); 220d5ac70f0Sopenharmony_ci if (ret >= 0) { 221d5ac70f0Sopenharmony_ci tplg_log(tplg, 'B', tplg->bin_pos, 222d5ac70f0Sopenharmony_ci "manifest: write %d priv bytes", 223d5ac70f0Sopenharmony_ci tplg->manifest.priv.size); 224d5ac70f0Sopenharmony_ci ret = twrite(tplg, tplg->manifest_pdata, tplg->manifest.priv.size); 225d5ac70f0Sopenharmony_ci } 226d5ac70f0Sopenharmony_ci return ret; 227d5ac70f0Sopenharmony_ci} 228d5ac70f0Sopenharmony_ci 229d5ac70f0Sopenharmony_ciint tplg_write_data(snd_tplg_t *tplg) 230d5ac70f0Sopenharmony_ci{ 231d5ac70f0Sopenharmony_ci struct tplg_table *tptr; 232d5ac70f0Sopenharmony_ci struct list_head *list; 233d5ac70f0Sopenharmony_ci ssize_t ret; 234d5ac70f0Sopenharmony_ci size_t total_size, size; 235d5ac70f0Sopenharmony_ci unsigned int index; 236d5ac70f0Sopenharmony_ci 237d5ac70f0Sopenharmony_ci /* calculate total size */ 238d5ac70f0Sopenharmony_ci total_size = calc_manifest_size(tplg); 239d5ac70f0Sopenharmony_ci for (index = 0; index < tplg_table_items; index++) { 240d5ac70f0Sopenharmony_ci tptr = &tplg_table[index]; 241d5ac70f0Sopenharmony_ci if (!tptr->build) 242d5ac70f0Sopenharmony_ci continue; 243d5ac70f0Sopenharmony_ci list = (struct list_head *)((void *)tplg + tptr->loff); 244d5ac70f0Sopenharmony_ci size = calc_real_size(list); 245d5ac70f0Sopenharmony_ci total_size += size; 246d5ac70f0Sopenharmony_ci } 247d5ac70f0Sopenharmony_ci 248d5ac70f0Sopenharmony_ci /* allocate new binary output */ 249d5ac70f0Sopenharmony_ci free(tplg->bin); 250d5ac70f0Sopenharmony_ci tplg->bin = malloc(total_size); 251d5ac70f0Sopenharmony_ci tplg->bin_pos = 0; 252d5ac70f0Sopenharmony_ci tplg->bin_size = total_size; 253d5ac70f0Sopenharmony_ci if (tplg->bin == NULL) { 254d5ac70f0Sopenharmony_ci tplg->bin_size = 0; 255d5ac70f0Sopenharmony_ci return -ENOMEM; 256d5ac70f0Sopenharmony_ci } 257d5ac70f0Sopenharmony_ci 258d5ac70f0Sopenharmony_ci /* write manifest */ 259d5ac70f0Sopenharmony_ci ret = write_manifest_data(tplg); 260d5ac70f0Sopenharmony_ci if (ret < 0) { 261d5ac70f0Sopenharmony_ci SNDERR("failed to write manifest %d", ret); 262d5ac70f0Sopenharmony_ci return ret; 263d5ac70f0Sopenharmony_ci } 264d5ac70f0Sopenharmony_ci 265d5ac70f0Sopenharmony_ci /* write all blocks */ 266d5ac70f0Sopenharmony_ci for (index = 0; index < tplg_table_items; index++) { 267d5ac70f0Sopenharmony_ci tptr = &tplg_table[index]; 268d5ac70f0Sopenharmony_ci if (!tptr->build) 269d5ac70f0Sopenharmony_ci continue; 270d5ac70f0Sopenharmony_ci list = (struct list_head *)((void *)tplg + tptr->loff); 271d5ac70f0Sopenharmony_ci /* calculate the block size in bytes for all elems in this list */ 272d5ac70f0Sopenharmony_ci size = calc_block_size(list); 273d5ac70f0Sopenharmony_ci if (size == 0) 274d5ac70f0Sopenharmony_ci continue; 275d5ac70f0Sopenharmony_ci tplg_log(tplg, 'B', tplg->bin_pos, 276d5ac70f0Sopenharmony_ci "block size for type %s (%d:%d) is 0x%zx/%zd", 277d5ac70f0Sopenharmony_ci tptr->name, tptr->type, 278d5ac70f0Sopenharmony_ci tptr->tsoc, size, size); 279d5ac70f0Sopenharmony_ci ret = write_elem_block(tplg, list, size, 280d5ac70f0Sopenharmony_ci tptr->tsoc, tptr->name); 281d5ac70f0Sopenharmony_ci if (ret < 0) { 282d5ac70f0Sopenharmony_ci SNDERR("failed to write %s elements: %s", 283d5ac70f0Sopenharmony_ci tptr->name, snd_strerror(-ret)); 284d5ac70f0Sopenharmony_ci return ret; 285d5ac70f0Sopenharmony_ci } 286d5ac70f0Sopenharmony_ci } 287d5ac70f0Sopenharmony_ci 288d5ac70f0Sopenharmony_ci tplg_log(tplg, 'B', tplg->bin_pos, "total size is 0x%zx/%zd", 289d5ac70f0Sopenharmony_ci tplg->bin_pos, tplg->bin_pos); 290d5ac70f0Sopenharmony_ci 291d5ac70f0Sopenharmony_ci if (total_size != tplg->bin_pos) { 292d5ac70f0Sopenharmony_ci SNDERR("total size mismatch (%zd != %zd)", 293d5ac70f0Sopenharmony_ci total_size, tplg->bin_pos); 294d5ac70f0Sopenharmony_ci return -EINVAL; 295d5ac70f0Sopenharmony_ci } 296d5ac70f0Sopenharmony_ci 297d5ac70f0Sopenharmony_ci return 0; 298d5ac70f0Sopenharmony_ci} 299