1c72fcc34Sopenharmony_ci/*
2c72fcc34Sopenharmony_ci *  Extract microphone configuration from the ACPI NHLT table
3c72fcc34Sopenharmony_ci *
4c72fcc34Sopenharmony_ci *  Specification:
5c72fcc34Sopenharmony_ci *         https://01.org/sites/default/files/595976_intel_sst_nhlt.pdf
6c72fcc34Sopenharmony_ci *
7c72fcc34Sopenharmony_ci *     Author: Jaroslav Kysela <perex@perex.cz>
8c72fcc34Sopenharmony_ci *
9c72fcc34Sopenharmony_ci *
10c72fcc34Sopenharmony_ci *   This program is free software; you can redistribute it and/or modify
11c72fcc34Sopenharmony_ci *   it under the terms of the GNU General Public License as published by
12c72fcc34Sopenharmony_ci *   the Free Software Foundation; either version 2 of the License, or
13c72fcc34Sopenharmony_ci *   (at your option) any later version.
14c72fcc34Sopenharmony_ci *
15c72fcc34Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
16c72fcc34Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
17c72fcc34Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18c72fcc34Sopenharmony_ci *   GNU General Public License for more details.
19c72fcc34Sopenharmony_ci *
20c72fcc34Sopenharmony_ci *   You should have received a copy of the GNU General Public License
21c72fcc34Sopenharmony_ci *   along with this program; if not, write to the Free Software
22c72fcc34Sopenharmony_ci *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
23c72fcc34Sopenharmony_ci *
24c72fcc34Sopenharmony_ci */
25c72fcc34Sopenharmony_ci
26c72fcc34Sopenharmony_ci#include "aconfig.h"
27c72fcc34Sopenharmony_ci#include <stdint.h>
28c72fcc34Sopenharmony_ci#include <stdio.h>
29c72fcc34Sopenharmony_ci#include <stdlib.h>
30c72fcc34Sopenharmony_ci#include <errno.h>
31c72fcc34Sopenharmony_ci#include <string.h>
32c72fcc34Sopenharmony_ci#include <unistd.h>
33c72fcc34Sopenharmony_ci#include <fcntl.h>
34c72fcc34Sopenharmony_ci#include <getopt.h>
35c72fcc34Sopenharmony_ci#include <sys/stat.h>
36c72fcc34Sopenharmony_ci#include <arpa/inet.h>
37c72fcc34Sopenharmony_ci
38c72fcc34Sopenharmony_ciint debug = 0;
39c72fcc34Sopenharmony_ci
40c72fcc34Sopenharmony_ci/*
41c72fcc34Sopenharmony_ci * Dump dmic parameters in json
42c72fcc34Sopenharmony_ci */
43c72fcc34Sopenharmony_ci
44c72fcc34Sopenharmony_ci#define ACPI_HDR_SIZE (4 + 4 + 1 + 1 + 6 + 8 + 4 + 4 + 4)
45c72fcc34Sopenharmony_ci#define NHLT_EP_HDR_SIZE (4 + 1 + 1 + 2 + 2 + 2 + 4 + 1 + 1 + 1)
46c72fcc34Sopenharmony_ci#define VENDOR_MIC_CFG_SIZE (1 + 1 + 2 + 2 + 2 + 1 + 1 + 2 + 2 + 2 + 2 + 2 + 2)
47c72fcc34Sopenharmony_ci
48c72fcc34Sopenharmony_cistatic const char *microphone_type(uint8_t type)
49c72fcc34Sopenharmony_ci{
50c72fcc34Sopenharmony_ci	switch (type) {
51c72fcc34Sopenharmony_ci	case 0: return "omnidirectional";
52c72fcc34Sopenharmony_ci	case 1: return "subcardoid";
53c72fcc34Sopenharmony_ci	case 2: return "cardoid";
54c72fcc34Sopenharmony_ci	case 3: return "supercardoid";
55c72fcc34Sopenharmony_ci	case 4: return "hypercardoid";
56c72fcc34Sopenharmony_ci	case 5: return "8shaped";
57c72fcc34Sopenharmony_ci	case 7: return "vendor";
58c72fcc34Sopenharmony_ci	}
59c72fcc34Sopenharmony_ci	return "unknown";
60c72fcc34Sopenharmony_ci}
61c72fcc34Sopenharmony_ci
62c72fcc34Sopenharmony_cistatic const char *microphone_location(uint8_t location)
63c72fcc34Sopenharmony_ci{
64c72fcc34Sopenharmony_ci	switch (location) {
65c72fcc34Sopenharmony_ci	case 0: return "laptop-top-panel";
66c72fcc34Sopenharmony_ci	case 1: return "laptop-bottom-panel";
67c72fcc34Sopenharmony_ci	case 2: return "laptop-left-panel";
68c72fcc34Sopenharmony_ci	case 3: return "laptop-right-panel";
69c72fcc34Sopenharmony_ci	case 4: return "laptop-front-panel";
70c72fcc34Sopenharmony_ci	case 5: return "laptop-rear-panel";
71c72fcc34Sopenharmony_ci	}
72c72fcc34Sopenharmony_ci	return "unknown";
73c72fcc34Sopenharmony_ci}
74c72fcc34Sopenharmony_ci
75c72fcc34Sopenharmony_ci
76c72fcc34Sopenharmony_cistatic inline uint8_t get_u8(uint8_t *base, uint32_t off)
77c72fcc34Sopenharmony_ci{
78c72fcc34Sopenharmony_ci	return *(base + off);
79c72fcc34Sopenharmony_ci}
80c72fcc34Sopenharmony_ci
81c72fcc34Sopenharmony_cistatic inline int32_t get_s16le(uint8_t *base, uint32_t off)
82c72fcc34Sopenharmony_ci{
83c72fcc34Sopenharmony_ci	uint32_t v =  *(base + off + 0) |
84c72fcc34Sopenharmony_ci		      (*(base + off + 1) << 8);
85c72fcc34Sopenharmony_ci	if (v & 0x8000)
86c72fcc34Sopenharmony_ci		return -((int32_t)0x10000 - (int32_t)v);
87c72fcc34Sopenharmony_ci	return v;
88c72fcc34Sopenharmony_ci}
89c72fcc34Sopenharmony_ci
90c72fcc34Sopenharmony_cistatic inline uint32_t get_u32le(uint8_t *base, uint32_t off)
91c72fcc34Sopenharmony_ci{
92c72fcc34Sopenharmony_ci	return   *(base + off + 0) |
93c72fcc34Sopenharmony_ci		(*(base + off + 1) << 8) |
94c72fcc34Sopenharmony_ci		(*(base + off + 2) << 16) |
95c72fcc34Sopenharmony_ci		(*(base + off + 3) << 24);
96c72fcc34Sopenharmony_ci}
97c72fcc34Sopenharmony_ci
98c72fcc34Sopenharmony_cistatic int nhlt_dmic_config(FILE *out, uint8_t *dmic, uint8_t mic)
99c72fcc34Sopenharmony_ci{
100c72fcc34Sopenharmony_ci	int32_t angle_begin, angle_end;
101c72fcc34Sopenharmony_ci
102c72fcc34Sopenharmony_ci	if (mic > 0)
103c72fcc34Sopenharmony_ci		fprintf(out, ",\n");
104c72fcc34Sopenharmony_ci	fprintf(out, "\t\t{\n");
105c72fcc34Sopenharmony_ci	fprintf(out, "\t\t\t\"channel\":%i,\n", mic);
106c72fcc34Sopenharmony_ci	fprintf(out, "\t\t\t\"type\":\"%s\",\n", microphone_type(get_u8(dmic, 0)));
107c72fcc34Sopenharmony_ci	fprintf(out, "\t\t\t\"location\":\"%s\"", microphone_location(get_u8(dmic, 1)));
108c72fcc34Sopenharmony_ci	if (get_s16le(dmic, 2) != 0)
109c72fcc34Sopenharmony_ci		fprintf(out, ",\n\t\t\t\"speaker-distance\":%i", get_s16le(dmic, 2));
110c72fcc34Sopenharmony_ci	if (get_s16le(dmic, 4) != 0)
111c72fcc34Sopenharmony_ci		fprintf(out, ",\n\t\t\t\"horizontal-offset\":%i", get_s16le(dmic, 4));
112c72fcc34Sopenharmony_ci	if (get_s16le(dmic, 6) != 0)
113c72fcc34Sopenharmony_ci		fprintf(out, ",\n\t\t\t\"vertical-offset\":%i", get_s16le(dmic, 6));
114c72fcc34Sopenharmony_ci	if (get_u8(dmic, 8) != 0)
115c72fcc34Sopenharmony_ci		fprintf(out, ",\n\t\t\t\"freq-low-band\":%i", get_u8(dmic, 8) * 5);
116c72fcc34Sopenharmony_ci	if (get_u8(dmic, 9) != 0)
117c72fcc34Sopenharmony_ci		fprintf(out, ",\n\t\t\t\"freq-high-band\":%i", get_u8(dmic, 9) * 500);
118c72fcc34Sopenharmony_ci	if (get_s16le(dmic, 10) != 0)
119c72fcc34Sopenharmony_ci		fprintf(out, ",\n\t\t\t\"direction-angle\":%i", get_s16le(dmic, 10));
120c72fcc34Sopenharmony_ci	if (get_s16le(dmic, 12) != 0)
121c72fcc34Sopenharmony_ci		fprintf(out, ",\n\t\t\t\"elevation-angle\":%i", get_s16le(dmic, 12));
122c72fcc34Sopenharmony_ci	angle_begin = get_s16le(dmic, 14);
123c72fcc34Sopenharmony_ci	angle_end = get_s16le(dmic, 16);
124c72fcc34Sopenharmony_ci	if (!((angle_begin == 180 && angle_end == -180) ||
125c72fcc34Sopenharmony_ci	      (angle_begin == -180 && angle_end == 180))) {
126c72fcc34Sopenharmony_ci		fprintf(out, ",\n\t\t\t\"vertical-angle-begin\":%i,\n", angle_begin);
127c72fcc34Sopenharmony_ci		fprintf(out, "\t\t\t\"vertical-angle-end\":%i", angle_end);
128c72fcc34Sopenharmony_ci	}
129c72fcc34Sopenharmony_ci	angle_begin = get_s16le(dmic, 18);
130c72fcc34Sopenharmony_ci	angle_end = get_s16le(dmic, 20);
131c72fcc34Sopenharmony_ci	if (!((angle_begin == 180 && angle_end == -180) ||
132c72fcc34Sopenharmony_ci	      (angle_begin == -180 && angle_end == 180))) {
133c72fcc34Sopenharmony_ci		fprintf(out, ",\n\t\t\t\"horizontal-angle-begin\":%i,\n", angle_begin);
134c72fcc34Sopenharmony_ci		fprintf(out, "\t\t\t\"horizontal-angle-end\":%i", angle_end);
135c72fcc34Sopenharmony_ci	}
136c72fcc34Sopenharmony_ci	fprintf(out, "\n\t\t}");
137c72fcc34Sopenharmony_ci	return 0;
138c72fcc34Sopenharmony_ci}
139c72fcc34Sopenharmony_ci
140c72fcc34Sopenharmony_cistatic int nhlt_dmic_ep_to_json(FILE *out, uint8_t *ep, uint32_t ep_size)
141c72fcc34Sopenharmony_ci{
142c72fcc34Sopenharmony_ci	uint32_t off, specific_cfg_size;
143c72fcc34Sopenharmony_ci	uint8_t config_type, array_type, mic, num_mics;
144c72fcc34Sopenharmony_ci	int res;
145c72fcc34Sopenharmony_ci
146c72fcc34Sopenharmony_ci	off = NHLT_EP_HDR_SIZE;
147c72fcc34Sopenharmony_ci	specific_cfg_size = get_u32le(ep, off);
148c72fcc34Sopenharmony_ci	if (off + specific_cfg_size > ep_size)
149c72fcc34Sopenharmony_ci		goto oob;
150c72fcc34Sopenharmony_ci	off += 4;
151c72fcc34Sopenharmony_ci	config_type = get_u8(ep, off + 1);
152c72fcc34Sopenharmony_ci	if (config_type != 1)	/* mic array */
153c72fcc34Sopenharmony_ci		return 0;
154c72fcc34Sopenharmony_ci	array_type = get_u8(ep, off + 2);
155c72fcc34Sopenharmony_ci	if ((array_type & 0x0f) != 0x0f) {
156c72fcc34Sopenharmony_ci		fprintf(stderr, "Unsupported ArrayType %02x\n", array_type & 0x0f);
157c72fcc34Sopenharmony_ci		return -EINVAL;
158c72fcc34Sopenharmony_ci	}
159c72fcc34Sopenharmony_ci	num_mics = get_u8(ep, off + 3);
160c72fcc34Sopenharmony_ci	fprintf(out, "{\n");
161c72fcc34Sopenharmony_ci	fprintf(out, "\t\"mics-data-version\":1,\n");
162c72fcc34Sopenharmony_ci	fprintf(out, "\t\"mics-data-source\":\"acpi-nhlt\"");
163c72fcc34Sopenharmony_ci	for (mic = 0; mic < num_mics; mic++) {
164c72fcc34Sopenharmony_ci		if (off - NHLT_EP_HDR_SIZE + VENDOR_MIC_CFG_SIZE > specific_cfg_size) {
165c72fcc34Sopenharmony_ci			fprintf(out, "\n}\n");
166c72fcc34Sopenharmony_ci			goto oob;
167c72fcc34Sopenharmony_ci		}
168c72fcc34Sopenharmony_ci		if (mic == 0)
169c72fcc34Sopenharmony_ci			fprintf(out, ",\n\t\"mics\":[\n");
170c72fcc34Sopenharmony_ci		res = nhlt_dmic_config(out, ep + off + 4, mic);
171c72fcc34Sopenharmony_ci		if (res < 0)
172c72fcc34Sopenharmony_ci			return res;
173c72fcc34Sopenharmony_ci		off += VENDOR_MIC_CFG_SIZE;
174c72fcc34Sopenharmony_ci	}
175c72fcc34Sopenharmony_ci	if (num_mics > 0)
176c72fcc34Sopenharmony_ci		fprintf(out, "\n\t]\n");
177c72fcc34Sopenharmony_ci	fprintf(out, "}\n");
178c72fcc34Sopenharmony_ci	return num_mics;
179c72fcc34Sopenharmony_cioob:
180c72fcc34Sopenharmony_ci	fprintf(stderr, "Data (out-of-bounds) error\n");
181c72fcc34Sopenharmony_ci	return -EINVAL;
182c72fcc34Sopenharmony_ci}
183c72fcc34Sopenharmony_ci
184c72fcc34Sopenharmony_cistatic int nhlt_table_to_json(FILE *out, uint8_t *nhlt, uint32_t size)
185c72fcc34Sopenharmony_ci{
186c72fcc34Sopenharmony_ci	uint32_t _size, off, ep_size;
187c72fcc34Sopenharmony_ci	uint8_t sum = 0, ep, ep_count, link_type, dmics = 0;
188c72fcc34Sopenharmony_ci	int res;
189c72fcc34Sopenharmony_ci
190c72fcc34Sopenharmony_ci	_size = get_u32le(nhlt, 4);
191c72fcc34Sopenharmony_ci	if (_size != size) {
192c72fcc34Sopenharmony_ci		fprintf(stderr, "Table size mismatch (%08x != %08x)\n", _size, (uint32_t)size);
193c72fcc34Sopenharmony_ci		return -EINVAL;
194c72fcc34Sopenharmony_ci	}
195c72fcc34Sopenharmony_ci	for (off = 0; off < size; off++)
196c72fcc34Sopenharmony_ci		sum += get_u8(nhlt, off);
197c72fcc34Sopenharmony_ci	if (sum != 0) {
198c72fcc34Sopenharmony_ci		fprintf(stderr, "Checksum error (%02x)\n", sum);
199c72fcc34Sopenharmony_ci		return -EINVAL;
200c72fcc34Sopenharmony_ci	}
201c72fcc34Sopenharmony_ci	/* skip header */
202c72fcc34Sopenharmony_ci	off = ACPI_HDR_SIZE;
203c72fcc34Sopenharmony_ci	ep_count = get_u8(nhlt, off++);
204c72fcc34Sopenharmony_ci	for (ep = 0; ep < ep_count; ep++) {
205c72fcc34Sopenharmony_ci		if (off + 17 > size)
206c72fcc34Sopenharmony_ci			goto oob;
207c72fcc34Sopenharmony_ci		ep_size = get_u32le(nhlt, off);
208c72fcc34Sopenharmony_ci		if (off + ep_size > size)
209c72fcc34Sopenharmony_ci			goto oob;
210c72fcc34Sopenharmony_ci		link_type = get_u8(nhlt, off + 4);
211c72fcc34Sopenharmony_ci		res = 0;
212c72fcc34Sopenharmony_ci		if (link_type == 2) { 	/* PDM */
213c72fcc34Sopenharmony_ci			res = nhlt_dmic_ep_to_json(out, nhlt + off, ep_size);
214c72fcc34Sopenharmony_ci			if (res > 0)
215c72fcc34Sopenharmony_ci				dmics++;
216c72fcc34Sopenharmony_ci		}
217c72fcc34Sopenharmony_ci		if (res < 0)
218c72fcc34Sopenharmony_ci			return res;
219c72fcc34Sopenharmony_ci		off += ep_size;
220c72fcc34Sopenharmony_ci	}
221c72fcc34Sopenharmony_ci	if (dmics == 0) {
222c72fcc34Sopenharmony_ci		fprintf(stderr, "No dmic endpoint found\n");
223c72fcc34Sopenharmony_ci		return -EINVAL;
224c72fcc34Sopenharmony_ci	}
225c72fcc34Sopenharmony_ci	return 0;
226c72fcc34Sopenharmony_cioob:
227c72fcc34Sopenharmony_ci	fprintf(stderr, "Data (out-of-bounds) error\n");
228c72fcc34Sopenharmony_ci	return -EINVAL;
229c72fcc34Sopenharmony_ci}
230c72fcc34Sopenharmony_ci
231c72fcc34Sopenharmony_cistatic int nhlt_to_json(FILE *out, const char *nhlt_file)
232c72fcc34Sopenharmony_ci{
233c72fcc34Sopenharmony_ci	struct stat st;
234c72fcc34Sopenharmony_ci	uint8_t *buf;
235c72fcc34Sopenharmony_ci	int _errno, fd, res;
236c72fcc34Sopenharmony_ci	size_t pos, size;
237c72fcc34Sopenharmony_ci	ssize_t ret;
238c72fcc34Sopenharmony_ci
239c72fcc34Sopenharmony_ci	if (stat(nhlt_file, &st))
240c72fcc34Sopenharmony_ci		return -errno;
241c72fcc34Sopenharmony_ci	size = st.st_size;
242c72fcc34Sopenharmony_ci	if (size < 45)
243c72fcc34Sopenharmony_ci		return -EINVAL;
244c72fcc34Sopenharmony_ci	buf = malloc(size);
245c72fcc34Sopenharmony_ci	if (buf == NULL)
246c72fcc34Sopenharmony_ci		return -ENOMEM;
247c72fcc34Sopenharmony_ci	fd = open(nhlt_file, O_RDONLY);
248c72fcc34Sopenharmony_ci	if (fd < 0) {
249c72fcc34Sopenharmony_ci		_errno = errno;
250c72fcc34Sopenharmony_ci		fprintf(stderr, "Unable to open file '%s': %s\n", nhlt_file, strerror(errno));
251c72fcc34Sopenharmony_ci		free(buf);
252c72fcc34Sopenharmony_ci		return _errno;
253c72fcc34Sopenharmony_ci	}
254c72fcc34Sopenharmony_ci	pos = 0;
255c72fcc34Sopenharmony_ci	while (pos < size) {
256c72fcc34Sopenharmony_ci		ret = read(fd, buf + pos, size - pos);
257c72fcc34Sopenharmony_ci		if (ret <= 0) {
258c72fcc34Sopenharmony_ci			fprintf(stderr, "Short read\n");
259c72fcc34Sopenharmony_ci			close(fd);
260c72fcc34Sopenharmony_ci			free(buf);
261c72fcc34Sopenharmony_ci			return -EIO;
262c72fcc34Sopenharmony_ci		}
263c72fcc34Sopenharmony_ci		pos += ret;
264c72fcc34Sopenharmony_ci	}
265c72fcc34Sopenharmony_ci	close(fd);
266c72fcc34Sopenharmony_ci	res = nhlt_table_to_json(out, buf, size);
267c72fcc34Sopenharmony_ci	free(buf);
268c72fcc34Sopenharmony_ci	return res;
269c72fcc34Sopenharmony_ci}
270c72fcc34Sopenharmony_ci
271c72fcc34Sopenharmony_ci/*
272c72fcc34Sopenharmony_ci *
273c72fcc34Sopenharmony_ci */
274c72fcc34Sopenharmony_ci
275c72fcc34Sopenharmony_ci#define PROG "nhlt-dmic-info"
276c72fcc34Sopenharmony_ci#define PROG_VERSION "1"
277c72fcc34Sopenharmony_ci
278c72fcc34Sopenharmony_ci#define NHLT_FILE "/sys/firmware/acpi/tables/NHLT"
279c72fcc34Sopenharmony_ci
280c72fcc34Sopenharmony_ci#define TITLE	0x0100
281c72fcc34Sopenharmony_ci#define HEADER	0x0200
282c72fcc34Sopenharmony_ci#define FILEARG 0x0400
283c72fcc34Sopenharmony_ci
284c72fcc34Sopenharmony_ci#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
285c72fcc34Sopenharmony_ci
286c72fcc34Sopenharmony_cistruct arg {
287c72fcc34Sopenharmony_ci	int sarg;
288c72fcc34Sopenharmony_ci	char *larg;
289c72fcc34Sopenharmony_ci	char *comment;
290c72fcc34Sopenharmony_ci};
291c72fcc34Sopenharmony_ci
292c72fcc34Sopenharmony_cistatic struct arg args[] = {
293c72fcc34Sopenharmony_ci{ TITLE, NULL, "Usage: nhtl-dmic-json <options>" },
294c72fcc34Sopenharmony_ci{ HEADER, NULL, "global options:" },
295c72fcc34Sopenharmony_ci{ 'h', "help", "this help" },
296c72fcc34Sopenharmony_ci{ 'v', "version", "print version of this program" },
297c72fcc34Sopenharmony_ci{ FILEARG | 'f', "file", "NHLT file (default " NHLT_FILE ")" },
298c72fcc34Sopenharmony_ci{ FILEARG | 'o', "output", "output file" },
299c72fcc34Sopenharmony_ci{ 0, NULL, NULL }
300c72fcc34Sopenharmony_ci};
301c72fcc34Sopenharmony_ci
302c72fcc34Sopenharmony_cistatic void help(void)
303c72fcc34Sopenharmony_ci{
304c72fcc34Sopenharmony_ci	struct arg *n = args, *a;
305c72fcc34Sopenharmony_ci	char *larg, sa[4], buf[32];
306c72fcc34Sopenharmony_ci	int sarg;
307c72fcc34Sopenharmony_ci
308c72fcc34Sopenharmony_ci	sa[0] = '-';
309c72fcc34Sopenharmony_ci	sa[2] = ',';
310c72fcc34Sopenharmony_ci	sa[3] = '\0';
311c72fcc34Sopenharmony_ci	while (n->comment) {
312c72fcc34Sopenharmony_ci		a = n;
313c72fcc34Sopenharmony_ci		n++;
314c72fcc34Sopenharmony_ci		sarg = a->sarg;
315c72fcc34Sopenharmony_ci		if (sarg & (HEADER|TITLE)) {
316c72fcc34Sopenharmony_ci			printf("%s%s\n", (sarg & HEADER) != 0 ? "\n" : "",
317c72fcc34Sopenharmony_ci								a->comment);
318c72fcc34Sopenharmony_ci			continue;
319c72fcc34Sopenharmony_ci		}
320c72fcc34Sopenharmony_ci		buf[0] = '\0';
321c72fcc34Sopenharmony_ci		larg = a->larg;
322c72fcc34Sopenharmony_ci		sa[1] = a->sarg;
323c72fcc34Sopenharmony_ci		sprintf(buf, "%s%s%s", sa[1] ? sa : "",
324c72fcc34Sopenharmony_ci				larg ? "--" : "", larg ? larg : "");
325c72fcc34Sopenharmony_ci		if (sarg & FILEARG)
326c72fcc34Sopenharmony_ci			strcat(buf, " #");
327c72fcc34Sopenharmony_ci		printf("  %-15s  %s\n", buf, a->comment);
328c72fcc34Sopenharmony_ci	}
329c72fcc34Sopenharmony_ci}
330c72fcc34Sopenharmony_ci
331c72fcc34Sopenharmony_ciint main(int argc, char *argv[])
332c72fcc34Sopenharmony_ci{
333c72fcc34Sopenharmony_ci	char *nhlt_file = NHLT_FILE;
334c72fcc34Sopenharmony_ci	char *output_file = "-";
335c72fcc34Sopenharmony_ci	int i, j, k, res;
336c72fcc34Sopenharmony_ci	struct arg *a;
337c72fcc34Sopenharmony_ci	struct option *o, *long_option;
338c72fcc34Sopenharmony_ci	char *short_option;
339c72fcc34Sopenharmony_ci	FILE *output = NULL;
340c72fcc34Sopenharmony_ci
341c72fcc34Sopenharmony_ci	long_option = calloc(ARRAY_SIZE(args), sizeof(struct option));
342c72fcc34Sopenharmony_ci	if (long_option == NULL)
343c72fcc34Sopenharmony_ci		exit(EXIT_FAILURE);
344c72fcc34Sopenharmony_ci	short_option = malloc(128);
345c72fcc34Sopenharmony_ci	if (short_option == NULL) {
346c72fcc34Sopenharmony_ci		free(long_option);
347c72fcc34Sopenharmony_ci		exit(EXIT_FAILURE);
348c72fcc34Sopenharmony_ci	}
349c72fcc34Sopenharmony_ci	for (i = j = k = 0; i < (int)ARRAY_SIZE(args); i++) {
350c72fcc34Sopenharmony_ci		a = &args[i];
351c72fcc34Sopenharmony_ci		if ((a->sarg & 0xff) == 0)
352c72fcc34Sopenharmony_ci			continue;
353c72fcc34Sopenharmony_ci		o = &long_option[j];
354c72fcc34Sopenharmony_ci		o->name = a->larg;
355c72fcc34Sopenharmony_ci		o->has_arg = (a->sarg & FILEARG) != 0;
356c72fcc34Sopenharmony_ci		o->flag = NULL;
357c72fcc34Sopenharmony_ci		o->val = a->sarg & 0xff;
358c72fcc34Sopenharmony_ci		j++;
359c72fcc34Sopenharmony_ci		short_option[k++] = o->val;
360c72fcc34Sopenharmony_ci		if (o->has_arg)
361c72fcc34Sopenharmony_ci			short_option[k++] = ':';
362c72fcc34Sopenharmony_ci	}
363c72fcc34Sopenharmony_ci	short_option[k] = '\0';
364c72fcc34Sopenharmony_ci	while (1) {
365c72fcc34Sopenharmony_ci		int c;
366c72fcc34Sopenharmony_ci
367c72fcc34Sopenharmony_ci		if ((c = getopt_long(argc, argv, short_option, long_option,
368c72fcc34Sopenharmony_ci								  NULL)) < 0)
369c72fcc34Sopenharmony_ci			break;
370c72fcc34Sopenharmony_ci		switch (c) {
371c72fcc34Sopenharmony_ci		case 'h':
372c72fcc34Sopenharmony_ci			help();
373c72fcc34Sopenharmony_ci			res = EXIT_SUCCESS;
374c72fcc34Sopenharmony_ci			goto out;
375c72fcc34Sopenharmony_ci		case 'f':
376c72fcc34Sopenharmony_ci			nhlt_file = optarg;
377c72fcc34Sopenharmony_ci			break;
378c72fcc34Sopenharmony_ci		case 'o':
379c72fcc34Sopenharmony_ci			output_file = optarg;
380c72fcc34Sopenharmony_ci			break;
381c72fcc34Sopenharmony_ci		case 'd':
382c72fcc34Sopenharmony_ci			debug = 1;
383c72fcc34Sopenharmony_ci			break;
384c72fcc34Sopenharmony_ci		case 'v':
385c72fcc34Sopenharmony_ci			printf(PROG " version " PROG_VERSION "\n");
386c72fcc34Sopenharmony_ci			res = EXIT_SUCCESS;
387c72fcc34Sopenharmony_ci			goto out;
388c72fcc34Sopenharmony_ci		case '?':		// error msg already printed
389c72fcc34Sopenharmony_ci			help();
390c72fcc34Sopenharmony_ci			res = EXIT_FAILURE;
391c72fcc34Sopenharmony_ci			goto out;
392c72fcc34Sopenharmony_ci		default:		// should never happen
393c72fcc34Sopenharmony_ci			fprintf(stderr,
394c72fcc34Sopenharmony_ci			"Invalid option '%c' (%d) not handled??\n", c, c);
395c72fcc34Sopenharmony_ci		}
396c72fcc34Sopenharmony_ci	}
397c72fcc34Sopenharmony_ci	free(short_option);
398c72fcc34Sopenharmony_ci	short_option = NULL;
399c72fcc34Sopenharmony_ci	free(long_option);
400c72fcc34Sopenharmony_ci	long_option = NULL;
401c72fcc34Sopenharmony_ci
402c72fcc34Sopenharmony_ci	if (strcmp(output_file, "-") == 0) {
403c72fcc34Sopenharmony_ci		output = stdout;
404c72fcc34Sopenharmony_ci	} else {
405c72fcc34Sopenharmony_ci		output = fopen(output_file, "w+");
406c72fcc34Sopenharmony_ci		if (output == NULL) {
407c72fcc34Sopenharmony_ci			fprintf(stderr, "Unable to create output file \"%s\": %s\n",
408c72fcc34Sopenharmony_ci						output_file, strerror(errno));
409c72fcc34Sopenharmony_ci			res = EXIT_FAILURE;
410c72fcc34Sopenharmony_ci			goto out;
411c72fcc34Sopenharmony_ci		}
412c72fcc34Sopenharmony_ci	}
413c72fcc34Sopenharmony_ci
414c72fcc34Sopenharmony_ci	if (argc - optind > 0)
415c72fcc34Sopenharmony_ci		fprintf(stderr, PROG ": Ignoring extra parameters\n");
416c72fcc34Sopenharmony_ci
417c72fcc34Sopenharmony_ci	res = 0;
418c72fcc34Sopenharmony_ci	if (nhlt_to_json(output, nhlt_file))
419c72fcc34Sopenharmony_ci		res = EXIT_FAILURE;
420c72fcc34Sopenharmony_ci
421c72fcc34Sopenharmony_ciout:
422c72fcc34Sopenharmony_ci	if (output)
423c72fcc34Sopenharmony_ci		fclose(output);
424c72fcc34Sopenharmony_ci	free(short_option);
425c72fcc34Sopenharmony_ci	free(long_option);
426c72fcc34Sopenharmony_ci	return res;
427c72fcc34Sopenharmony_ci}
428