Blame src/topology/builder.c

Packit 4a16fb
/*
Packit 4a16fb
  Copyright(c) 2014-2015 Intel Corporation
Packit 4a16fb
  All rights reserved.
Packit 4a16fb
Packit 4a16fb
  This library is free software; you can redistribute it and/or modify
Packit 4a16fb
  it under the terms of the GNU Lesser General Public License as
Packit 4a16fb
  published by the Free Software Foundation; either version 2.1 of
Packit 4a16fb
  the License, or (at your option) any later version.
Packit 4a16fb
Packit 4a16fb
  This program is distributed in the hope that it will be useful,
Packit 4a16fb
  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 4a16fb
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 4a16fb
  GNU Lesser General Public License for more details.
Packit 4a16fb
Packit 4a16fb
  Authors: Mengdong Lin <mengdong.lin@intel.com>
Packit 4a16fb
           Yao Jin <yao.jin@intel.com>
Packit 4a16fb
           Liam Girdwood <liam.r.girdwood@linux.intel.com>
Packit 4a16fb
*/
Packit 4a16fb
Packit 4a16fb
#include "list.h"
Packit 4a16fb
#include "tplg_local.h"
Packit 4a16fb
Packit Service f36a15
/* write a block, track the position */
Packit Service f36a15
static ssize_t twrite(snd_tplg_t *tplg, void *data, size_t data_size)
Packit 4a16fb
{
Packit Service f36a15
	if (tplg->bin_pos + data_size > tplg->bin_size)
Packit Service f36a15
		return -EIO;
Packit Service f36a15
	memcpy(tplg->bin + tplg->bin_pos, data, data_size);
Packit Service f36a15
	tplg->bin_pos += data_size;
Packit Service f36a15
	return data_size;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/* write out block header to output file */
Packit Service f36a15
static ssize_t write_block_header(snd_tplg_t *tplg, unsigned int type,
Packit Service f36a15
				  unsigned int vendor_type,
Packit Service f36a15
				  unsigned int version, unsigned int index,
Packit Service f36a15
				  size_t payload_size, int count)
Packit 4a16fb
{
Packit 4a16fb
	struct snd_soc_tplg_hdr hdr;
Packit 4a16fb
Packit 4a16fb
	memset(&hdr, 0, sizeof(hdr));
Packit 4a16fb
	hdr.magic = SND_SOC_TPLG_MAGIC;
Packit 4a16fb
	hdr.abi = SND_SOC_TPLG_ABI_VERSION;
Packit 4a16fb
	hdr.type = type;
Packit 4a16fb
	hdr.vendor_type = vendor_type;
Packit 4a16fb
	hdr.version = version;
Packit 4a16fb
	hdr.payload_size = payload_size;
Packit 4a16fb
	hdr.index = index;
Packit 4a16fb
	hdr.size = sizeof(hdr);
Packit 4a16fb
	hdr.count = count;
Packit 4a16fb
Packit 4a16fb
	/* make sure file offset is aligned with the calculated HDR offset */
Packit Service f36a15
	if (tplg->bin_pos != tplg->next_hdr_pos) {
Packit Service f36a15
		SNDERR("New header is at offset 0x%zx but file"
Packit Service f36a15
			" offset 0x%zx is %s by %ld bytes",
Packit Service f36a15
			tplg->next_hdr_pos, tplg->bin_pos,
Packit Service f36a15
			tplg->bin_pos > tplg->next_hdr_pos ? "ahead" : "behind",
Packit Service f36a15
			labs(tplg->bin_pos - tplg->next_hdr_pos));
Packit Service f36a15
		return -EINVAL;
Packit 4a16fb
	}
Packit 4a16fb
Packit Service f36a15
	tplg_log(tplg, 'B', tplg->bin_pos,
Packit Service f36a15
		 "header index %d type %d count %d size 0x%lx/%ld vendor %d "
Packit Service f36a15
		 "version %d", index, type, count,
Packit Service f36a15
		 (long unsigned int)payload_size, (long int)payload_size,
Packit Service f36a15
		 vendor_type, version);
Packit 4a16fb
Packit 4a16fb
	tplg->next_hdr_pos += hdr.payload_size + sizeof(hdr);
Packit 4a16fb
Packit Service f36a15
	return twrite(tplg, &hdr, sizeof(hdr));
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int write_elem_block(snd_tplg_t *tplg,
Packit Service f36a15
			    struct list_head *base, size_t size,
Packit Service f36a15
			    int tplg_type, const char *obj_name)
Packit 4a16fb
{
Packit 4a16fb
	struct list_head *pos, *sub_pos, *sub_base;
Packit 4a16fb
	struct tplg_elem *elem, *elem_next;
Packit Service f36a15
	size_t total_size = 0, count = 0, block_size = 0;
Packit Service f36a15
	ssize_t ret, wsize;
Packit 4a16fb
Packit 4a16fb
	sub_base = base;
Packit 4a16fb
	list_for_each(pos, base) {
Packit 4a16fb
		/* find elems with the same index to make a block */
Packit 4a16fb
		elem = list_entry(pos, struct tplg_elem, list);
Packit 4a16fb
Packit 4a16fb
		if (elem->compound_elem)
Packit 4a16fb
			continue;
Packit 4a16fb
Packit 4a16fb
		elem_next = list_entry(pos->next, struct tplg_elem, list);
Packit 4a16fb
		block_size += elem->size;
Packit 4a16fb
		count++;
Packit 4a16fb
Packit 4a16fb
		if ((pos->next == base) || (elem_next->index != elem->index)) {
Packit 4a16fb
			/* write header for the block */
Packit 4a16fb
			ret = write_block_header(tplg, tplg_type, elem->vendor_type,
Packit 4a16fb
				tplg->version, elem->index, block_size, count);
Packit 4a16fb
			if (ret < 0) {
Packit Service f36a15
				SNDERR("failed to write %s block %d",
Packit 4a16fb
					obj_name, ret);
Packit 4a16fb
				return ret;
Packit 4a16fb
			}
Packit 4a16fb
Packit 4a16fb
			/* write elems for the block */
Packit 4a16fb
			list_for_each(sub_pos, sub_base) {
Packit 4a16fb
				elem = list_entry(sub_pos, struct tplg_elem, list);
Packit 4a16fb
				/* compound elems have already been copied to other elems */
Packit 4a16fb
				if (elem->compound_elem)
Packit 4a16fb
					continue;
Packit 4a16fb
Packit 4a16fb
				if (elem->type != SND_TPLG_TYPE_DAPM_GRAPH)
Packit Service f36a15
					tplg_log(tplg, 'B', tplg->bin_pos,
Packit Service f36a15
						 "%s '%s': write %d bytes",
Packit Service f36a15
						 obj_name, elem->id, elem->size);
Packit 4a16fb
				else
Packit Service f36a15
					tplg_log(tplg, 'B', tplg->bin_pos,
Packit Service f36a15
						 "%s '%s -> %s -> %s': write %d bytes",
Packit Service f36a15
						 obj_name, elem->route->source,
Packit Service f36a15
						 elem->route->control,
Packit Service f36a15
						 elem->route->sink, elem->size);
Packit Service f36a15
Packit Service f36a15
				wsize = twrite(tplg, elem->obj, elem->size);
Packit Service f36a15
				if (wsize < 0)
Packit Service f36a15
					return size;
Packit 4a16fb
Packit 4a16fb
				total_size += wsize;
Packit 4a16fb
				/* get to the end of sub list */
Packit 4a16fb
				if (sub_pos == pos)
Packit 4a16fb
					break;
Packit 4a16fb
			}
Packit 4a16fb
			/* the last elem of the current sub list as the head of 
Packit 4a16fb
			next sub list*/
Packit 4a16fb
			sub_base = pos;
Packit 4a16fb
			count = 0;
Packit 4a16fb
			block_size = 0;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/* make sure we have written the correct size */
Packit 4a16fb
	if (total_size != size) {
Packit Service f36a15
		SNDERR("size mismatch. Expected %zu wrote %zu",
Packit 4a16fb
			size, total_size);
Packit 4a16fb
		return -EIO;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit Service f36a15
static size_t calc_manifest_size(snd_tplg_t *tplg)
Packit Service f36a15
{
Packit Service f36a15
	return sizeof(struct snd_soc_tplg_hdr) +
Packit Service f36a15
	       sizeof(tplg->manifest) +
Packit Service f36a15
	       tplg->manifest.priv.size;
Packit Service f36a15
}
Packit Service f36a15
Packit Service f36a15
static size_t calc_real_size(struct list_head *base)
Packit 4a16fb
{
Packit 4a16fb
	struct list_head *pos;
Packit Service f36a15
	struct tplg_elem *elem, *elem_next;
Packit Service f36a15
	size_t size = 0;
Packit 4a16fb
Packit 4a16fb
	list_for_each(pos, base) {
Packit 4a16fb
Packit 4a16fb
		elem = list_entry(pos, struct tplg_elem, list);
Packit 4a16fb
Packit 4a16fb
		/* compound elems have already been copied to other elems */
Packit 4a16fb
		if (elem->compound_elem)
Packit 4a16fb
			continue;
Packit 4a16fb
Packit Service f36a15
		if (elem->size <= 0)
Packit Service f36a15
			continue;
Packit Service f36a15
Packit 4a16fb
		size += elem->size;
Packit Service f36a15
Packit Service f36a15
		elem_next = list_entry(pos->next, struct tplg_elem, list);
Packit Service f36a15
Packit Service f36a15
		if ((pos->next == base) || (elem_next->index != elem->index))
Packit Service f36a15
			size += sizeof(struct snd_soc_tplg_hdr);
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	return size;
Packit 4a16fb
}
Packit 4a16fb
Packit Service f36a15
static size_t calc_block_size(struct list_head *base)
Packit 4a16fb
{
Packit Service f36a15
	struct list_head *pos;
Packit Service f36a15
	struct tplg_elem *elem;
Packit Service f36a15
	size_t size = 0;
Packit Service f36a15
Packit Service f36a15
	list_for_each(pos, base) {
Packit Service f36a15
Packit Service f36a15
		elem = list_entry(pos, struct tplg_elem, list);
Packit Service f36a15
Packit Service f36a15
		/* compound elems have already been copied to other elems */
Packit Service f36a15
		if (elem->compound_elem)
Packit Service f36a15
			continue;
Packit Service f36a15
Packit Service f36a15
		size += elem->size;
Packit 4a16fb
	}
Packit 4a16fb
Packit Service f36a15
	return size;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/* write the manifest including its private data */
Packit Service f36a15
static ssize_t write_manifest_data(snd_tplg_t *tplg)
Packit 4a16fb
{
Packit Service f36a15
	ssize_t ret;
Packit 4a16fb
Packit 4a16fb
	/* write the header for this block */
Packit 4a16fb
	ret = write_block_header(tplg, SND_SOC_TPLG_TYPE_MANIFEST, 0,
Packit 4a16fb
		tplg->version, 0,
Packit 4a16fb
		sizeof(tplg->manifest) + tplg->manifest.priv.size, 1);
Packit 4a16fb
	if (ret < 0) {
Packit Service f36a15
		SNDERR("failed to write manifest block");
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit Service f36a15
	tplg_log(tplg, 'B', tplg->bin_pos, "manifest: write %d bytes",
Packit Service f36a15
		 sizeof(tplg->manifest));
Packit Service f36a15
	ret = twrite(tplg, &tplg->manifest, sizeof(tplg->manifest));
Packit Service f36a15
	if (ret >= 0) {
Packit Service f36a15
		tplg_log(tplg, 'B', tplg->bin_pos,
Packit Service f36a15
			 "manifest: write %d priv bytes",
Packit Service f36a15
			 tplg->manifest.priv.size);
Packit Service f36a15
		ret = twrite(tplg, tplg->manifest_pdata, tplg->manifest.priv.size);
Packit 4a16fb
	}
Packit Service f36a15
	return ret;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
int tplg_write_data(snd_tplg_t *tplg)
Packit 4a16fb
{
Packit Service f36a15
	struct tplg_table *tptr;
Packit Service f36a15
	struct list_head *list;
Packit Service f36a15
	ssize_t ret;
Packit Service f36a15
	size_t total_size, size;
Packit Service f36a15
	unsigned int index;
Packit Service f36a15
Packit Service f36a15
	/* calculate total size */
Packit Service f36a15
	total_size = calc_manifest_size(tplg);
Packit Service f36a15
	for (index = 0; index < tplg_table_items; index++) {
Packit Service f36a15
		tptr = &tplg_table[index];
Packit Service f36a15
		if (!tptr->build)
Packit Service f36a15
			continue;
Packit Service f36a15
		list = (struct list_head *)((void *)tplg + tptr->loff);
Packit Service f36a15
		size = calc_real_size(list);
Packit Service f36a15
		total_size += size;
Packit 4a16fb
	}
Packit 4a16fb
Packit Service f36a15
	/* allocate new binary output */
Packit Service f36a15
	free(tplg->bin);
Packit Service f36a15
	tplg->bin = malloc(total_size);
Packit Service f36a15
	tplg->bin_pos = 0;
Packit Service f36a15
	tplg->bin_size = total_size;
Packit Service f36a15
	if (tplg->bin == NULL) {
Packit Service f36a15
		tplg->bin_size = 0;
Packit Service f36a15
		return -ENOMEM;
Packit 4a16fb
	}
Packit 4a16fb
Packit Service f36a15
	/* write manifest */
Packit Service f36a15
	ret = write_manifest_data(tplg);
Packit 4a16fb
	if (ret < 0) {
Packit Service f36a15
		SNDERR("failed to write manifest %d", ret);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit Service f36a15
	/* write all blocks */
Packit Service f36a15
	for (index = 0; index < tplg_table_items; index++) {
Packit Service f36a15
		tptr = &tplg_table[index];
Packit Service f36a15
		if (!tptr->build)
Packit Service f36a15
			continue;
Packit Service f36a15
		list = (struct list_head *)((void *)tplg + tptr->loff);
Packit Service f36a15
		/* calculate the block size in bytes for all elems in this list */
Packit Service f36a15
		size = calc_block_size(list);
Packit Service f36a15
		if (size == 0)
Packit Service f36a15
			continue;
Packit Service f36a15
		tplg_log(tplg, 'B', tplg->bin_pos,
Packit Service f36a15
			 "block size for type %s (%d:%d) is 0x%zx/%zd",
Packit Service f36a15
			 tptr->name, tptr->type,
Packit Service f36a15
			 tptr->tsoc, size, size);
Packit Service f36a15
		ret = write_elem_block(tplg, list, size,
Packit Service f36a15
				       tptr->tsoc, tptr->name);
Packit Service f36a15
		if (ret < 0) {
Packit Service f36a15
			SNDERR("failed to write %s elements: %s",
Packit Service f36a15
						tptr->name, snd_strerror(-ret));
Packit Service f36a15
			return ret;
Packit Service f36a15
		}
Packit 4a16fb
	}
Packit 4a16fb
Packit Service f36a15
	tplg_log(tplg, 'B', tplg->bin_pos, "total size is 0x%zx/%zd",
Packit Service f36a15
		 tplg->bin_pos, tplg->bin_pos);
Packit 4a16fb
Packit Service f36a15
	if (total_size != tplg->bin_pos) {
Packit Service f36a15
		SNDERR("total size mismatch (%zd != %zd)",
Packit Service f36a15
		       total_size, tplg->bin_pos);
Packit Service f36a15
		return -EINVAL;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	return 0;
Packit 4a16fb
}