1 /*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "EffectsFactory"
18 //#define LOG_NDEBUG 0
19
20 #include "EffectsFactory.h"
21 #include <string.h>
22 #include <stdlib.h>
23 #include <dlfcn.h>
24
25 #include <cutils/misc.h>
26 #include <cutils/config_utils.h>
27 #include <audio_effects/audio_effects_conf.h>
28
29 static list_elem_t *gEffectList; // list of effect_entry_t: all currently created effects
30 static list_elem_t *gLibraryList; // list of lib_entry_t: all currently loaded libraries
31 static pthread_mutex_t gLibLock = PTHREAD_MUTEX_INITIALIZER; // controls access to gLibraryList
32 static uint32_t gNumEffects; // total number number of effects
33 static list_elem_t *gCurLib; // current library in enumeration process
34 static list_elem_t *gCurEffect; // current effect in enumeration process
35 static uint32_t gCurEffectIdx; // current effect index in enumeration process
36 static lib_entry_t *gCachedLibrary; // last library accessed by getLibrary()
37
38 static int gInitDone; // true is global initialization has been preformed
39 static int gCanQueryEffect; // indicates that call to EffectQueryEffect() is valid, i.e. that the list of effects
40 // was not modified since last call to EffectQueryNumberEffects()
41
42
43 /////////////////////////////////////////////////
44 // Local functions prototypes
45 /////////////////////////////////////////////////
46
47 static int init();
48 static int loadEffectConfigFile(const char *path);
49 static int loadLibraries(cnode *root);
50 static int loadLibrary(cnode *root, const char *name);
51 static int loadEffects(cnode *root);
52 static int loadEffect(cnode *node);
53 static lib_entry_t *getLibrary(const char *path);
54 static void resetEffectEnumeration();
55 static uint32_t updateNumEffects();
56 static int findEffect(const effect_uuid_t *type,
57 const effect_uuid_t *uuid,
58 lib_entry_t **lib,
59 effect_descriptor_t **desc);
60 static void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len);
61 static int stringToUuid(const char *str, effect_uuid_t *uuid);
62 static int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen);
63
64 /////////////////////////////////////////////////
65 // Effect Control Interface functions
66 /////////////////////////////////////////////////
67
Effect_Process(effect_handle_t self,audio_buffer_t * inBuffer,audio_buffer_t * outBuffer)68 int Effect_Process(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
69 {
70 int ret = init();
71 if (ret < 0) {
72 return ret;
73 }
74 effect_entry_t *fx = (effect_entry_t *)self;
75 pthread_mutex_lock(&gLibLock);
76 if (fx->lib == NULL) {
77 pthread_mutex_unlock(&gLibLock);
78 return -EPIPE;
79 }
80 pthread_mutex_lock(&fx->lib->lock);
81 pthread_mutex_unlock(&gLibLock);
82
83 ret = (*fx->subItfe)->process(fx->subItfe, inBuffer, outBuffer);
84 pthread_mutex_unlock(&fx->lib->lock);
85 return ret;
86 }
87
Effect_Command(effect_handle_t self,uint32_t cmdCode,uint32_t cmdSize,void * pCmdData,uint32_t * replySize,void * pReplyData)88 int Effect_Command(effect_handle_t self,
89 uint32_t cmdCode,
90 uint32_t cmdSize,
91 void *pCmdData,
92 uint32_t *replySize,
93 void *pReplyData)
94 {
95 int ret = init();
96 if (ret < 0) {
97 return ret;
98 }
99 effect_entry_t *fx = (effect_entry_t *)self;
100 pthread_mutex_lock(&gLibLock);
101 if (fx->lib == NULL) {
102 pthread_mutex_unlock(&gLibLock);
103 return -EPIPE;
104 }
105 pthread_mutex_lock(&fx->lib->lock);
106 pthread_mutex_unlock(&gLibLock);
107
108 ret = (*fx->subItfe)->command(fx->subItfe, cmdCode, cmdSize, pCmdData, replySize, pReplyData);
109 pthread_mutex_unlock(&fx->lib->lock);
110 return ret;
111 }
112
Effect_GetDescriptor(effect_handle_t self,effect_descriptor_t * desc)113 int Effect_GetDescriptor(effect_handle_t self,
114 effect_descriptor_t *desc)
115 {
116 int ret = init();
117 if (ret < 0) {
118 return ret;
119 }
120 effect_entry_t *fx = (effect_entry_t *)self;
121 pthread_mutex_lock(&gLibLock);
122 if (fx->lib == NULL) {
123 pthread_mutex_unlock(&gLibLock);
124 return -EPIPE;
125 }
126 pthread_mutex_lock(&fx->lib->lock);
127 pthread_mutex_unlock(&gLibLock);
128
129 ret = (*fx->subItfe)->get_descriptor(fx->subItfe, desc);
130 pthread_mutex_unlock(&fx->lib->lock);
131 return ret;
132 }
133
Effect_ProcessReverse(effect_handle_t self,audio_buffer_t * inBuffer,audio_buffer_t * outBuffer)134 int Effect_ProcessReverse(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
135 {
136 int ret = init();
137 if (ret < 0) {
138 return ret;
139 }
140 effect_entry_t *fx = (effect_entry_t *)self;
141 pthread_mutex_lock(&gLibLock);
142 if (fx->lib == NULL) {
143 pthread_mutex_unlock(&gLibLock);
144 return -EPIPE;
145 }
146 pthread_mutex_lock(&fx->lib->lock);
147 pthread_mutex_unlock(&gLibLock);
148
149 if ((*fx->subItfe)->process_reverse != NULL) {
150 ret = (*fx->subItfe)->process_reverse(fx->subItfe, inBuffer, outBuffer);
151 } else {
152 ret = -ENOSYS;
153 }
154 pthread_mutex_unlock(&fx->lib->lock);
155 return ret;
156 }
157
158
159 const struct effect_interface_s gInterface = {
160 Effect_Process,
161 Effect_Command,
162 Effect_GetDescriptor,
163 NULL
164 };
165
166 const struct effect_interface_s gInterfaceWithReverse = {
167 Effect_Process,
168 Effect_Command,
169 Effect_GetDescriptor,
170 Effect_ProcessReverse
171 };
172
173 /////////////////////////////////////////////////
174 // Effect Factory Interface functions
175 /////////////////////////////////////////////////
176
EffectQueryNumberEffects(uint32_t * pNumEffects)177 int EffectQueryNumberEffects(uint32_t *pNumEffects)
178 {
179 int ret = init();
180 if (ret < 0) {
181 return ret;
182 }
183 if (pNumEffects == NULL) {
184 return -EINVAL;
185 }
186
187 pthread_mutex_lock(&gLibLock);
188 *pNumEffects = gNumEffects;
189 gCanQueryEffect = 1;
190 pthread_mutex_unlock(&gLibLock);
191 ALOGV("EffectQueryNumberEffects(): %d", *pNumEffects);
192 return ret;
193 }
194
EffectQueryEffect(uint32_t index,effect_descriptor_t * pDescriptor)195 int EffectQueryEffect(uint32_t index, effect_descriptor_t *pDescriptor)
196 {
197 int ret = init();
198 if (ret < 0) {
199 return ret;
200 }
201 if (pDescriptor == NULL ||
202 index >= gNumEffects) {
203 return -EINVAL;
204 }
205 if (gCanQueryEffect == 0) {
206 return -ENOSYS;
207 }
208
209 pthread_mutex_lock(&gLibLock);
210 ret = -ENOENT;
211 if (index < gCurEffectIdx) {
212 resetEffectEnumeration();
213 }
214 while (gCurLib) {
215 if (gCurEffect) {
216 if (index == gCurEffectIdx) {
217 *pDescriptor = *(effect_descriptor_t *)gCurEffect->object;
218 ret = 0;
219 break;
220 } else {
221 gCurEffect = gCurEffect->next;
222 gCurEffectIdx++;
223 }
224 } else {
225 gCurLib = gCurLib->next;
226 gCurEffect = ((lib_entry_t *)gCurLib->object)->effects;
227 }
228 }
229
230 #if (LOG_NDEBUG == 0)
231 char str[256];
232 dumpEffectDescriptor(pDescriptor, str, 256);
233 ALOGV("EffectQueryEffect() desc:%s", str);
234 #endif
235 pthread_mutex_unlock(&gLibLock);
236 return ret;
237 }
238
EffectGetDescriptor(const effect_uuid_t * uuid,effect_descriptor_t * pDescriptor)239 int EffectGetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor)
240 {
241 lib_entry_t *l = NULL;
242 effect_descriptor_t *d = NULL;
243
244 int ret = init();
245 if (ret < 0) {
246 return ret;
247 }
248 if (pDescriptor == NULL || uuid == NULL) {
249 return -EINVAL;
250 }
251 pthread_mutex_lock(&gLibLock);
252 ret = findEffect(NULL, uuid, &l, &d);
253 if (ret == 0) {
254 *pDescriptor = *d;
255 }
256 pthread_mutex_unlock(&gLibLock);
257 return ret;
258 }
259
EffectCreate(const effect_uuid_t * uuid,int32_t sessionId,int32_t ioId,effect_handle_t * pHandle)260 int EffectCreate(const effect_uuid_t *uuid, int32_t sessionId, int32_t ioId, effect_handle_t *pHandle)
261 {
262 list_elem_t *e = gLibraryList;
263 lib_entry_t *l = NULL;
264 effect_descriptor_t *d = NULL;
265 effect_handle_t itfe;
266 effect_entry_t *fx;
267 int found = 0;
268 int ret;
269
270 if (uuid == NULL || pHandle == NULL) {
271 return -EINVAL;
272 }
273
274 ALOGV("EffectCreate() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
275 uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion,
276 uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2],
277 uuid->node[3],uuid->node[4],uuid->node[5]);
278
279 ret = init();
280
281 if (ret < 0) {
282 ALOGW("EffectCreate() init error: %d", ret);
283 return ret;
284 }
285
286 pthread_mutex_lock(&gLibLock);
287
288 ret = findEffect(NULL, uuid, &l, &d);
289 if (ret < 0){
290 goto exit;
291 }
292
293 // create effect in library
294 ret = l->desc->create_effect(uuid, sessionId, ioId, &itfe);
295 if (ret != 0) {
296 ALOGW("EffectCreate() library %s: could not create fx %s, error %d", l->name, d->name, ret);
297 goto exit;
298 }
299
300 // add entry to effect list
301 fx = (effect_entry_t *)malloc(sizeof(effect_entry_t));
302 fx->subItfe = itfe;
303 if ((*itfe)->process_reverse != NULL) {
304 fx->itfe = (struct effect_interface_s *)&gInterfaceWithReverse;
305 ALOGV("EffectCreate() gInterfaceWithReverse");
306 } else {
307 fx->itfe = (struct effect_interface_s *)&gInterface;
308 ALOGV("EffectCreate() gInterface");
309 }
310 fx->lib = l;
311
312 e = (list_elem_t *)malloc(sizeof(list_elem_t));
313 e->object = fx;
314 e->next = gEffectList;
315 gEffectList = e;
316
317 *pHandle = (effect_handle_t)fx;
318
319 ALOGV("EffectCreate() created entry %p with sub itfe %p in library %s", *pHandle, itfe, l->name);
320
321 exit:
322 pthread_mutex_unlock(&gLibLock);
323 return ret;
324 }
325
EffectRelease(effect_handle_t handle)326 int EffectRelease(effect_handle_t handle)
327 {
328 effect_entry_t *fx;
329 list_elem_t *e1;
330 list_elem_t *e2;
331
332 int ret = init();
333 if (ret < 0) {
334 return ret;
335 }
336
337 // remove effect from effect list
338 pthread_mutex_lock(&gLibLock);
339 e1 = gEffectList;
340 e2 = NULL;
341 while (e1) {
342 if (e1->object == handle) {
343 if (e2) {
344 e2->next = e1->next;
345 } else {
346 gEffectList = e1->next;
347 }
348 fx = (effect_entry_t *)e1->object;
349 free(e1);
350 break;
351 }
352 e2 = e1;
353 e1 = e1->next;
354 }
355 if (e1 == NULL) {
356 ret = -ENOENT;
357 goto exit;
358 }
359
360 // release effect in library
361 if (fx->lib == NULL) {
362 ALOGW("EffectRelease() fx %p library already unloaded", handle);
363 } else {
364 pthread_mutex_lock(&fx->lib->lock);
365 fx->lib->desc->release_effect(fx->subItfe);
366 pthread_mutex_unlock(&fx->lib->lock);
367 }
368 free(fx);
369
370 exit:
371 pthread_mutex_unlock(&gLibLock);
372 return ret;
373 }
374
EffectIsNullUuid(const effect_uuid_t * uuid)375 int EffectIsNullUuid(const effect_uuid_t *uuid)
376 {
377 if (memcmp(uuid, EFFECT_UUID_NULL, sizeof(effect_uuid_t))) {
378 return 0;
379 }
380 return 1;
381 }
382
383 /////////////////////////////////////////////////
384 // Local functions
385 /////////////////////////////////////////////////
386
init()387 int init() {
388 int hdl;
389
390 if (gInitDone) {
391 return 0;
392 }
393
394 pthread_mutex_init(&gLibLock, NULL);
395
396 if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) {
397 loadEffectConfigFile(AUDIO_EFFECT_VENDOR_CONFIG_FILE);
398 } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) {
399 loadEffectConfigFile(AUDIO_EFFECT_DEFAULT_CONFIG_FILE);
400 }
401
402 updateNumEffects();
403 gInitDone = 1;
404 ALOGV("init() done");
405 return 0;
406 }
407
loadEffectConfigFile(const char * path)408 int loadEffectConfigFile(const char *path)
409 {
410 cnode *root;
411 char *data;
412
413 data = load_file(path, NULL);
414 if (data == NULL) {
415 return -ENODEV;
416 }
417 root = config_node("", "");
418 config_load(root, data);
419 loadLibraries(root);
420 loadEffects(root);
421 config_free(root);
422 free(root);
423 free(data);
424
425 return 0;
426 }
427
loadLibraries(cnode * root)428 int loadLibraries(cnode *root)
429 {
430 cnode *node;
431
432 node = config_find(root, LIBRARIES_TAG);
433 if (node == NULL) {
434 return -ENOENT;
435 }
436 node = node->first_child;
437 while (node) {
438 loadLibrary(node, node->name);
439 node = node->next;
440 }
441 return 0;
442 }
443
loadLibrary(cnode * root,const char * name)444 int loadLibrary(cnode *root, const char *name)
445 {
446 cnode *node;
447 void *hdl;
448 audio_effect_library_t *desc;
449 list_elem_t *e;
450 lib_entry_t *l;
451
452 node = config_find(root, PATH_TAG);
453 if (node == NULL) {
454 return -EINVAL;
455 }
456
457 hdl = dlopen(node->value, RTLD_NOW);
458 if (hdl == NULL) {
459 ALOGW("loadLibrary() failed to open %s", node->value);
460 goto error;
461 }
462
463 desc = (audio_effect_library_t *)dlsym(hdl, AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR);
464 if (desc == NULL) {
465 ALOGW("loadLibrary() could not find symbol %s", AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR);
466 goto error;
467 }
468
469 if (AUDIO_EFFECT_LIBRARY_TAG != desc->tag) {
470 ALOGW("getLibrary() bad tag %08x in lib info struct", desc->tag);
471 goto error;
472 }
473
474 if (EFFECT_API_VERSION_MAJOR(desc->version) !=
475 EFFECT_API_VERSION_MAJOR(EFFECT_LIBRARY_API_VERSION)) {
476 ALOGW("loadLibrary() bad lib version %08x", desc->version);
477 goto error;
478 }
479
480 // add entry for library in gLibraryList
481 l = malloc(sizeof(lib_entry_t));
482 l->name = strndup(name, PATH_MAX);
483 l->path = strndup(node->value, PATH_MAX);
484 l->handle = hdl;
485 l->desc = desc;
486 l->effects = NULL;
487 pthread_mutex_init(&l->lock, NULL);
488
489 e = malloc(sizeof(list_elem_t));
490 e->object = l;
491 pthread_mutex_lock(&gLibLock);
492 e->next = gLibraryList;
493 gLibraryList = e;
494 pthread_mutex_unlock(&gLibLock);
495 ALOGV("getLibrary() linked library %p for path %s", l, node->value);
496
497 return 0;
498
499 error:
500 if (hdl != NULL) {
501 dlclose(hdl);
502 }
503 return -EINVAL;
504 }
505
loadEffects(cnode * root)506 int loadEffects(cnode *root)
507 {
508 cnode *node;
509
510 node = config_find(root, EFFECTS_TAG);
511 if (node == NULL) {
512 return -ENOENT;
513 }
514 node = node->first_child;
515 while (node) {
516 loadEffect(node);
517 node = node->next;
518 }
519 return 0;
520 }
521
loadEffect(cnode * root)522 int loadEffect(cnode *root)
523 {
524 cnode *node;
525 effect_uuid_t uuid;
526 lib_entry_t *l;
527 effect_descriptor_t *d;
528 list_elem_t *e;
529
530 node = config_find(root, LIBRARY_TAG);
531 if (node == NULL) {
532 return -EINVAL;
533 }
534
535 l = getLibrary(node->value);
536 if (l == NULL) {
537 ALOGW("loadEffect() could not get library %s", node->value);
538 return -EINVAL;
539 }
540
541 node = config_find(root, UUID_TAG);
542 if (node == NULL) {
543 return -EINVAL;
544 }
545 if (stringToUuid(node->value, &uuid) != 0) {
546 ALOGW("loadEffect() invalid uuid %s", node->value);
547 return -EINVAL;
548 }
549
550 d = malloc(sizeof(effect_descriptor_t));
551 if (l->desc->get_descriptor(&uuid, d) != 0) {
552 char s[40];
553 uuidToString(&uuid, s, 40);
554 ALOGW("Error querying effect %s on lib %s", s, l->name);
555 free(d);
556 return -EINVAL;
557 }
558 #if (LOG_NDEBUG==0)
559 char s[256];
560 dumpEffectDescriptor(d, s, 256);
561 ALOGV("loadEffect() read descriptor %p:%s",d, s);
562 #endif
563 if (EFFECT_API_VERSION_MAJOR(d->apiVersion) !=
564 EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION)) {
565 ALOGW("Bad API version %08x on lib %s", d->apiVersion, l->name);
566 free(d);
567 return -EINVAL;
568 }
569 e = malloc(sizeof(list_elem_t));
570 e->object = d;
571 e->next = l->effects;
572 l->effects = e;
573
574 return 0;
575 }
576
getLibrary(const char * name)577 lib_entry_t *getLibrary(const char *name)
578 {
579 list_elem_t *e;
580
581 if (gCachedLibrary &&
582 !strncmp(gCachedLibrary->name, name, PATH_MAX)) {
583 return gCachedLibrary;
584 }
585
586 e = gLibraryList;
587 while (e) {
588 lib_entry_t *l = (lib_entry_t *)e->object;
589 if (!strcmp(l->name, name)) {
590 gCachedLibrary = l;
591 return l;
592 }
593 e = e->next;
594 }
595
596 return NULL;
597 }
598
599
resetEffectEnumeration()600 void resetEffectEnumeration()
601 {
602 gCurLib = gLibraryList;
603 gCurEffect = NULL;
604 if (gCurLib) {
605 gCurEffect = ((lib_entry_t *)gCurLib->object)->effects;
606 }
607 gCurEffectIdx = 0;
608 }
609
updateNumEffects()610 uint32_t updateNumEffects() {
611 list_elem_t *e;
612 uint32_t cnt = 0;
613
614 resetEffectEnumeration();
615
616 e = gLibraryList;
617 while (e) {
618 lib_entry_t *l = (lib_entry_t *)e->object;
619 list_elem_t *efx = l->effects;
620 while (efx) {
621 cnt++;
622 efx = efx->next;
623 }
624 e = e->next;
625 }
626 gNumEffects = cnt;
627 gCanQueryEffect = 0;
628 return cnt;
629 }
630
findEffect(const effect_uuid_t * type,const effect_uuid_t * uuid,lib_entry_t ** lib,effect_descriptor_t ** desc)631 int findEffect(const effect_uuid_t *type,
632 const effect_uuid_t *uuid,
633 lib_entry_t **lib,
634 effect_descriptor_t **desc)
635 {
636 list_elem_t *e = gLibraryList;
637 lib_entry_t *l = NULL;
638 effect_descriptor_t *d = NULL;
639 int found = 0;
640 int ret = 0;
641
642 while (e && !found) {
643 l = (lib_entry_t *)e->object;
644 list_elem_t *efx = l->effects;
645 while (efx) {
646 d = (effect_descriptor_t *)efx->object;
647 if (type != NULL && memcmp(&d->type, type, sizeof(effect_uuid_t)) == 0) {
648 found = 1;
649 break;
650 }
651 if (uuid != NULL && memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) {
652 found = 1;
653 break;
654 }
655 efx = efx->next;
656 }
657 e = e->next;
658 }
659 if (!found) {
660 ALOGV("findEffect() effect not found");
661 ret = -ENOENT;
662 } else {
663 ALOGV("findEffect() found effect: %s in lib %s", d->name, l->name);
664 *lib = l;
665 if (desc) {
666 *desc = d;
667 }
668 }
669
670 return ret;
671 }
672
dumpEffectDescriptor(effect_descriptor_t * desc,char * str,size_t len)673 void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len) {
674 char s[256];
675
676 snprintf(str, len, "\nEffect Descriptor %p:\n", desc);
677 strncat(str, "- TYPE: ", len);
678 uuidToString(&desc->uuid, s, 256);
679 snprintf(str, len, "- UUID: %s\n", s);
680 uuidToString(&desc->type, s, 256);
681 snprintf(str, len, "- TYPE: %s\n", s);
682 sprintf(s, "- apiVersion: %08X\n- flags: %08X\n",
683 desc->apiVersion, desc->flags);
684 strncat(str, s, len);
685 sprintf(s, "- name: %s\n", desc->name);
686 strncat(str, s, len);
687 sprintf(s, "- implementor: %s\n", desc->implementor);
688 strncat(str, s, len);
689 }
690
stringToUuid(const char * str,effect_uuid_t * uuid)691 int stringToUuid(const char *str, effect_uuid_t *uuid)
692 {
693 int tmp[10];
694
695 if (sscanf(str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
696 tmp, tmp+1, tmp+2, tmp+3, tmp+4, tmp+5, tmp+6, tmp+7, tmp+8, tmp+9) < 10) {
697 return -EINVAL;
698 }
699 uuid->timeLow = (uint32_t)tmp[0];
700 uuid->timeMid = (uint16_t)tmp[1];
701 uuid->timeHiAndVersion = (uint16_t)tmp[2];
702 uuid->clockSeq = (uint16_t)tmp[3];
703 uuid->node[0] = (uint8_t)tmp[4];
704 uuid->node[1] = (uint8_t)tmp[5];
705 uuid->node[2] = (uint8_t)tmp[6];
706 uuid->node[3] = (uint8_t)tmp[7];
707 uuid->node[4] = (uint8_t)tmp[8];
708 uuid->node[5] = (uint8_t)tmp[9];
709
710 return 0;
711 }
712
uuidToString(const effect_uuid_t * uuid,char * str,size_t maxLen)713 int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen)
714 {
715
716 snprintf(str, maxLen, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
717 uuid->timeLow,
718 uuid->timeMid,
719 uuid->timeHiAndVersion,
720 uuid->clockSeq,
721 uuid->node[0],
722 uuid->node[1],
723 uuid->node[2],
724 uuid->node[3],
725 uuid->node[4],
726 uuid->node[5]);
727
728 return 0;
729 }
730
731