1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "audio_mixer.h"
17
18#include <assert.h>
19#include <ctype.h>
20#include <dlfcn.h>
21#include <errno.h>
22#include <limits.h>
23#include <string.h>
24#include <strings.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <unistd.h>
28
29#include "hdf_base.h"
30#include "securec.h"
31
32#define AUDIO_UTIL_VERSION_STR "V1.0.0"
33
34#define AUDIO_DEV_FILE_PATH    "/dev/"
35#define CTL_SRV_NAME_PRE       "hdf_audio_control"
36#define CTL_SRV_DEFAULT        CTL_SRV_NAME_PRE
37#define MIXER_SRV_NAME_PRE     "hdf_audio_codec_"
38#define MIXER_SRV_NAME         MIXER_SRV_NAME_PRE "%s_dev%i"
39#define MIXER_SRV_NAME_DEFAULT MIXER_SRV_NAME_PRE "primary_dev0"
40#define SERVICE_NAME_LEN       64
41#define BUF_SIZE_T             256
42#define CHN_MONO               (1 << 0)
43#define CHN_STEREO             (1 << 1)
44#define OUTPUT_ALIGN           16
45#define BIT_VALULE_OFFSET      31
46#define STRTOL_BASE            10
47
48#define IFACE(v) [AUDIO_CTL_ELEM_IFACE_##v] = #v
49#define TYPE(v)  [AUDIO_CTL_ELEM_TYPE_##v] = #v
50
51static struct AudioMixer g_audioMixer;
52static struct AudioMixerContents g_mixerCts;
53
54static const char *g_capLibPath = HDF_LIBRARY_FULL_PATH("libaudio_capture_adapter");
55static const char *g_renLibPath = HDF_LIBRARY_FULL_PATH("libaudio_render_adapter");
56static void *g_soHandle = NULL;
57static AudioPcmType g_pcmT = PCM_RENDER;
58static bool g_debugFlag = false;
59
60static const char * const ctlEIfNames[] = {
61    IFACE(CARD),
62    IFACE(PCM),
63    IFACE(MIXER),
64};
65
66static const char * const ctlElemTypeNames[] = {
67    TYPE(NONE),
68    TYPE(BOOLEAN),
69    TYPE(INTEGER),
70    TYPE(ENUMERATED),
71    TYPE(BYTES),
72};
73
74void DebugLog(bool flag)
75{
76    g_debugFlag = flag;
77}
78
79static void *CallLibFunction(const char *funcName)
80{
81    void *func = NULL;
82    char *error = NULL;
83
84    if (g_soHandle == NULL) {
85        DEBUG_LOG("Invalid dynamic library handle!\n");
86        return NULL;
87    }
88
89    (void)dlerror(); /* Clear any existing error */
90    func = dlsym(g_soHandle, funcName);
91    error = dlerror();
92    if (error != NULL) {
93        DEBUG_LOG("%s\n", error);
94        printf("%s\n", error);
95        return NULL;
96    }
97
98    return func;
99}
100
101static int32_t AudioMixerList(const struct HdfIoService *service, struct AudioMixerContents *mixerCts)
102{
103    int32_t (*AmixerCtlElemList)(AudioPcmType, const struct HdfIoService *, struct AudioMixerContents *);
104
105    AmixerCtlElemList = CallLibFunction("AudioMixerCtlElem");
106    if (AmixerCtlElemList == NULL) {
107        return U_FAILURE;
108    }
109
110    return AmixerCtlElemList(g_pcmT, service, mixerCts);
111}
112
113static int32_t AudioMixerGet(const struct HdfIoService *service, struct AudioMixerCtlElemInfo *infoData)
114{
115    int32_t (*AmixerGetCtlElem)(AudioPcmType, const struct HdfIoService *, struct AudioMixerCtlElemInfo *);
116
117    AmixerGetCtlElem = CallLibFunction("AudioMixerCtlGetElem");
118    if (AmixerGetCtlElem == NULL) {
119        return U_FAILURE;
120    }
121
122    return AmixerGetCtlElem(g_pcmT, service, infoData);
123}
124
125static int32_t AudioMixerSet(const struct HdfIoService *service, struct AudioMixerCtlElemInfo *infoData)
126{
127    int32_t (*AmixerSetCtlElem)(AudioPcmType, const struct HdfIoService *, struct AudioMixerCtlElemInfo *);
128
129    AmixerSetCtlElem = CallLibFunction("AudioMixerCtlSetElem");
130    if (AmixerSetCtlElem == NULL) {
131        return U_FAILURE;
132    }
133
134    return AmixerSetCtlElem(g_pcmT, service, infoData);
135}
136
137static int32_t GetSoHandle(const char *filename)
138{
139    char buf[PATH_MAX] = {0};
140
141    if (realpath(filename, buf) == NULL) {
142        return U_FAILURE;
143    }
144
145    if (g_soHandle != NULL) {
146        DEBUG_LOG("It's been initialized!\n");
147        return U_SUCCESS;
148    }
149
150    g_soHandle = dlopen(buf, RTLD_LAZY);
151    if (g_soHandle == NULL) {
152        DEBUG_LOG("%s\n", dlerror());
153        printf("%s\n", dlerror());
154        return U_FAILURE;
155    }
156
157    return U_SUCCESS;
158}
159
160int32_t GetLibsoHandle(AudioPcmType pcm)
161{
162    int32_t ret;
163
164    g_pcmT = pcm;
165    switch (pcm) {
166        default:
167            DEBUG_LOG("Wrong PCM type!\n");
168            /* fall through */
169            __attribute__((fallthrough));
170        case PCM_RENDER:
171            ret = GetSoHandle(g_renLibPath);
172            if (ret != U_SUCCESS) {
173                DEBUG_LOG("Failed to open the render dynamic library!\n");
174            }
175            break;
176        case PCM_CAPTURE:
177            ret = GetSoHandle(g_capLibPath);
178            if (ret != U_SUCCESS) {
179                DEBUG_LOG("Failed to open the capture dynamic library!\n");
180            }
181            break;
182    }
183
184    return ret;
185}
186
187void CloseLibsoHandle(void)
188{
189    if (g_soHandle != NULL) {
190        (void)dlclose(g_soHandle);
191        g_soHandle = NULL;
192    }
193}
194
195void AudioMixerOpsInit(void)
196{
197    g_audioMixer.GetElemList = AudioMixerList;
198    g_audioMixer.GetElemProp = AudioMixerGet;
199    g_audioMixer.SetElemProp = AudioMixerSet;
200
201    g_mixerCts.data = NULL;
202    g_mixerCts.elemNum = 0;
203    (void)memset_s(g_mixerCts.cardServiceName, AUDIO_CARD_SRV_NAME_LEN, 0x0, AUDIO_CARD_SRV_NAME_LEN);
204}
205
206const char *ShowVersion(void)
207{
208    return AUDIO_UTIL_VERSION_STR;
209}
210
211static bool CheckMixerDevFile(const char *file)
212{
213    char buf[PATH_MAX] = {0};
214
215    if (realpath(file, buf) == NULL) {
216        DEBUG_LOG("%s\n", strerror(errno));
217        return false;
218    }
219
220    if (access(file, F_OK)) {
221        DEBUG_LOG("%s\n", strerror(errno));
222        return false;
223    }
224
225    return true;
226}
227
228struct HdfIoService *MixerBindCrlSrvDefault(void)
229{
230    int ret;
231    char buf[BUF_SIZE_T + 1] = {0};
232    struct HdfIoService *(*SrvBindDef)(const char *);
233
234    ret = snprintf_s(buf, BUF_SIZE_T, BUF_SIZE_T, "%s%s", AUDIO_DEV_FILE_PATH, MIXER_SRV_NAME_DEFAULT);
235    if (ret < 0) {
236        DEBUG_LOG("Failed to synthesize the service path!\n");
237        return NULL;
238    }
239
240    if (!CheckMixerDevFile(buf)) {
241        /* The sound card service file does not exist */
242        return NULL;
243    }
244
245    SrvBindDef = CallLibFunction("HdfIoServiceBindName");
246    if (SrvBindDef == NULL) {
247        return NULL;
248    }
249
250    return SrvBindDef(CTL_SRV_DEFAULT);
251}
252
253struct HdfIoService *MixerBindCrlSrv(const char *serviceName)
254{
255    int ret;
256    char path[BUF_SIZE_T + 1] = {0};
257    struct HdfIoService *(*SrvBind)(const char *);
258
259    if (serviceName == NULL) {
260        DEBUG_LOG("Invalid parameters!\n");
261        return NULL;
262    }
263
264    if (strncmp(serviceName, MIXER_SRV_NAME_PRE, strlen(MIXER_SRV_NAME_PRE))) {
265        DEBUG_LOG("The service name does not match!\n");
266        return NULL;
267    }
268
269    ret = snprintf_s(path, BUF_SIZE_T, BUF_SIZE_T, "%s%s", AUDIO_DEV_FILE_PATH, serviceName);
270    if (ret < 0) {
271        DEBUG_LOG("Failed to synthesize the service path!\n");
272        return NULL;
273    }
274    if (!CheckMixerDevFile(path)) {
275        /* The sound card service file does not exist */
276        return NULL;
277    }
278
279    SrvBind = CallLibFunction("HdfIoServiceBindName");
280    if (SrvBind == NULL) {
281        return NULL;
282    }
283
284    return SrvBind(CTL_SRV_DEFAULT);
285}
286
287void MixerRecycleCrlSrv(struct HdfIoService *srv)
288{
289    void (*SrvRecycle)(struct HdfIoService *);
290
291    if (srv != NULL) {
292        SrvRecycle = CallLibFunction("AudioCloseServiceSub");
293        if (SrvRecycle != NULL) {
294            SrvRecycle(srv);
295        }
296    }
297}
298
299static const char *CtlElemIfaceName(AudioCtlElemIfaceType iface)
300{
301    return ctlEIfNames[iface];
302}
303
304static const char *CtlElemName(struct AudioHwCtlElemIndex *id)
305{
306    return (const char *)id->eId.name;
307}
308
309static void PrintCtlElemInfo(struct AudioHwCtlElemId *id)
310{
311    printf("iface=%s, name='%s'\n", CtlElemIfaceName(id->iface), id->name);
312}
313
314static void PrintCtlElemInfoidx(struct MixerCtsElemIdx *eidx)
315{
316    printf("index=%u, numid=%u, ", eidx->index, eidx->index + 1);
317    PrintCtlElemInfo(eidx->id);
318}
319
320static void PrintCtlElemList(struct AudioMixerContents *contents)
321{
322    uint32_t num, idx;
323    struct MixerCtsElemIdx eidx;
324    struct AudioHwCtlElemId *id = NULL;
325
326    num = contents->elemNum;
327    id = (struct AudioHwCtlElemId *)contents->data;
328    for (idx = 0; idx < num; idx++) {
329        eidx.id = id++;
330        eidx.index = idx;
331        PrintCtlElemInfoidx(&eidx);
332    }
333}
334
335void ReleaseCtlElemList(void)
336{
337    if (g_mixerCts.data != NULL) {
338        free(g_mixerCts.data);
339        g_mixerCts.data = NULL;
340    }
341    g_mixerCts.elemNum = 0;
342    (void)memset_s(g_mixerCts.cardServiceName, AUDIO_CARD_SRV_NAME_LEN, 0x0, AUDIO_CARD_SRV_NAME_LEN);
343}
344
345static int32_t MctlListSub(const struct HdfIoService *service, const char *cardSrv)
346{
347    int32_t ret;
348
349    ret = strncpy_s(g_mixerCts.cardServiceName, AUDIO_CARD_SRV_NAME_LEN - 1, cardSrv, AUDIO_CARD_SRV_NAME_LEN - 1);
350    if (ret != 0) {
351        DEBUG_LOG("strncpy_s fail!\n");
352        return U_FAILURE;
353    }
354    g_mixerCts.cardServiceName[AUDIO_CARD_SRV_NAME_LEN - 1] = '\0';
355    if (g_audioMixer.GetElemList == NULL) {
356        DEBUG_LOG("The callback function is NULL!\n");
357        return U_FAILURE;
358    }
359
360    if (g_mixerCts.data != NULL && g_mixerCts.elemNum > 0) {
361        /* The list of control elements has been obtained */
362        return U_SUCCESS;
363    }
364
365    if (g_mixerCts.data != NULL) {
366        free(g_mixerCts.data);
367        g_mixerCts.data = NULL;
368    }
369    g_mixerCts.elemNum = 0;
370
371    ret = g_audioMixer.GetElemList(service, &g_mixerCts);
372    if (ret != U_SUCCESS) {
373        return ret;
374    }
375    if (g_mixerCts.data == NULL) {
376        g_mixerCts.elemNum = 0;
377        (void)memset_s(g_mixerCts.cardServiceName, AUDIO_CARD_SRV_NAME_LEN, 0x0, AUDIO_CARD_SRV_NAME_LEN);
378        DEBUG_LOG("Failed to obtain data!\n");
379        return U_FAILURE;
380    }
381    if (g_mixerCts.elemNum == 0) {
382        free(g_mixerCts.data);
383        g_mixerCts.data = NULL;
384        (void)memset_s(g_mixerCts.cardServiceName, AUDIO_CARD_SRV_NAME_LEN, 0x0, AUDIO_CARD_SRV_NAME_LEN);
385        DEBUG_LOG("Description Failed to obtain the number of data!\n");
386        return U_FAILURE;
387    }
388
389    return U_SUCCESS;
390}
391
392int32_t MctlList(const struct HdfIoService *service, const char *cardSrv)
393{
394    int32_t ret;
395
396    if (service == NULL || cardSrv == NULL) {
397        DEBUG_LOG("Invalid parameters!\n");
398        return U_INVALID_PARAM;
399    }
400
401    ret = MctlListSub(service, cardSrv);
402    if (ret != U_SUCCESS) {
403        return ret;
404    }
405    PrintCtlElemList(&g_mixerCts);
406
407    return U_SUCCESS;
408}
409
410int32_t MctlInfo(const struct HdfIoService *service, const char *cardSrv)
411{
412    int32_t ret;
413    uint32_t num;
414    struct AudioHwCtlElemId *id;
415
416    if (g_mixerCts.data == NULL || g_mixerCts.elemNum == 0) {
417        ReleaseCtlElemList();
418        ret = MctlListSub(service, cardSrv);
419        if (ret != U_SUCCESS) {
420            DEBUG_LOG("No reference data\n");
421            return ret;
422        }
423    }
424
425    /* The list of control elements has been obtained */
426    num = g_mixerCts.elemNum;
427    id = (struct AudioHwCtlElemId *)g_mixerCts.data;
428    while (num--) {
429        PrintCtlElemInfo(id++);
430    }
431    ReleaseCtlElemList();
432
433    return U_SUCCESS;
434}
435
436bool MixerFindSelem(const struct HdfIoService *srv, const char *cardSrv, const struct AudioHwCtlElemId *eId)
437{
438    int32_t ret;
439    uint32_t index;
440    struct AudioHwCtlElemId *findElem;
441
442    if (eId == NULL) {
443        DEBUG_LOG("Invalid parameters\n");
444        return false;
445    }
446
447    if (g_mixerCts.elemNum == 0 || g_mixerCts.data == NULL) {
448        ReleaseCtlElemList();
449        ret = MctlListSub(srv, cardSrv);
450        if (ret != U_SUCCESS) {
451            DEBUG_LOG("No reference data\n");
452            return false;
453        }
454    }
455
456    findElem = g_mixerCts.data;
457    for (index = 0; index < g_mixerCts.elemNum; index++) {
458        if (strcmp(eId->name, findElem[index].name) == 0) {
459            break;
460        }
461    }
462    if (index == g_mixerCts.elemNum) {
463        DEBUG_LOG("The corresponding control does not match!\n");
464        ReleaseCtlElemList();
465        return false;
466    }
467    ReleaseCtlElemList();
468
469    return true;
470}
471
472static AudioCtlElemIfaceType GetCtlElemIface(struct AudioHwCtlElemIndex *id)
473{
474    return id->eId.iface;
475}
476
477static unsigned int CtlElemGetIdx(struct AudioHwCtlElemIndex *id)
478{
479    return id->index;
480}
481
482static unsigned int CtlElemInfoCnt(struct AudioMixerCtlElemInfo *info)
483{
484    return info->count;
485}
486
487static AudioCtlElemType CtlElemGetInfoType(struct AudioMixerCtlElemInfo *info)
488{
489    return info->type;
490}
491
492static const char *CtlElemTypeName(AudioCtlElemType type)
493{
494    return ctlElemTypeNames[type];
495}
496
497static const char *MixerCtlType(struct AudioMixerCtlElemInfo *info)
498{
499    return CtlElemTypeName(CtlElemGetInfoType(info));
500}
501
502static int32_t CtlElemInfoGetMin(struct AudioMixerCtlElemInfo *info)
503{
504    return info->value.intVal.min;
505}
506
507static int32_t CtlElemInfoGetMax(struct AudioMixerCtlElemInfo *info)
508{
509    return info->value.intVal.max;
510}
511
512static int32_t CtlElemInfoGetStep(struct AudioMixerCtlElemInfo *info)
513{
514    return info->value.intVal.step;
515}
516
517static long *CtlElemInfoGetVals(struct AudioMixerCtlElemInfo *info)
518{
519    return info->value.intVal.vals;
520}
521
522static void CtlElemInfoSetItem(struct AudioMixerCtlElemInfo *info, uint32_t val)
523{
524    info->value.enumVal.item = val;
525}
526
527static uint32_t CtlElemInfoGetItems(struct AudioMixerCtlElemInfo *info)
528{
529    return info->value.enumVal.items;
530}
531
532static uint32_t CtlElemInfoGetItem(struct AudioMixerCtlElemInfo *info)
533{
534    return info->value.enumVal.item;
535}
536
537static char *CtlElemInfoGetItemName(struct AudioMixerCtlElemInfo *info)
538{
539    return info->value.enumVal.name;
540}
541
542static int32_t CtlElemValueGetBoolean(struct AudioMixerCtlElemInfo *info, uint32_t idx)
543{
544    if (idx > BIT_VALULE_OFFSET - 1) {
545        return 0;
546    }
547
548    return ((info->value.intVal.max >> idx) & 0x1);
549}
550
551static int32_t CtlElemValueGetInt(struct AudioMixerCtlElemInfo *info, uint32_t idx)
552{
553    return CtlElemValueGetBoolean(info, idx);
554}
555
556static char CtlElemValueGetByte(struct AudioMixerCtlElemInfo *info, uint32_t idx)
557{
558    if (idx >= RESERVED_BUF_LEN) {
559        return '?';
560    }
561
562    return info->value.reserved[idx];
563}
564
565static unsigned char *CtlElemValueGetBytes(struct AudioMixerCtlElemInfo *info)
566{
567    return info->value.reserved;
568}
569
570static uint32_t CtlElemValueGetEnum(struct AudioMixerCtlElemInfo *info, uint32_t idx)
571{
572    if (idx > BIT_VALULE_OFFSET) {
573        return 0;
574    }
575
576    return ((info->value.enumVal.item >> idx) & 0x1);
577}
578
579static void PrintOtherVal(struct AudioMixerCtlElemInfo *info)
580{
581    uint32_t idx;
582    unsigned int count;
583    AudioCtlElemType type;
584
585    type = CtlElemGetInfoType(info);
586    count = CtlElemInfoCnt(info);
587    for (idx = 0; idx < count; idx++) {
588        if (idx > 0) {
589            printf(", ");
590        }
591
592        switch (type) {
593            case AUDIO_CTL_ELEM_TYPE_BOOLEAN:
594                printf("%s", CtlElemValueGetBoolean(info, idx) ? "on" : "off");
595                break;
596            case AUDIO_CTL_ELEM_TYPE_BYTES:
597                printf("0x%02x", CtlElemValueGetByte(info, idx));
598                break;
599            case AUDIO_CTL_ELEM_TYPE_INTEGER:
600                printf("%i", CtlElemValueGetInt(info, idx));
601                break;
602            case AUDIO_CTL_ELEM_TYPE_ENUMERATED:
603                printf("%u", CtlElemValueGetEnum(info, idx));
604                break;
605            default:
606                printf("?");
607                break;
608        }
609    }
610    printf("\n");
611}
612
613static void PrintValue(struct AudioMixerCtlElemInfo *info)
614{
615    long *ptr;
616    uint32_t idx, items;
617    AudioCtlElemType type;
618
619    type = CtlElemGetInfoType(info);
620    switch (type) {
621        case AUDIO_CTL_ELEM_TYPE_INTEGER:
622            printf(", min=%i, max=%i, step=%i\n",
623                CtlElemInfoGetMin(info),
624                CtlElemInfoGetMax(info),
625                CtlElemInfoGetStep(info));
626            ptr = CtlElemInfoGetVals(info);
627            printf("  : values=");
628            for (idx = 0; idx < info->count; idx++) {
629                if (idx > 0) {
630                    printf(", ");
631                }
632                printf("%i", (int)ptr[idx]);
633            }
634            printf("\n");
635            break;
636        case AUDIO_CTL_ELEM_TYPE_ENUMERATED:
637            {
638                items = CtlElemInfoGetItems(info);
639                printf(", items=%u\n", items);
640                for (idx = 0; idx < items; idx++) {
641                    CtlElemInfoSetItem(info, idx);
642                    printf("%s; Item #%u '%s'\n", "  ", idx, CtlElemInfoGetItemName(info));
643                }
644                printf("  : values=%u\n", CtlElemInfoGetItem(info));
645            }
646            break;
647        case AUDIO_CTL_ELEM_TYPE_BOOLEAN:
648            printf("\n  : values=");
649            PrintOtherVal(info);
650            break;
651        case AUDIO_CTL_ELEM_TYPE_BYTES:
652            printf("  : values=%s\n", CtlElemValueGetBytes(info));
653            break;
654        default:
655            DEBUG_LOG("Mismatched control value type!\n");
656            break;
657    }
658}
659
660static void ShowIntVal(struct AudioMixerCtlElemInfo *info)
661{
662    int32_t ret;
663    uint32_t index;
664    unsigned int count;
665    const char *iface;
666    const char *space = "  ";
667    char buf[BUF_SIZE_T + 1] = {0};
668
669    index = CtlElemGetIdx(&info->eIndexId);
670    iface = CtlElemIfaceName(GetCtlElemIface(&info->eIndexId));
671    if (index > 0) {
672        ret = snprintf_s(buf, BUF_SIZE_T, BUF_SIZE_T, "index=%u, iface=%s, name=%s",
673            index, iface, CtlElemName(&info->eIndexId));
674    } else {
675        ret = snprintf_s(buf, BUF_SIZE_T, BUF_SIZE_T, "iface=%s, name=%s",
676            iface, CtlElemName(&info->eIndexId));
677    }
678    if (ret < 0) {
679        DEBUG_LOG("Failed to snprintf_s!\n");
680        return;
681    }
682
683    buf[BUF_SIZE_T] = '\0';
684    printf("%s\n", buf);
685
686    count = CtlElemInfoCnt(info);
687    printf("%s; type=%s, values=%u", space, MixerCtlType(info), count);
688    PrintValue(info);
689}
690
691static void ShowEnumVal(struct AudioMixerCtlElemInfo *info)
692{
693    printf("  : values=%u\n", CtlElemInfoGetItem(info));
694}
695
696static void ShowBytesVal(const unsigned char *data)
697{
698    int i;
699    const unsigned char *p = data;
700
701    printf("\ndata:\t");
702    for (i = 0; i < RESERVED_BUF_LEN; i++) {
703        if (*p == '\0') {
704            break;
705        }
706
707        if ((i % OUTPUT_ALIGN) == 0) {
708            printf("\n");
709        }
710        printf("0x%02x \n", *p++);
711    }
712    printf("\n");
713    printf("string: %s\n", data);
714}
715
716static void ShowElemInfo(struct AudioMixerCtlElemInfo *info)
717{
718    switch (info->type) {
719        case AUDIO_CTL_ELEM_TYPE_INTEGER:
720        case AUDIO_CTL_ELEM_TYPE_BOOLEAN:
721            ShowIntVal(info);
722            break;
723        case AUDIO_CTL_ELEM_TYPE_ENUMERATED:
724            ShowEnumVal(info);
725            break;
726        case AUDIO_CTL_ELEM_TYPE_BYTES:
727            ShowBytesVal(info->value.reserved);
728            break;
729        default:
730            DEBUG_LOG("Mismatched control value type!\n");
731            break;
732    }
733}
734
735int32_t MctlGetElem(const struct HdfIoService *service, struct MixerCardCtlInfo *ctlInfo)
736{
737    int32_t ret;
738    struct AudioMixerCtlElemInfo info;
739
740    if (service == NULL || ctlInfo == NULL) {
741        DEBUG_LOG("Invalid parameters!\n");
742        return U_INVALID_PARAM;
743    }
744
745    ret = strncpy_s(info.cardSrvName, AUDIO_CARD_SRV_NAME_LEN - 1, ctlInfo->cardSrvName, AUDIO_CARD_SRV_NAME_LEN - 1);
746    if (ret != 0) {
747        DEBUG_LOG("strncpy_s cardSrvName fail!\n");
748        return U_FAILURE;
749    }
750    info.cardSrvName[AUDIO_CARD_SRV_NAME_LEN - 1] = '\0';
751    ret = strncpy_s(info.eIndexId.eId.name, AUDIO_ELEM_NAME_LEN - 1, ctlInfo->edx.eId.name, AUDIO_ELEM_NAME_LEN - 1);
752    if (ret != 0) {
753        DEBUG_LOG("strncpy_s element name fail!\n");
754        return U_FAILURE;
755    }
756    info.eIndexId.eId.name[AUDIO_ELEM_NAME_LEN - 1] = '\0';
757    info.eIndexId.eId.iface = ctlInfo->edx.eId.iface;
758    // need info.type
759    info.type = AUDIO_CTL_ELEM_TYPE_INTEGER;
760
761    if (g_audioMixer.GetElemProp == NULL) {
762        DEBUG_LOG("The callback function is NULL!\n");
763        return U_FAILURE;
764    }
765    ret = g_audioMixer.GetElemProp(service, &info);
766    if (ret != U_SUCCESS) {
767        DEBUG_LOG("Failed to get control!\n");
768        return U_FAILURE;
769    }
770    ShowElemInfo(&info);
771
772    return U_SUCCESS;
773}
774
775static const struct ChannelMask g_chnMask[] = {
776    {"frontleft",   1 << AMIXER_CHN_FRONT_LEFT                                    },
777    {"frontright",  1 << AMIXER_CHN_FRONT_RIGHT                                   },
778    {"frontcenter", 1 << AMIXER_CHN_FRONT_CENTER                                  },
779    {"front",       ((1 << AMIXER_CHN_FRONT_LEFT) | (1 << AMIXER_CHN_FRONT_RIGHT))},
780    {"center",      1 << AMIXER_CHN_FRONT_CENTER                                  },
781    {"rearleft",    1 << AMIXER_CHN_REAR_LEFT                                     },
782    {"rearright",   1 << AMIXER_CHN_REAR_RIGHT                                    },
783    {"rear",        ((1 << AMIXER_CHN_REAR_LEFT) | (1 << AMIXER_CHN_REAR_RIGHT))  },
784    {"woofer",      1 << AMIXER_CHN_WOOFER                                        },
785    {NULL,          0                                                             }
786};
787
788static uint32_t ChannelsMask(char **ptr, unsigned int chns)
789{
790    const struct ChannelMask *msk;
791
792    for (msk = g_chnMask; msk->name != NULL; msk++) {
793        if (strncasecmp(*ptr, msk->name, strlen(msk->name)) == 0) {
794            /* Stop loop at specified character. */
795            while (**ptr != '\0' && **ptr != ',' && **ptr != ' ' && **ptr != '\t') {
796                (*ptr)++;
797            }
798            /* Skip the specified character. */
799            if (**ptr == ',' || **ptr == ' ' || **ptr == '\t') {
800                (*ptr)++;
801            }
802
803            return msk->mask;
804        }
805    }
806
807    return chns;
808}
809
810static uint32_t DirMask(char **ptr, unsigned int dir)
811{
812    int find = 0;
813
814    /* Stop loop at specified character. */
815    while (**ptr != '\0' && **ptr != ',' && **ptr != ' ' && **ptr != '\t') {
816        (*ptr)++;
817    }
818
819    /* Skip the specified character. */
820    if (**ptr == ',' || **ptr == ' ' || **ptr == '\t') {
821        (*ptr)++;
822    }
823
824    if (strncasecmp(*ptr, "render", strlen("render")) == 0) {
825        dir = find = PCM_RENDER + 1;
826    } else if (strncasecmp(*ptr, "capture", strlen("capture")) == 0) {
827        dir = find = PCM_CAPTURE + 1;
828    }
829
830    if (find) {
831        /* Stop loop at specified character. */
832        while (**ptr != '\0' && **ptr != ',' && **ptr != ' ' && **ptr != '\t') {
833            (*ptr)++;
834        }
835
836        /* Skip the specified character. */
837        if (**ptr == ',' || **ptr == ' ' || **ptr == '\t') {
838            (*ptr)++;
839        }
840    }
841
842    return dir;
843}
844
845static bool IsRenderChannel(AudioMixerChannelIdType chn)
846{
847    return !!(chn);
848}
849
850static bool IsCaptureChannel(AudioMixerChannelIdType chn)
851{
852    return !(chn);
853}
854
855static int32_t FillChnmap(struct AudioMixerCtlElemInfo *info, uint32_t chns, uint32_t dir, char **ptr)
856{
857    char *sp;
858    AudioMixerChannelIdType chn;
859
860    /* Matches the specified channel. */
861    for (chn = AMIXER_CHN_FRONT_LEFT; chn < AMIXER_CHN_LAST; chn++) {
862        sp = NULL;
863        if (!(chns & (1 << (uint32_t)chn))) {
864            continue;
865        }
866
867        if (!((dir & PCM_RENDER) && IsRenderChannel(chn)) && !((dir & PCM_CAPTURE) && IsCaptureChannel(chn))) {
868            DEBUG_LOG("Unable to set channel!\n");
869            return U_FAILURE;
870        }
871
872        info->value.enumVal.item |= (chns & (1 << chn));
873        /* Search for the next channel. */
874        while (**ptr != '\0' && **ptr != ',') {
875            (*ptr)++;
876        }
877        if (**ptr == '\0') {
878            break;
879        }
880        (*ptr)++; // skip ','
881        DEBUG_LOG("skip, = %s\n", *ptr);
882    }
883
884    if (info->value.enumVal.item > CHN_STEREO) {
885        info->value.enumVal.item = CHN_STEREO;
886    } else {
887        info->value.enumVal.item = CHN_MONO;
888    }
889    DEBUG_LOG("chns = %i\n", info->value.enumVal.item);
890
891    return U_SUCCESS;
892}
893
894static int32_t FillChnlsIntVal(struct AudioMixerCtlElemInfo *info, unsigned int argc, char *argv)
895{
896    bool mchns;
897    int32_t ret;
898    char *ptr = NULL;
899    uint32_t dir = 3;
900    uint32_t chns = ~0U;
901
902    ptr = argv;
903    chns = ChannelsMask(&ptr, chns);
904    if (*ptr == '\0') {
905        DEBUG_LOG("Channels Mask = %u\n", chns);
906        return U_FAILURE;
907    }
908
909    dir = DirMask(&ptr, dir);
910    if (*ptr == '\0') {
911        DEBUG_LOG("Direct Mask = %u\n", chns);
912        return U_FAILURE;
913    }
914
915    mchns = (strchr(ptr, ',') != NULL);
916    if (!mchns) {
917        info->value.enumVal.item = CHN_MONO;
918        return U_SUCCESS;
919    }
920
921    ret = FillChnmap(info, chns, dir, &ptr);
922    if (ret != U_SUCCESS) {
923        return ret;
924    }
925
926    return U_SUCCESS;
927}
928
929int32_t SetChannels(const struct HdfIoService *srv, const char *cardSrv, unsigned int argc, char *argv)
930{
931    int32_t ret;
932    struct AudioMixerCtlElemInfo infoData;
933
934    if (srv == NULL || cardSrv == NULL || argc == 0) {
935        DEBUG_LOG("Invalid parameters!\n");
936        return U_INVALID_PARAM;
937    }
938
939    DEBUG_LOG("argc = %u, argv = %s\n", argc, argv);
940    (void)memset_s(infoData.cardSrvName, AUDIO_CARD_SRV_NAME_LEN, 0, AUDIO_CARD_SRV_NAME_LEN);
941    ret = strncpy_s(infoData.cardSrvName, AUDIO_CARD_SRV_NAME_LEN, cardSrv, strlen(cardSrv) + 1);
942    if (ret != 0) {
943        DEBUG_LOG("strncpy_s card service name is faild!\n");
944        return U_FAILURE;
945    }
946    infoData.cardSrvName[AUDIO_CARD_SRV_NAME_LEN - 1] = '\0';
947
948    (void)memset_s(infoData.eIndexId.eId.name, AUDIO_ELEM_NAME_LEN, 0, AUDIO_ELEM_NAME_LEN);
949    ret = strncpy_s(
950        infoData.eIndexId.eId.name, AUDIO_ELEM_NAME_LEN, "Captrue Channel Mode", strlen("Captrue Channel Mode") + 1);
951    if (ret != 0) {
952        DEBUG_LOG("strncpy_s element name is failed!\n");
953        return U_FAILURE;
954    }
955    infoData.eIndexId.eId.iface = AUDIO_CTL_ELEM_IFACE_MIXER;
956    infoData.type = AUDIO_CTL_ELEM_TYPE_INTEGER;
957    ret = FillChnlsIntVal(&infoData, argc, argv);
958    if (ret != U_SUCCESS) {
959        return ret;
960    }
961
962    if (g_audioMixer.SetElemProp == NULL) {
963        DEBUG_LOG("The callback function is NULL!\n");
964        return U_FAILURE;
965    }
966    ret = g_audioMixer.SetElemProp(srv, &infoData);
967    if (ret != U_SUCCESS) {
968        return ret;
969    }
970
971    ret = strncpy_s(
972        infoData.eIndexId.eId.name, AUDIO_ELEM_NAME_LEN, "Render Channel Mode", strlen("Render Channel Mode") + 1);
973    if (ret != 0) {
974        DEBUG_LOG("strncpy_s element name is failed!\n");
975        return U_FAILURE;
976    }
977
978    return g_audioMixer.SetElemProp(srv, &infoData);
979}
980
981static int32_t FillIntVal(struct AudioMixerCtlElemInfo *info, unsigned int argc, char *argv[])
982{
983    char *vals, *minPtr, *maxPtr, *stepPtr;
984    char *ptr = NULL;
985    char *outPtr = NULL;
986
987    if (argc != 2) {    // 2 for numbers of argc
988        DEBUG_LOG("Unable to set too much value!\n");
989        return U_FAILURE;
990    }
991
992    ptr = argv[argc - 1];
993    DEBUG_LOG("argv[%u] = %s\n", argc - 1, ptr);
994    vals = strtok_r(ptr, ",", &outPtr);
995    if (outPtr == NULL) {
996        info->value.intVal.vals[0] = strtol(ptr, NULL, STRTOL_BASE);
997        info->value.intVal.min = 0;
998        info->value.intVal.step = 0;
999
1000        return U_SUCCESS;
1001    }
1002
1003    info->value.intVal.vals[0] = strtol(vals, NULL, STRTOL_BASE);
1004    maxPtr = strtok_r(NULL, ",", &outPtr);
1005    if (outPtr == NULL) {
1006        info->value.intVal.max = (int32_t)strtol(maxPtr, NULL, STRTOL_BASE);
1007        info->value.intVal.min = 0;
1008        info->value.intVal.step = 0;
1009
1010        return U_SUCCESS;
1011    }
1012
1013    info->value.intVal.max = (int32_t)strtol(maxPtr, NULL, STRTOL_BASE);
1014    minPtr = strtok_r(NULL, ",", &outPtr);
1015    if (outPtr != NULL) {
1016        info->value.intVal.min = (int32_t)strtol(minPtr, NULL, STRTOL_BASE);
1017        stepPtr = strtok_r(NULL, ",", &outPtr);
1018        info->value.intVal.step = outPtr != NULL ? (int32_t)strtol(stepPtr, NULL, STRTOL_BASE) : 0;
1019    } else {
1020        info->value.intVal.min = (int32_t)strtol(minPtr, NULL, STRTOL_BASE);
1021        info->value.intVal.step = 0;
1022    }
1023
1024    return U_SUCCESS;
1025}
1026
1027static int32_t FillEnumVal(struct AudioMixerCtlElemInfo *info, unsigned int argc, char *argv[])
1028{
1029    int32_t ret;
1030    unsigned int idx;
1031    char *ptr = NULL;
1032
1033    printf("\n");
1034    /* Multiple enumerated values are output line by line. */
1035    for (idx = 1; idx < argc; idx++) {
1036        ptr = argv[idx];
1037        // Control Settings with enumerated values.
1038        ret = strcpy_s(info->value.enumVal.name, AUDIO_ELEM_NAME_LEN - 1, ptr);
1039        if (ret != EOK) {
1040            printf("strcpy_s failed: argv = %s\n", ptr);
1041        } else {
1042            printf("%s\n", ptr);
1043        }
1044    }
1045
1046    return U_SUCCESS;
1047}
1048
1049static int32_t FillBytesVal(unsigned char *buf, unsigned int argc, char *argv[])
1050{
1051    int32_t ret;
1052    size_t len;
1053    unsigned int idx;
1054    unsigned char *ptr = buf;
1055    char *sptr = NULL;
1056    size_t size = RESERVED_BUF_LEN;
1057
1058    /* Multiple input parameters are separated and combined with a ",". */
1059    for (idx = 1; idx < argc; idx++) {
1060        sptr = argv[idx];
1061        len = strlen(argv[idx]);
1062        if (size <= len) {
1063            DEBUG_LOG("The callback function is NULL!\n");
1064            break;
1065        }
1066        ret = strncpy_s((char *)ptr, RESERVED_BUF_LEN - 1, sptr, len);
1067        if (ret != 0) {
1068            DEBUG_LOG("strncpy_s faild!\n");
1069            return U_FAILURE;
1070        }
1071        ptr += len;
1072        *ptr++ = ',';
1073        size -= len + 1;
1074    }
1075    if (idx < argc) {
1076        DEBUG_LOG("Unable to set too much data!\n");
1077        return U_FAILURE;
1078    }
1079    printf("data buf = %s\n", buf);
1080
1081    return U_SUCCESS;
1082}
1083
1084
1085int32_t MctlSetElem(const struct HdfIoService *srv,
1086                    struct MixerCardCtlInfo *ctlInfo,
1087                    unsigned int argc, char *argv[])
1088{
1089    int32_t ret;
1090    struct AudioMixerCtlElemInfo infoData;
1091
1092    if (srv == NULL || ctlInfo == NULL || argc == 0) {
1093        DEBUG_LOG("Invalid parameters!\n");
1094        return U_INVALID_PARAM;
1095    }
1096
1097    ret = strncpy_s(infoData.cardSrvName, AUDIO_CARD_SRV_NAME_LEN, ctlInfo->cardSrvName, AUDIO_CARD_SRV_NAME_LEN);
1098    if (ret != 0) {
1099        DEBUG_LOG("strncpy_s card service name is faild!\n");
1100        return U_FAILURE;
1101    }
1102    infoData.cardSrvName[AUDIO_CARD_SRV_NAME_LEN - 1] = '\0';
1103
1104    ret = strncpy_s(infoData.eIndexId.eId.name, AUDIO_ELEM_NAME_LEN, ctlInfo->edx.eId.name, AUDIO_ELEM_NAME_LEN);
1105    if (ret != 0) {
1106        DEBUG_LOG("strncpy_s element name is failed!\n");
1107        return U_FAILURE;
1108    }
1109    infoData.eIndexId.eId.name[AUDIO_ELEM_NAME_LEN - 1] = '\0';
1110    infoData.eIndexId.eId.iface = AUDIO_CTL_ELEM_IFACE_MIXER;
1111
1112    // First, read the value type.(infoData.type)
1113    infoData.type = AUDIO_CTL_ELEM_TYPE_INTEGER;
1114    switch (infoData.type) {
1115        case AUDIO_CTL_ELEM_TYPE_INTEGER:
1116        case AUDIO_CTL_ELEM_TYPE_BOOLEAN:
1117            ret = FillIntVal(&infoData, argc, argv);
1118            break;
1119        case AUDIO_CTL_ELEM_TYPE_ENUMERATED:
1120            ret = FillEnumVal(&infoData, argc, argv);
1121            break;
1122        case AUDIO_CTL_ELEM_TYPE_BYTES:
1123            ret = FillBytesVal(infoData.value.reserved, argc, argv);
1124            break;
1125        default:
1126            ret = U_FAILURE;
1127            break;
1128    }
1129    if (ret != U_SUCCESS) {
1130        DEBUG_LOG("The value type does not match!\n");
1131        return U_FAILURE;
1132    }
1133
1134    if (g_audioMixer.SetElemProp == NULL) {
1135        DEBUG_LOG("The callback function is NULL!\n");
1136        return U_FAILURE;
1137    }
1138
1139    return g_audioMixer.SetElemProp(srv, &infoData);
1140}
1141
1142static char *SkipSpecifyStr(const char *src, const char *needle)
1143{
1144    char *p = NULL;
1145
1146    p = strstr(src, needle);
1147    if (p != NULL) {
1148        p += strlen(needle);
1149    }
1150
1151    return p;
1152}
1153
1154static int32_t GetSndCardType(const char *name, char *buf, uint32_t len)
1155{
1156    int32_t ret;
1157    char *ptr = NULL;
1158    char *out = NULL;
1159
1160    if (name == NULL || buf == NULL || len == 0) {
1161        DEBUG_LOG("Invalid parameters!\n");
1162        return U_INVALID_PARAM;
1163    }
1164
1165    ptr = SkipSpecifyStr(name, MIXER_SRV_NAME_PRE);
1166    if (ptr == NULL) {
1167        DEBUG_LOG("No found card type!\n");
1168        return U_FAILURE;
1169    }
1170
1171    ret = memcpy_s(buf, len, ptr, strlen(ptr) + 1);
1172    if (ret != EOK) {
1173        DEBUG_LOG("memcpy_s fail!\n");
1174        return U_FAILURE;
1175    }
1176    ptr = strtok_r(buf, "_", &out);
1177    ret = memcpy_s(buf, len, ptr, strlen(ptr) + 1);
1178    if (ret != EOK) {
1179        DEBUG_LOG("memcpy_s fail!\n");
1180        return U_FAILURE;
1181    }
1182
1183    return U_SUCCESS;
1184}
1185
1186static void ShowAllAdapters(struct SndCardsList *sndList)
1187{
1188    int32_t i, ret;
1189    uint32_t cnums;
1190    char ctype[AUDIO_BASE_LEN] = {0};
1191    struct AudioCardId *clist = NULL;
1192
1193    if (sndList->cardNums == 0 || sndList->cardsList == NULL) {
1194        DEBUG_LOG("No sound cards found...!\n");
1195        goto end;
1196    }
1197
1198    cnums = sndList->cardNums;
1199    clist = sndList->cardsList;
1200    printf("****** List of Audio Hardware Devices ******\n");
1201    /* To keep the primary sound card always in front of the total,
1202     * output it in the following order.
1203     */
1204    for (i = (int32_t)cnums - 1; i >= 0; i--) {
1205        (void)memset_s(ctype, AUDIO_BASE_LEN, 0, AUDIO_BASE_LEN);
1206        ret = GetSndCardType(clist[i].cardName, ctype, AUDIO_BASE_LEN);
1207        if (ret != U_SUCCESS) {
1208            goto end;
1209        }
1210        printf("card %i: %s [%s], device 0\n", clist[i].index, ctype, clist[i].cardName);
1211        printf("  Subdevices: 1/1\n");
1212        printf("  Subdevice #0: subdevice #0\n");
1213    }
1214
1215end:
1216    if (sndList->cardsList != NULL) {
1217        free(sndList->cardsList);
1218        sndList->cardsList = NULL;
1219    }
1220}
1221
1222int32_t GetAllCards(const struct HdfIoService *service)
1223{
1224    int32_t ret;
1225    struct SndCardsList sndcards;
1226    int32_t (*GetAllAdapters)(const struct HdfIoService *, struct SndCardsList *);
1227
1228    if (service == NULL) {
1229        DEBUG_LOG("Invalid parameter!\n");
1230        return U_INVALID_PARAM;
1231    }
1232
1233    GetAllAdapters = CallLibFunction("AudioMixerGetAllAdapters");
1234    if (GetAllAdapters == NULL) {
1235        return U_FAILURE;
1236    }
1237
1238    (void)memset_s(&sndcards, sizeof(struct SndCardsList), 0, sizeof(struct SndCardsList));
1239    ret = GetAllAdapters(service, &sndcards);
1240    if (ret != U_SUCCESS) {
1241        DEBUG_LOG("Description Failed to obtain the sound card list!\n");
1242        return U_FAILURE;
1243    }
1244    ShowAllAdapters(&sndcards);
1245
1246    return U_SUCCESS;
1247}
1248
1249static char *FindSpecificCardName(int card, struct SndCardsList *cardList)
1250{
1251    int32_t i;
1252    uint32_t cnums;
1253    struct AudioCardId *clist = NULL;
1254
1255    if (cardList->cardNums == 0 || cardList->cardsList == NULL) {
1256        DEBUG_LOG("No sound cards found...!\n");
1257        goto end;
1258    }
1259
1260    cnums = cardList->cardNums;
1261    clist = cardList->cardsList;
1262    for (i = 0; i < (int32_t)cnums; i++) {
1263        if (card == clist[i].index) {
1264            DEBUG_LOG("I found this sound card. [card%i: %s]\n", card, clist[i].cardName);
1265            return clist[i].cardName;
1266        }
1267    }
1268
1269end:
1270    if (cardList->cardsList != NULL) {
1271        free(cardList->cardsList);
1272        cardList->cardsList = NULL;
1273    }
1274    cardList->cardNums = 0;
1275
1276    return NULL;
1277}
1278
1279void UpdateCardSname(int card, const struct HdfIoService *srv, char * const sname, size_t snameLen)
1280{
1281    int32_t ret;
1282    struct SndCardsList cardList;
1283    int32_t (*GetAllCardsFunc)(const struct HdfIoService *, struct SndCardsList *);
1284    char *cname = NULL;
1285
1286    if (card < 0 || srv == NULL || sname == NULL || snameLen == 0) {
1287        DEBUG_LOG("Invalid parameter!\n");
1288        return;
1289    }
1290
1291    GetAllCardsFunc = CallLibFunction("AudioMixerGetAllAdapters");
1292    if (GetAllCardsFunc == NULL) {
1293        DEBUG_LOG("Description Failed to obtain the current sound card list of the system!\n");
1294        return;
1295    }
1296
1297    (void)memset_s(&cardList, sizeof(struct SndCardsList), 0, sizeof(struct SndCardsList));
1298    ret = GetAllCardsFunc(srv, &cardList);
1299    if (ret != U_SUCCESS) {
1300        DEBUG_LOG("Update failed: Description Failed to obtain the sound card list!\n");
1301        return;
1302    }
1303
1304    cname = FindSpecificCardName(card, &cardList);
1305    if (cname == NULL) {
1306        DEBUG_LOG("Update failed: The corresponding sound card cannot be matched!\n");
1307        return;
1308    }
1309
1310    ret = memcpy_s(sname, snameLen, cname, strlen(cname) + 1);
1311    if (ret != EOK) {
1312        DEBUG_LOG("Update failed: memcpy_s fail!\n");
1313    }
1314    sname[snameLen - 1] = '\0';
1315    if (g_debugFlag) {
1316        printf("|--> [%s]\n", sname);
1317    }
1318
1319    if (cardList.cardsList != NULL) {
1320        free(cardList.cardsList);
1321        cardList.cardsList = NULL;
1322    }
1323    cardList.cardNums = 0;
1324}
1325