Blame src/topology/builder.c

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