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 4a16fb
/* verbose output detailing each object size and file position */
Packit 4a16fb
static void verbose(snd_tplg_t *tplg, const char *fmt, ...)
Packit 4a16fb
{
Packit 4a16fb
	int offset;
Packit 4a16fb
	va_list va;
Packit 4a16fb
Packit 4a16fb
	if (!tplg->verbose)
Packit 4a16fb
		return;
Packit 4a16fb
Packit 4a16fb
	offset = lseek(tplg->out_fd, 0, SEEK_CUR);
Packit 4a16fb
Packit 4a16fb
	va_start(va, fmt);
Packit 4a16fb
	fprintf(stdout, "0x%6.6x/%6.6d -", offset, offset);
Packit 4a16fb
	vfprintf(stdout, fmt, va);
Packit 4a16fb
	va_end(va);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/* write out block header to output file */
Packit 4a16fb
static int write_block_header(snd_tplg_t *tplg, unsigned int type,
Packit 4a16fb
	unsigned int vendor_type, unsigned int version, unsigned int index,
Packit 4a16fb
	size_t payload_size, int count)
Packit 4a16fb
{
Packit 4a16fb
	struct snd_soc_tplg_hdr hdr;
Packit 4a16fb
	size_t bytes;
Packit 4a16fb
	int offset = lseek(tplg->out_fd, 0, SEEK_CUR);
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 4a16fb
	if ((unsigned int)offset != tplg->next_hdr_pos) {
Packit 4a16fb
		SNDERR("error: New header is at offset 0x%x but file"
Packit 4a16fb
			" offset 0x%x is %s by %d bytes\n",
Packit 4a16fb
			tplg->next_hdr_pos, offset,
Packit 4a16fb
			(unsigned int)offset > tplg->next_hdr_pos ? "ahead" : "behind",
Packit 4a16fb
			abs(offset - tplg->next_hdr_pos));
Packit 4a16fb
		exit(-EINVAL);
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	verbose(tplg, " header index %d type %d count %d size 0x%lx/%ld vendor %d "
Packit 4a16fb
		"version %d\n", index, type, count,
Packit 4a16fb
		(long unsigned int)payload_size, (long int)payload_size,
Packit 4a16fb
		vendor_type, version);
Packit 4a16fb
Packit 4a16fb
	tplg->next_hdr_pos += hdr.payload_size + sizeof(hdr);
Packit 4a16fb
Packit 4a16fb
	bytes = write(tplg->out_fd, &hdr, sizeof(hdr));
Packit 4a16fb
	if (bytes != sizeof(hdr)) {
Packit 4a16fb
		SNDERR("error: can't write section header %lu\n",
Packit 4a16fb
			(long unsigned int)bytes);
Packit 4a16fb
		return bytes;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	return bytes;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int write_elem_block(snd_tplg_t *tplg,
Packit 4a16fb
	struct list_head *base, int size, 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 4a16fb
	int ret, wsize = 0, total_size = 0, count = 0, block_size = 0;
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 4a16fb
				SNDERR("error: failed to write %s block %d\n",
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 4a16fb
					verbose(tplg, " %s '%s': write %d bytes\n",
Packit 4a16fb
						obj_name, elem->id, elem->size);
Packit 4a16fb
				else
Packit 4a16fb
					verbose(tplg, " %s '%s -> %s -> %s': write %d bytes\n",
Packit 4a16fb
						obj_name, elem->route->source,
Packit 4a16fb
						elem->route->control,
Packit 4a16fb
						elem->route->sink, elem->size);
Packit 4a16fb
Packit 4a16fb
				wsize = write(tplg->out_fd, elem->obj, elem->size);
Packit 4a16fb
				if (wsize < 0) {
Packit 4a16fb
					SNDERR("error: failed to write %s %d\n",
Packit 4a16fb
						obj_name, ret);
Packit 4a16fb
					return ret;
Packit 4a16fb
				}
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 4a16fb
		SNDERR("error: size mismatch. Expected %d wrote %d\n",
Packit 4a16fb
			size, total_size);
Packit 4a16fb
		return -EIO;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int calc_block_size(struct list_head *base)
Packit 4a16fb
{
Packit 4a16fb
	struct list_head *pos;
Packit 4a16fb
	struct tplg_elem *elem;
Packit 4a16fb
	int 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 4a16fb
		size += elem->size;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	return size;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int write_block(snd_tplg_t *tplg, struct list_head *base,
Packit 4a16fb
	int type)
Packit 4a16fb
{
Packit 4a16fb
	int size;
Packit 4a16fb
Packit 4a16fb
	/* calculate the block size in bytes for all elems in this list */
Packit 4a16fb
	size = calc_block_size(base);
Packit 4a16fb
	if (size <= 0)
Packit 4a16fb
		return size;
Packit 4a16fb
Packit 4a16fb
	verbose(tplg, " block size for type %d is %d\n", type, size);
Packit 4a16fb
Packit 4a16fb
	/* write each elem for this block */
Packit 4a16fb
	switch (type) {
Packit 4a16fb
	case SND_TPLG_TYPE_MIXER:
Packit 4a16fb
		return write_elem_block(tplg, base, size,
Packit 4a16fb
			SND_SOC_TPLG_TYPE_MIXER, "mixer");
Packit 4a16fb
	case SND_TPLG_TYPE_BYTES:
Packit 4a16fb
		return write_elem_block(tplg, base, size,
Packit 4a16fb
			SND_SOC_TPLG_TYPE_BYTES, "bytes");
Packit 4a16fb
	case SND_TPLG_TYPE_ENUM:
Packit 4a16fb
		return write_elem_block(tplg, base, size,
Packit 4a16fb
			SND_SOC_TPLG_TYPE_ENUM, "enum");
Packit 4a16fb
	case SND_TPLG_TYPE_DAPM_GRAPH:
Packit 4a16fb
		return write_elem_block(tplg, base, size,
Packit 4a16fb
			SND_SOC_TPLG_TYPE_DAPM_GRAPH, "route");
Packit 4a16fb
	case SND_TPLG_TYPE_DAPM_WIDGET:
Packit 4a16fb
		return write_elem_block(tplg, base, size,
Packit 4a16fb
			SND_SOC_TPLG_TYPE_DAPM_WIDGET, "widget");
Packit 4a16fb
	case SND_TPLG_TYPE_PCM:
Packit 4a16fb
		return write_elem_block(tplg, base, size,
Packit 4a16fb
			SND_SOC_TPLG_TYPE_PCM, "pcm");
Packit 4a16fb
	case SND_TPLG_TYPE_BE:
Packit 4a16fb
		return write_elem_block(tplg, base, size,
Packit 4a16fb
			SND_SOC_TPLG_TYPE_BACKEND_LINK, "be");
Packit 4a16fb
	case SND_TPLG_TYPE_CC:
Packit 4a16fb
		return write_elem_block(tplg, base, size,
Packit 4a16fb
			SND_SOC_TPLG_TYPE_CODEC_LINK, "cc");
Packit 4a16fb
	case SND_TPLG_TYPE_DATA:
Packit 4a16fb
		return write_elem_block(tplg, base, size,
Packit 4a16fb
			SND_SOC_TPLG_TYPE_PDATA, "data");
Packit 4a16fb
	case SND_TPLG_TYPE_DAI:
Packit 4a16fb
		return write_elem_block(tplg, base, size,
Packit 4a16fb
			SND_SOC_TPLG_TYPE_DAI, "dai");
Packit 4a16fb
	default:
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/* write the manifest including its private data */
Packit 4a16fb
static int write_manifest_data(snd_tplg_t *tplg)
Packit 4a16fb
{
Packit 4a16fb
	int 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 4a16fb
		SNDERR("error: failed to write manifest block %d\n", ret);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	verbose(tplg, "manifest : write %d bytes\n", sizeof(tplg->manifest));
Packit 4a16fb
	ret = write(tplg->out_fd, &tplg->manifest, sizeof(tplg->manifest));
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		SNDERR("error: failed to write manifest %d\n", ret);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	verbose(tplg, "manifest : write %d priv bytes\n", tplg->manifest.priv.size);
Packit 4a16fb
	ret = write(tplg->out_fd, tplg->manifest_pdata, tplg->manifest.priv.size);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		SNDERR("error: failed to write manifest priv data %d\n", ret);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
int tplg_write_data(snd_tplg_t *tplg)
Packit 4a16fb
{
Packit 4a16fb
	int ret;
Packit 4a16fb
Packit 4a16fb
	/* write manifest */
Packit 4a16fb
	ret = write_manifest_data(tplg);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		SNDERR("failed to write manifest %d\n", ret);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/* write mixer elems. */
Packit 4a16fb
	ret = write_block(tplg, &tplg->mixer_list,
Packit 4a16fb
		SND_TPLG_TYPE_MIXER);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		SNDERR("failed to write control elems %d\n", ret);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/* write enum control elems. */
Packit 4a16fb
	ret = write_block(tplg, &tplg->enum_list,
Packit 4a16fb
		SND_TPLG_TYPE_ENUM);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		SNDERR("failed to write control elems %d\n", ret);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/* write bytes extended control elems. */
Packit 4a16fb
	ret = write_block(tplg, &tplg->bytes_ext_list,
Packit 4a16fb
		SND_TPLG_TYPE_BYTES);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		SNDERR("failed to write control elems %d\n", ret);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/* write widget elems */
Packit 4a16fb
	ret = write_block(tplg, &tplg->widget_list,
Packit 4a16fb
		SND_TPLG_TYPE_DAPM_WIDGET);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		SNDERR("failed to write widget elems %d\n", ret);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/* write pcm elems */
Packit 4a16fb
	ret = write_block(tplg, &tplg->pcm_list,
Packit 4a16fb
		SND_TPLG_TYPE_PCM);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		SNDERR("failed to write pcm elems %d\n", ret);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/* write physical dai elems */
Packit 4a16fb
	ret = write_block(tplg, &tplg->dai_list,
Packit 4a16fb
		SND_TPLG_TYPE_DAI);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		SNDERR("failed to write physical dai elems %d\n", ret);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/* write be elems */
Packit 4a16fb
	ret = write_block(tplg, &tplg->be_list,
Packit 4a16fb
		SND_TPLG_TYPE_BE);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		SNDERR("failed to write be elems %d\n", ret);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/* write cc elems */
Packit 4a16fb
	ret = write_block(tplg, &tplg->cc_list,
Packit 4a16fb
		SND_TPLG_TYPE_CC);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		SNDERR("failed to write cc elems %d\n", ret);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/* write route elems */
Packit 4a16fb
	ret = write_block(tplg, &tplg->route_list,
Packit 4a16fb
		SND_TPLG_TYPE_DAPM_GRAPH);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		SNDERR("failed to write graph elems %d\n", ret);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/* write private data */
Packit 4a16fb
	ret = write_block(tplg, &tplg->pdata_list,
Packit 4a16fb
		SND_TPLG_TYPE_DATA);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		SNDERR("failed to write private data %d\n", ret);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	return 0;
Packit 4a16fb
}