1 /*
2   Copyright (c) 2019 Red Hat Inc.
3   All rights reserved.
4 
5   This library is free software; you can redistribute it and/or modify
6   it under the terms of the GNU Lesser General Public License as
7   published by the Free Software Foundation; either version 2.1 of
8   the License, or (at your option) any later version.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU Lesser General Public License for more details.
14 
15   Authors: Jaroslav Kysela <perex@perex.cz>
16 */
17 
18 #include "tplg_local.h"
19 
tplg_decode_template(snd_tplg_t *tplg, size_t pos, struct snd_soc_tplg_hdr *hdr, snd_tplg_obj_template_t *t)20 int tplg_decode_template(snd_tplg_t *tplg,
21 			 size_t pos,
22 			 struct snd_soc_tplg_hdr *hdr,
23 			 snd_tplg_obj_template_t *t)
24 {
25 	int type;
26 
27 	type = tplg_get_type(hdr->type);
28 	tplg_log(tplg, 'D', pos, "template: asoc type %d library type %d",
29 		 hdr->type, type);
30 	if (type < 0)
31 		return type;
32 
33 	memset(t, 0, sizeof(*t));
34 	t->type = type;
35 	t->index = hdr->index;
36 	t->version = hdr->version;
37 	t->vendor_type = hdr->vendor_type;
38 	tplg_log(tplg, 'D', pos, "template: index %d version %d vendor_type %d",
39 		 hdr->index, hdr->version, hdr->vendor_type);
40 	return 0;
41 }
42 
snd_tplg_decode(snd_tplg_t *tplg, void *bin, size_t size, int dflags)43 int snd_tplg_decode(snd_tplg_t *tplg, void *bin, size_t size, int dflags)
44 {
45 	struct snd_soc_tplg_hdr *hdr;
46 	struct tplg_table *tptr;
47 	size_t pos;
48 	void *b = bin;
49 	unsigned int index;
50 	int err;
51 
52 	if (dflags != 0)
53 		return -EINVAL;
54 	if (tplg == NULL || bin == NULL)
55 		return -EINVAL;
56 	while (1) {
57 		pos = b - bin;
58 		if (size == pos) {
59 			tplg_log(tplg, 'D', pos, "block: success (total %zd)", size);
60 			return 0;
61 		}
62 		if (size - pos < sizeof(*hdr)) {
63 			tplg_log(tplg, 'D', pos, "block: small size");
64 			SNDERR("incomplete header data to decode");
65 			return -EINVAL;
66 		}
67 		hdr = b;
68 		if (hdr->magic != SND_SOC_TPLG_MAGIC) {
69 			SNDERR("bad block magic %08x", hdr->magic);
70 			return -EINVAL;
71 		}
72 
73 		tplg_log(tplg, 'D', pos, "block: abi %d size %d payload size %d",
74 			 hdr->abi, hdr->size, hdr->payload_size);
75 		if (hdr->abi != SND_SOC_TPLG_ABI_VERSION) {
76 			SNDERR("unsupported ABI version %d", hdr->abi);
77 			return -EINVAL;
78 		}
79 		if (hdr->size != sizeof(*hdr)) {
80 			SNDERR("header size mismatch");
81 			return -EINVAL;
82 		}
83 
84 		if (size - pos < hdr->size + hdr->payload_size) {
85 			SNDERR("incomplete payload data to decode");
86 			return -EINVAL;
87 		}
88 
89 		if (hdr->payload_size < 8) {
90 			SNDERR("wrong payload size %d", hdr->payload_size);
91 			return -EINVAL;
92 		}
93 
94 		/* first block must be manifest */
95 		if (b == bin) {
96 			if (hdr->type != SND_SOC_TPLG_TYPE_MANIFEST) {
97 				SNDERR("first block must be manifest (value %d)", hdr->type);
98 				return -EINVAL;
99 			}
100 			err = snd_tplg_set_version(tplg, hdr->version);
101 			if (err < 0)
102 				return err;
103 		}
104 
105 		pos += hdr->size;
106 		for (index = 0; index < tplg_table_items; index++) {
107 			tptr = &tplg_table[index];
108 			if (tptr->tsoc == (int)hdr->type)
109 				break;
110 		}
111 		if (index >= tplg_table_items || tptr->decod == NULL) {
112 			SNDERR("unknown block type %d", hdr->type);
113 			return -EINVAL;
114 		}
115 		tplg_log(tplg, 'D', pos, "block: type %d - %s", hdr->type, tptr->name);
116 		err = tptr->decod(tplg, pos, hdr, b + hdr->size, hdr->payload_size);
117 		if (err < 0)
118 			return err;
119 		b += hdr->size + hdr->payload_size;
120 	}
121 }
122