1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2019 Intel Corporation. All rights reserved.
4 //
5 // Author: Adrian Bonislawski <adrian.bonislawski@intel.com>
6 
7 /*
8  * Probes will extract data for several probe points in one stream
9  * with extra headers. This app will read the resulting file,
10  * strip the headers and create wave files for each extracted buffer.
11  *
12  * Usage to parse data and create wave files: ./sof-probes -p data.bin
13  *
14  */
15 
16 #include <ipc/probe.h>
17 #include <sof/math/numbers.h>
18 #include "wave.h"
19 
20 #include <ctype.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdbool.h>
24 #include <stddef.h>
25 #include <stdio.h>
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 
31 #define APP_NAME "sof-probes"
32 
33 #define PACKET_MAX_SIZE	4096	/**< Size limit for probe data packet */
34 #define DATA_READ_LIMIT 1024	/**< Data limit for file read */
35 #define FILES_LIMIT	32	/**< Maximum num of probe output files */
36 #define FILE_PATH_LIMIT 128	/**< Path limit for probe output files */
37 
38 struct wave_files {
39 	FILE *fd;
40 	uint32_t buffer_id;
41 	uint32_t size;
42 	struct wave header;
43 };
44 
45 enum p_state {
46 	READY = 0,		/**< At this stage app is looking for a SYNC word */
47 	SYNC,			/**< SYNC received, copying data */
48 	CHECK			/**< Check crc and save packet if valid */
49 };
50 
51 static uint32_t sample_rate[] = {
52 	8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100,
53 	48000, 64000, 88200, 96000, 128000, 176400, 192000
54 };
55 
usage(void)56 static void usage(void)
57 {
58 	fprintf(stdout, "Usage %s <option(s)> <buffer_id/file>\n\n", APP_NAME);
59 	fprintf(stdout, "%s:\t -p file\tParse extracted file\n\n", APP_NAME);
60 	fprintf(stdout, "%s:\t -h \t\tHelp, usage info\n", APP_NAME);
61 	exit(0);
62 }
63 
write_data(char * path,char * data)64 int write_data(char *path, char *data)
65 {
66 	FILE *fd;
67 
68 	fd = fopen(path, "w");
69 	if (!fd) {
70 		fprintf(stderr, "error: unable to open file %s, error %d\n",
71 			path, errno);
72 		return errno;
73 	}
74 
75 	fprintf(fd, "%s", data);
76 	fclose(fd);
77 
78 	return 0;
79 }
80 
get_buffer_file(struct wave_files * files,uint32_t buffer_id)81 int get_buffer_file(struct wave_files *files, uint32_t buffer_id)
82 {
83 	int i;
84 
85 	for (i = 0; i < FILES_LIMIT; i++) {
86 		if (files[i].buffer_id == buffer_id)
87 			return i;
88 	}
89 	return -1;
90 }
91 
init_wave(struct wave_files * files,uint32_t buffer_id,uint32_t format)92 int init_wave(struct wave_files *files, uint32_t buffer_id, uint32_t format)
93 {
94 	char path[FILE_PATH_LIMIT];
95 	int i;
96 
97 	i = get_buffer_file(files, 0);
98 	if (i == -1) {
99 		fprintf(stderr, "error: too many buffers\n");
100 		exit(0);
101 	}
102 
103 	fprintf(stdout, "%s:\t Creating wave file for buffer id: %d\n",
104 		APP_NAME, buffer_id);
105 
106 	sprintf(path, "buffer_%d.wav", buffer_id);
107 
108 	files[i].fd = fopen(path, "wb");
109 	if (!files[i].fd) {
110 		fprintf(stderr, "error: unable to create file %s, error %d\n",
111 			path, errno);
112 		exit(0);
113 	}
114 
115 	files[i].buffer_id = buffer_id;
116 
117 	files[i].header.riff.chunk_id = HEADER_RIFF;
118 	files[i].header.riff.format = HEADER_WAVE;
119 	files[i].header.fmt.subchunk_id = HEADER_FMT;
120 	files[i].header.fmt.subchunk_size = 16;
121 	files[i].header.fmt.audio_format = 1;
122 	files[i].header.fmt.num_channels = ((format & PROBE_MASK_NB_CHANNELS) >> PROBE_SHIFT_NB_CHANNELS) + 1;
123 	files[i].header.fmt.sample_rate = sample_rate[(format & PROBE_MASK_SAMPLE_RATE) >> PROBE_SHIFT_SAMPLE_RATE];
124 	files[i].header.fmt.bits_per_sample = (((format & PROBE_MASK_CONTAINER_SIZE) >> PROBE_SHIFT_CONTAINER_SIZE) + 1) * 8;
125 	files[i].header.fmt.byte_rate = files[i].header.fmt.sample_rate *
126 					files[i].header.fmt.num_channels *
127 					files[i].header.fmt.bits_per_sample / 8;
128 	files[i].header.fmt.block_align = files[i].header.fmt.num_channels *
129 					  files[i].header.fmt.bits_per_sample / 8;
130 	files[i].header.data.subchunk_id = HEADER_DATA;
131 
132 	fwrite(&files[i].header, sizeof(struct wave), 1, files[i].fd);
133 
134 	return i;
135 }
136 
finalize_wave_files(struct wave_files * files)137 void finalize_wave_files(struct wave_files *files)
138 {
139 	uint32_t i, chunk_size;
140 
141 	/* fill the header at the beginning of each file */
142 	/* and close all opened files */
143 	/* check wave struct to understand the offsets */
144 	for (i = 0; i < FILES_LIMIT; i++) {
145 		if (files[i].fd) {
146 			chunk_size = files[i].size + sizeof(struct wave) -
147 				     offsetof(struct riff_chunk, format);
148 
149 			fseek(files[i].fd, sizeof(uint32_t), SEEK_SET);
150 			fwrite(&chunk_size, sizeof(uint32_t), 1, files[i].fd);
151 			fseek(files[i].fd, sizeof(struct wave) -
152 			      offsetof(struct data_subchunk, subchunk_size),
153 			      SEEK_SET);
154 			fwrite(&files[i].size, sizeof(uint32_t), 1, files[i].fd);
155 
156 			fclose(files[i].fd);
157 		}
158 	}
159 }
160 
validate_data_packet(struct probe_data_packet * data_packet)161 int validate_data_packet(struct probe_data_packet *data_packet)
162 {
163 	uint32_t received_crc;
164 	uint32_t calc_crc;
165 
166 	received_crc = data_packet->checksum;
167 	data_packet->checksum = 0;
168 	calc_crc = crc32(0, (char *)data_packet, sizeof(*data_packet));
169 
170 	if (received_crc == calc_crc) {
171 		return 0;
172 	} else {
173 		fprintf(stderr, "error: data packet for buffer %d is not valid: crc32: %d/%d\n",
174 			data_packet->buffer_id, calc_crc, received_crc);
175 		return -EINVAL;
176 	}
177 }
178 
process_sync(struct probe_data_packet * packet,uint32_t ** w_ptr,uint32_t * total_data_to_copy)179 int process_sync(struct probe_data_packet *packet, uint32_t **w_ptr, uint32_t *total_data_to_copy)
180 {
181 	struct probe_data_packet *temp_packet;
182 
183 	/* request to copy data_size from probe packet */
184 	*total_data_to_copy = packet->data_size_bytes /
185 					 sizeof(uint32_t);
186 	if (packet->data_size_bytes > PACKET_MAX_SIZE) {
187 		temp_packet = realloc(packet,
188 				      sizeof(struct probe_data_packet) + packet->data_size_bytes);
189 		if (!temp_packet)
190 			return -ENOMEM;
191 	}
192 
193 	*w_ptr = (uint32_t *)&packet->data;
194 	return 0;
195 }
196 
parse_data(char * file_in)197 void parse_data(char *file_in)
198 {
199 	FILE *fd_in;
200 	struct wave_files files[FILES_LIMIT];
201 	struct probe_data_packet *packet;
202 	uint32_t data[DATA_READ_LIMIT];
203 	uint32_t total_data_to_copy = 0;
204 	uint32_t data_to_copy = 0;
205 	uint32_t *w_ptr;
206 	int i, j, file;
207 
208 	enum p_state state = READY;
209 
210 	fprintf(stdout, "%s:\t Parsing file: %s\n", APP_NAME, file_in);
211 
212 	fd_in = fopen(file_in, "rb");
213 	if (!fd_in) {
214 		fprintf(stderr, "error: unable to open file %s, error %d\n",
215 			file_in, errno);
216 		exit(0);
217 	}
218 
219 	packet = malloc(PACKET_MAX_SIZE);
220 	if (!packet) {
221 		fprintf(stderr, "error: allocation failed, err %d\n",
222 			errno);
223 		fclose(fd_in);
224 		exit(0);
225 	}
226 	memset(&data, 0, sizeof(uint32_t) * DATA_READ_LIMIT);
227 	memset(&files, 0, sizeof(struct wave_files) * FILES_LIMIT);
228 
229 	/* data read loop to process DATA_READ_LIMIT bytes at each iteration */
230 	do {
231 		i = fread(&data, sizeof(uint32_t), DATA_READ_LIMIT, fd_in);
232 		/* processing all loaded bytes */
233 		for (j = 0; j < i; j++) {
234 			/* SYNC received */
235 			if (data[j] == PROBE_EXTRACT_SYNC_WORD) {
236 				if (state != READY) {
237 					fprintf(stderr, "error: wrong state %d, err %d\n",
238 						state, errno);
239 					free(packet);
240 					exit(0);
241 				}
242 				memset(packet, 0, PACKET_MAX_SIZE);
243 				/* request to copy full data packet */
244 				total_data_to_copy = sizeof(struct probe_data_packet) /
245 					sizeof(uint32_t);
246 				w_ptr = (uint32_t *)packet;
247 				state = SYNC;
248 			}
249 			/* data copying section */
250 			if (total_data_to_copy > 0) {
251 				/* check if there is enough bytes loaded */
252 				/* or copy partially if not */
253 				if (j + total_data_to_copy > i) {
254 					data_to_copy = i - j;
255 					total_data_to_copy -= data_to_copy;
256 				} else {
257 					data_to_copy = total_data_to_copy;
258 					total_data_to_copy = 0;
259 				}
260 				memcpy(w_ptr, data + j, data_to_copy * sizeof(uint32_t));
261 				w_ptr += data_to_copy;
262 				j += data_to_copy - 1;
263 			}
264 
265 			if (total_data_to_copy == 0) {
266 				switch (state) {
267 				case READY:
268 					break;
269 				case SYNC:
270 					/* SYNC -> CHECK */
271 					if (process_sync(packet, &w_ptr, &total_data_to_copy) < 0) {
272 						fprintf(stderr, "OOM, quitting\n");
273 						goto err;
274 					}
275 					state = CHECK;
276 					break;
277 				case CHECK:
278 					/* CHECK -> READY */
279 					/* find corresponding file and save data if valid */
280 					if (validate_data_packet(packet) == 0) {
281 						file = get_buffer_file(files,
282 								       packet->buffer_id);
283 
284 						if (file < 0)
285 							file = init_wave(files,
286 									 packet->buffer_id,
287 									 packet->format);
288 
289 						fwrite(packet->data,
290 						       sizeof(uint32_t),
291 						       packet->data_size_bytes /
292 						       sizeof(uint32_t),
293 						       files[file].fd);
294 
295 						files[file].size += packet->data_size_bytes;
296 					}
297 					state = READY;
298 					break;
299 				}
300 			}
301 		}
302 	} while (i > 0);
303 
304 err:
305 	/* all done, can close files */
306 	finalize_wave_files(files);
307 	free(packet);
308 	fclose(fd_in);
309 	fprintf(stdout, "%s:\t done\n", APP_NAME);
310 }
311 
main(int argc,char * argv[])312 int main(int argc, char *argv[])
313 {
314 	int opt;
315 
316 	while ((opt = getopt(argc, argv, "hp:")) != -1) {
317 		switch (opt) {
318 		case 'p':
319 			parse_data(optarg);
320 			break;
321 		case 'h':
322 		default:
323 			usage();
324 		}
325 	}
326 
327 	return 0;
328 }
329