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