1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2022 Intel Corporation. All rights reserved.
4 //
5 // Author: Jyri Sarha <jyri.sarha@intel.com>
6
7 #include <sof/audio/module_adapter/module/generic.h>
8 #include <rtos/sof.h>
9 #include <rtos/alloc.h>
10 #include <ipc/topology.h>
11 #include <ipc/control.h>
12 #include <sof/audio/component.h>
13 #include <sof/audio/data_blob.h>
14
15 LOG_MODULE_REGISTER(data_blob, CONFIG_SOF_LOG_LEVEL);
16
17 /** \brief Struct handler for large component configs */
18 struct comp_data_blob_handler {
19 struct comp_dev *dev; /**< audio component device */
20 uint32_t data_size; /**< size of component's data blob */
21 uint32_t new_data_size; /**< size of component's new data blob */
22 void *data; /**< pointer to data blob */
23 void *data_new; /**< pointer to new data blob */
24 bool data_ready; /**< set when data blob is fully received */
25 uint32_t data_pos; /**< indicates a data position in data
26 * sending/receiving process
27 */
28 uint32_t single_blob:1; /**< Allocate only one blob. Module can not
29 * be active while reconfguring.
30 */
31 void *(*alloc)(size_t size); /**< alternate allocator, maybe null */
32 void (*free)(void *buf); /**< alternate free(), maybe null */
33
34 /** validator for new data, maybe null */
35 int (*validator)(struct comp_dev *dev, void *new_data, uint32_t new_data_size);
36 };
37
comp_free_data_blob(struct comp_data_blob_handler * blob_handler)38 static void comp_free_data_blob(struct comp_data_blob_handler *blob_handler)
39 {
40 assert(blob_handler);
41
42 if (!blob_handler->data)
43 return;
44
45 blob_handler->free(blob_handler->data);
46 blob_handler->free(blob_handler->data_new);
47 blob_handler->data = NULL;
48 blob_handler->data_new = NULL;
49 blob_handler->data_size = 0;
50 }
51
comp_data_blob_set_validator(struct comp_data_blob_handler * blob_handler,int (* validator)(struct comp_dev * dev,void * new_data,uint32_t new_data_size))52 void comp_data_blob_set_validator(struct comp_data_blob_handler *blob_handler,
53 int (*validator)(struct comp_dev *dev, void *new_data,
54 uint32_t new_data_size))
55 {
56 assert(blob_handler);
57
58 blob_handler->validator = validator;
59 }
60
comp_get_data_blob(struct comp_data_blob_handler * blob_handler,size_t * size,uint32_t * crc)61 void *comp_get_data_blob(struct comp_data_blob_handler *blob_handler,
62 size_t *size, uint32_t *crc)
63 {
64 assert(blob_handler);
65
66 comp_dbg(blob_handler->dev, "comp_get_data_blob()");
67
68 if (size)
69 *size = 0;
70
71 /* Function returns new data blob if available */
72 if (comp_is_new_data_blob_available(blob_handler)) {
73 comp_dbg(blob_handler->dev, "comp_get_data_blob(): new data available");
74
75 /* Free "old" data blob and set data to data_new pointer */
76 blob_handler->free(blob_handler->data);
77 blob_handler->data = blob_handler->data_new;
78 blob_handler->data_size = blob_handler->new_data_size;
79
80 blob_handler->data_new = NULL;
81 blob_handler->data_ready = false;
82 blob_handler->new_data_size = 0;
83 blob_handler->data_pos = 0;
84 }
85
86 /* If data is available we calculate crc32 when crc pointer is given */
87 if (blob_handler->data) {
88 if (crc)
89 *crc = crc32(0, blob_handler->data,
90 blob_handler->data_size);
91 } else {
92 /* If blob_handler->data is equal to NULL and there is no new
93 * data blob it means that component hasn't got any config yet.
94 * Function returns NULL in that case.
95 */
96 comp_warn(blob_handler->dev, "comp_get_data_blob(): blob_handler->data is not set.");
97 }
98
99 if (size)
100 *size = blob_handler->data_size;
101
102 return blob_handler->data;
103 }
104
comp_is_new_data_blob_available(struct comp_data_blob_handler * blob_handler)105 bool comp_is_new_data_blob_available(struct comp_data_blob_handler
106 *blob_handler)
107 {
108 assert(blob_handler);
109
110 comp_dbg(blob_handler->dev, "comp_is_new_data_blob_available()");
111
112 /* New data blob is available when new data blob is allocated (data_new
113 * is not NULL), and the component has received all required chunks of data
114 * (data_ready is set to TRUE)
115 */
116 if (blob_handler->data_new && blob_handler->data_ready)
117 return true;
118
119 return false;
120 }
121
comp_is_current_data_blob_valid(struct comp_data_blob_handler * blob_handler)122 bool comp_is_current_data_blob_valid(struct comp_data_blob_handler
123 *blob_handler)
124 {
125 return !!blob_handler->data;
126 }
127
comp_init_data_blob(struct comp_data_blob_handler * blob_handler,uint32_t size,const void * init_data)128 int comp_init_data_blob(struct comp_data_blob_handler *blob_handler,
129 uint32_t size, const void *init_data)
130 {
131 int ret;
132
133 assert(blob_handler);
134
135 comp_free_data_blob(blob_handler);
136
137 if (!size)
138 return 0;
139
140 /* Data blob allocation */
141 blob_handler->data = blob_handler->alloc(size);
142 if (!blob_handler->data) {
143 comp_err(blob_handler->dev, "comp_init_data_blob(): model->data allocation failed");
144 return -ENOMEM;
145 }
146
147 /* If init_data is given, data will be initialized with it. In other
148 * case, data will be set to zero.
149 */
150 if (init_data) {
151 ret = memcpy_s(blob_handler->data, size, init_data, size);
152 assert(!ret);
153 } else {
154 bzero(blob_handler->data, size);
155 }
156
157 blob_handler->data_new = NULL;
158 blob_handler->data_size = size;
159 blob_handler->new_data_size = 0;
160 blob_handler->validator = NULL;
161
162 return 0;
163 }
164
comp_data_blob_set(struct comp_data_blob_handler * blob_handler,enum module_cfg_fragment_position pos,uint32_t data_offset_size,const uint8_t * fragment_in,size_t fragment_size)165 int comp_data_blob_set(struct comp_data_blob_handler *blob_handler,
166 enum module_cfg_fragment_position pos, uint32_t data_offset_size,
167 const uint8_t *fragment_in, size_t fragment_size)
168 {
169 #if CONFIG_IPC_MAJOR_3
170 struct sof_ipc_ctrl_data *cdata = (struct sof_ipc_ctrl_data *)fragment_in;
171 const uint8_t *fragment = (const uint8_t *)cdata->data[0].data;
172 #elif CONFIG_IPC_MAJOR_4
173 const uint8_t *fragment = fragment_in;
174 #endif
175 int ret;
176
177 if (!blob_handler)
178 return -EINVAL;
179
180 #if CONFIG_IPC_MAJOR_3
181 if (cdata->cmd != SOF_CTRL_CMD_BINARY) {
182 comp_err(blob_handler->dev, "comp_data_blob_set_cmd(), illegal control command");
183 return -EINVAL;
184 }
185 #endif
186
187 comp_dbg(blob_handler->dev, "comp_data_blob_set_cmd() pos = %d, fragment size = %d",
188 pos, fragment_size);
189
190 /* Check that there is no work-in-progress previous request */
191 if (blob_handler->data_new &&
192 (pos == MODULE_CFG_FRAGMENT_FIRST || pos == MODULE_CFG_FRAGMENT_SINGLE)) {
193 comp_err(blob_handler->dev, "comp_data_blob_set_cmd(), busy with previous request");
194 return -EBUSY;
195 }
196
197 /* In single blob mode the component can not be reconfigured if the component is active.
198 */
199 if (blob_handler->single_blob && blob_handler->dev->state == COMP_STATE_ACTIVE) {
200 comp_err(blob_handler->dev, "comp_data_blob_set_cmd(), on the fly updates forbidden in single blob mode");
201 return -EBUSY;
202 }
203
204 /* in case when the current package is the first, we should allocate
205 * memory for whole model data
206 */
207 if (pos == MODULE_CFG_FRAGMENT_FIRST || pos == MODULE_CFG_FRAGMENT_SINGLE) {
208 /* in case when required model size is equal to zero we do not
209 * allocate memory and should just return 0.
210 *
211 * Set cmd with cdata->data->size equal to 0 is possible in
212 * following situation:
213 * 1. At first boot and topology parsing stage, the driver will
214 * read all initial values of DSP kcontrols via IPC. Driver send
215 * get_model() cmd to components. If we do not initialize
216 * component earlier driver will get "model" with size 0.
217 * 2. When resuming from runtime suspended, the driver will
218 * restore all pipelines and kcontrols, for the tlv binary
219 * kcontrols, it will call the set_model() with the cached value
220 * and size (0 if it is not updated by any actual end user
221 * sof-ctl settings) - basically driver will send set_model()
222 * command with size equal to 0.
223 */
224 if (!fragment_size)
225 return 0;
226
227 if (blob_handler->single_blob) {
228 if (data_offset_size != blob_handler->data_size) {
229 blob_handler->free(blob_handler->data);
230 blob_handler->data = NULL;
231 } else {
232 blob_handler->data_new = blob_handler->data;
233 blob_handler->data = NULL;
234 }
235 }
236
237 if (!blob_handler->data_new) {
238 blob_handler->data_new = blob_handler->alloc(data_offset_size);
239 if (!blob_handler->data_new) {
240 comp_err(blob_handler->dev, "comp_data_blob_set_cmd(): blob_handler->data_new allocation failed.");
241 return -ENOMEM;
242 }
243 }
244
245 blob_handler->new_data_size = data_offset_size;
246 blob_handler->data_ready = false;
247 blob_handler->data_pos = 0;
248 }
249
250 /* return an error in case when we do not have allocated memory for model data */
251 if (!blob_handler->data_new) {
252 comp_err(blob_handler->dev, "comp_data_blob_set_cmd(): buffer not allocated");
253 return -ENOMEM;
254 }
255
256 ret = memcpy_s((char *)blob_handler->data_new + blob_handler->data_pos,
257 blob_handler->new_data_size - blob_handler->data_pos,
258 fragment, fragment_size);
259 if (ret) {
260 comp_err(blob_handler->dev, "comp_data_blob_set_cmd(): failed to copy fragment");
261 return ret;
262 }
263
264 blob_handler->data_pos += fragment_size;
265
266 if (pos == MODULE_CFG_FRAGMENT_SINGLE || pos == MODULE_CFG_FRAGMENT_LAST) {
267 comp_dbg(blob_handler->dev, "comp_data_blob_set_cmd(): final package received");
268 if (blob_handler->validator) {
269 comp_dbg(blob_handler->dev, "comp_data_blob_set_cmd(): validating new data...");
270 ret = blob_handler->validator(blob_handler->dev, blob_handler->data_new,
271 blob_handler->new_data_size);
272 if (ret < 0) {
273 comp_err(blob_handler->dev, "comp_data_blob_set_cmd(): new data is invalid! discarding it...");
274 blob_handler->free(blob_handler->data_new);
275 blob_handler->data_new = NULL;
276 return ret;
277 }
278 }
279
280 /* If component state is READY we can omit old
281 * configuration immediately. When in playback/capture
282 * the new configuration presence is checked in copy().
283 */
284 if (blob_handler->dev->state == COMP_STATE_READY) {
285 blob_handler->free(blob_handler->data);
286 blob_handler->data = NULL;
287 }
288
289 /* If there is no existing configuration the received
290 * can be set to current immediately. It will be
291 * applied in prepare() when streaming starts.
292 */
293 if (!blob_handler->data) {
294 blob_handler->data = blob_handler->data_new;
295 blob_handler->data_size = blob_handler->new_data_size;
296
297 blob_handler->data_new = NULL;
298
299 /* The new configuration has been applied */
300 blob_handler->data_ready = false;
301 blob_handler->new_data_size = 0;
302 blob_handler->data_pos = 0;
303 } else {
304 /* The new configuration is ready to be applied */
305 blob_handler->data_ready = true;
306 }
307 }
308
309 return 0;
310 }
311
ipc4_comp_data_blob_set(struct comp_data_blob_handler * blob_handler,bool first_block,bool last_block,uint32_t data_offset,const char * data)312 int ipc4_comp_data_blob_set(struct comp_data_blob_handler *blob_handler,
313 bool first_block,
314 bool last_block,
315 uint32_t data_offset,
316 const char *data)
317 {
318 int ret;
319 int valid_data_size;
320
321 assert(blob_handler);
322
323 comp_dbg(blob_handler->dev,
324 "ipc4_comp_data_blob_set(): data_offset = %d",
325 data_offset);
326
327 /* in case when the current package is the first, we should allocate
328 * memory for whole model data
329 */
330 if (first_block) {
331 if (!data_offset)
332 return 0;
333
334 if (blob_handler->single_blob) {
335 if (data_offset != blob_handler->data_size) {
336 blob_handler->free(blob_handler->data);
337 blob_handler->data = NULL;
338 } else {
339 blob_handler->data_new = blob_handler->data;
340 blob_handler->data = NULL;
341 }
342 }
343
344 if (!blob_handler->data_new) {
345 blob_handler->data_new =
346 blob_handler->alloc(data_offset);
347
348 if (!blob_handler->data_new) {
349 comp_err(blob_handler->dev,
350 "ipc4_comp_data_blob_set(): blob_handler allocation failed!");
351 return -ENOMEM;
352 }
353 }
354
355 blob_handler->new_data_size = data_offset;
356 blob_handler->data_ready = false;
357 blob_handler->data_pos = 0;
358
359 valid_data_size = last_block ? data_offset : MAILBOX_DSPBOX_SIZE;
360
361 ret = memcpy_s((char *)blob_handler->data_new,
362 valid_data_size, data, valid_data_size);
363 assert(!ret);
364
365 blob_handler->data_pos += valid_data_size;
366 } else {
367 /* return an error in case when we do not have allocated memory for
368 * model data
369 */
370 if (!blob_handler->data_new) {
371 comp_err(blob_handler->dev,
372 "ipc4_comp_data_blob_set(): Buffer not allocated!");
373 return -ENOMEM;
374 }
375
376 if (blob_handler->data_pos != data_offset) {
377 comp_err(blob_handler->dev,
378 "ipc4_comp_data_blob_set(): Wrong data offset received!");
379 return -EINVAL;
380 }
381
382 valid_data_size = MAILBOX_DSPBOX_SIZE;
383
384 if (last_block)
385 valid_data_size = blob_handler->new_data_size - data_offset;
386
387 ret = memcpy_s((char *)blob_handler->data_new + data_offset,
388 valid_data_size, data, valid_data_size);
389 assert(!ret);
390
391 blob_handler->data_pos += valid_data_size;
392 }
393
394 if (last_block) {
395 comp_dbg(blob_handler->dev,
396 "ipc4_comp_data_blob_set(): final package received");
397
398 /* If component state is READY we can omit old
399 * configuration immediately. When in playback/capture
400 * the new configuration presence is checked in copy().
401 */
402 if (blob_handler->dev->state == COMP_STATE_READY) {
403 blob_handler->free(blob_handler->data);
404 blob_handler->data = NULL;
405 }
406
407 /* If there is no existing configuration the received
408 * can be set to current immediately. It will be
409 * applied in prepare() when streaming starts.
410 */
411 if (!blob_handler->data) {
412 blob_handler->data = blob_handler->data_new;
413 blob_handler->data_size = blob_handler->new_data_size;
414
415 blob_handler->data_new = NULL;
416
417 /* The new configuration has been applied */
418 blob_handler->data_ready = false;
419 blob_handler->new_data_size = 0;
420 blob_handler->data_pos = 0;
421 } else {
422 /* The new configuration is ready to be applied */
423 blob_handler->data_ready = true;
424 }
425 }
426
427 return 0;
428 }
429
comp_data_blob_set_cmd(struct comp_data_blob_handler * blob_handler,struct sof_ipc_ctrl_data * cdata)430 int comp_data_blob_set_cmd(struct comp_data_blob_handler *blob_handler,
431 struct sof_ipc_ctrl_data *cdata)
432 {
433 int ret = 0;
434
435 assert(blob_handler);
436
437 comp_dbg(blob_handler->dev, "comp_data_blob_set_cmd() msg_index = %d, num_elems = %d, remaining = %d ",
438 cdata->msg_index, cdata->num_elems,
439 cdata->elems_remaining);
440
441 /* Check that there is no work-in-progress previous request */
442 if (blob_handler->data_new && cdata->msg_index == 0) {
443 comp_err(blob_handler->dev, "comp_data_blob_set_cmd(), busy with previous request");
444 return -EBUSY;
445 }
446
447 /* In single blob mode the component can not be reconfigured if
448 * the component is active.
449 */
450 if (blob_handler->single_blob &&
451 blob_handler->dev->state == COMP_STATE_ACTIVE) {
452 comp_err(blob_handler->dev, "comp_data_blob_set_cmd(), on the fly updates forbidden in single blob mode");
453 return -EBUSY;
454 }
455
456 /* in case when the current package is the first, we should allocate
457 * memory for whole model data
458 */
459 if (!cdata->msg_index) {
460 /* in case when required model size is equal to zero we do not
461 * allocate memory and should just return 0.
462 *
463 * Set cmd with cdata->data->size equal to 0 is possible in
464 * following situation:
465 * 1. At first boot and topology parsing stage, the driver will
466 * read all initial values of DSP kcontrols via IPC. Driver send
467 * get_model() cmd to components. If we do not initialize
468 * component earlier driver will get "model" with size 0.
469 * 2. When resuming from runtime suspended, the driver will
470 * restore all pipelines and kcontrols, for the tlv binary
471 * kcontrols, it will call the set_model() with the cached value
472 * and size (0 if it is not updated by any actual end user
473 * sof-ctl settings) - basically driver will send set_model()
474 * command with size equal to 0.
475 */
476 if (!cdata->data->size)
477 return 0;
478
479 if (blob_handler->single_blob) {
480 if (cdata->data->size != blob_handler->data_size) {
481 blob_handler->free(blob_handler->data);
482 blob_handler->data = NULL;
483 } else {
484 blob_handler->data_new = blob_handler->data;
485 blob_handler->data = NULL;
486 }
487 }
488
489 if (!blob_handler->data_new) {
490 blob_handler->data_new =
491 blob_handler->alloc(cdata->data->size);
492 if (!blob_handler->data_new) {
493 comp_err(blob_handler->dev, "comp_data_blob_set_cmd(): blob_handler->data_new allocation failed.");
494 return -ENOMEM;
495 }
496 }
497
498 blob_handler->new_data_size = cdata->data->size;
499 blob_handler->data_ready = false;
500 blob_handler->data_pos = 0;
501 }
502
503 /* return an error in case when we do not have allocated memory for
504 * model data
505 */
506 if (!blob_handler->data_new) {
507 comp_err(blob_handler->dev, "comp_data_blob_set_cmd(): buffer not allocated");
508 return -ENOMEM;
509 }
510
511 ret = memcpy_s((char *)blob_handler->data_new + blob_handler->data_pos,
512 blob_handler->new_data_size - blob_handler->data_pos,
513 cdata->data->data, cdata->num_elems);
514 assert(!ret);
515
516 blob_handler->data_pos += cdata->num_elems;
517
518 if (!cdata->elems_remaining) {
519 comp_dbg(blob_handler->dev, "comp_data_blob_set_cmd(): final package received");
520
521 if (blob_handler->validator) {
522 comp_dbg(blob_handler->dev, "comp_data_blob_set_cmd(): validating new data blob");
523 ret = blob_handler->validator(blob_handler->dev, blob_handler->data_new,
524 blob_handler->new_data_size);
525 if (ret < 0) {
526 comp_err(blob_handler->dev, "comp_data_blob_set_cmd(): new data blob invalid, discarding");
527 blob_handler->free(blob_handler->data_new);
528 blob_handler->data_new = NULL;
529 return ret;
530 }
531 }
532
533 /* If component state is READY we can omit old
534 * configuration immediately. When in playback/capture
535 * the new configuration presence is checked in copy().
536 */
537 if (blob_handler->dev->state == COMP_STATE_READY) {
538 blob_handler->free(blob_handler->data);
539 blob_handler->data = NULL;
540 }
541
542 /* If there is no existing configuration the received
543 * can be set to current immediately. It will be
544 * applied in prepare() when streaming starts.
545 */
546 if (!blob_handler->data) {
547 blob_handler->data = blob_handler->data_new;
548 blob_handler->data_size = blob_handler->new_data_size;
549
550 blob_handler->data_new = NULL;
551
552 /* The new configuration has been applied */
553 blob_handler->data_ready = false;
554 blob_handler->new_data_size = 0;
555 blob_handler->data_pos = 0;
556 } else {
557 /* The new configuration is ready to be applied */
558 blob_handler->data_ready = true;
559 }
560 }
561
562 return 0;
563 }
564
comp_data_blob_get_cmd(struct comp_data_blob_handler * blob_handler,struct sof_ipc_ctrl_data * cdata,int size)565 int comp_data_blob_get_cmd(struct comp_data_blob_handler *blob_handler,
566 struct sof_ipc_ctrl_data *cdata, int size)
567 {
568 int ret = 0;
569
570 assert(blob_handler);
571
572 if (cdata->cmd != SOF_CTRL_CMD_BINARY) {
573 comp_err(blob_handler->dev, "comp_data_blob_set_cmd(), illegal control command");
574 return -EINVAL;
575 }
576
577 comp_dbg(blob_handler->dev, "comp_data_blob_get_cmd() msg_index = %d, num_elems = %d, remaining = %d ",
578 cdata->msg_index, cdata->num_elems,
579 cdata->elems_remaining);
580
581 /* Copy back to user space */
582 if (blob_handler->data) {
583 /* reset data_pos variable in case of copying first element */
584 if (!cdata->msg_index) {
585 blob_handler->data_pos = 0;
586 comp_dbg(blob_handler->dev, "comp_data_blob_get_cmd() model data_size = 0x%x",
587 blob_handler->data_size);
588 }
589
590 /* return an error in case of mismatch between num_elems and
591 * required size
592 */
593 if (cdata->num_elems > size) {
594 comp_err(blob_handler->dev, "comp_data_blob_get_cmd(): invalid cdata->num_elems %d",
595 cdata->num_elems);
596 return -EINVAL;
597 }
598
599 /* copy required size of data */
600 ret = memcpy_s(cdata->data->data, size,
601 (char *)blob_handler->data + blob_handler->data_pos,
602 cdata->num_elems);
603 assert(!ret);
604
605 cdata->data->abi = SOF_ABI_VERSION;
606 cdata->data->size = blob_handler->data_size;
607 blob_handler->data_pos += cdata->num_elems;
608 } else {
609 comp_warn(blob_handler->dev, "comp_data_blob_get_cmd(): model->data not allocated yet.");
610 cdata->data->abi = SOF_ABI_VERSION;
611 cdata->data->size = 0;
612 }
613
614 return ret;
615 }
616
default_alloc(size_t size)617 static void *default_alloc(size_t size)
618 {
619 return rballoc(0, SOF_MEM_CAPS_RAM, size);
620 }
621
default_free(void * buf)622 static void default_free(void *buf)
623 {
624 rfree(buf);
625 }
626
627 struct comp_data_blob_handler *
comp_data_blob_handler_new_ext(struct comp_dev * dev,bool single_blob,void * (* alloc)(size_t size),void (* free)(void * buf))628 comp_data_blob_handler_new_ext(struct comp_dev *dev, bool single_blob,
629 void *(*alloc)(size_t size),
630 void (*free)(void *buf))
631 {
632 struct comp_data_blob_handler *handler;
633
634 comp_dbg(dev, "comp_data_blob_handler_new_ext()");
635
636 handler = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM,
637 sizeof(struct comp_data_blob_handler));
638
639 if (handler) {
640 handler->dev = dev;
641 handler->single_blob = single_blob;
642 handler->alloc = alloc ? alloc : default_alloc;
643 handler->free = free ? free : default_free;
644 }
645
646 return handler;
647 }
648
comp_data_blob_handler_free(struct comp_data_blob_handler * blob_handler)649 void comp_data_blob_handler_free(struct comp_data_blob_handler *blob_handler)
650 {
651 if (!blob_handler)
652 return;
653
654 comp_free_data_blob(blob_handler);
655
656 rfree(blob_handler);
657 }
658