summaryrefslogtreecommitdiffstats
path: root/HAL-afb/HAL-interface/hal-interface.c
blob: 4d86504b5bc5b3ca34af57bd6412d755501d9b71 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
Overview
========

The
[AGL Project](https://www.automotivelinux.org/) is an automotive-specific
development environment that provides a Linux distribution
[(AGL UCB](https://www.automotivelinux.org/software/unified-code-base)).

AGL uses layers designed to be compatible with the
[Yocto Project](https://www.yoctoproject.org) and the
[OpenEmbedded Project (OE)](https://www.openembedded.org/wiki/Main_Page).

This section provides information about the layers used by the AGL Project:

* **`meta-agl`**: Minimal set of software needed to create an AGL distribution
  used to boot a system.
  AGL profiles are built on top of this minimal set of software.

* **`meta-agl-demo`**: Provides a reference or demo platform and applications
  for the AGL Distribution.
  The reference UI is part of the `meta-agl-demo` layer.

* **`meta-agl-devel`**: Contains components under development or being tested.
  This layer also contains software packages that OEMs need but do not exist
  in AGL.
81 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
/*
 * Copyright (C) 2016 "IoT.bzh"
 * Author Fulup Ar Foll <fulup@iot.bzh>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * reference: 
 *   amixer contents; amixer controls;
 *   http://www.tldp.org/HOWTO/Alsa-sound-6.html 
 */
#define _GNU_SOURCE  // needed for vasprintf
#include <string.h>
#include "hal-interface.h"
#include <systemd/sd-event.h>

alsaHalSndCardT *halSndCard;


// Force specific HAL to depend on ShareHalLib
PUBLIC char* SharedHalLibVersion = "1.0";

// Subscribe to AudioBinding events

STATIC void halSubscribe(afb_req request) {
    const char *devidJ = afb_req_value(request, "devid");
    if (devidJ == NULL) {
        afb_req_fail_f(request, "devidJ-missing", "devidJ=hw:xxx missing");
    }
}

// Map HAL ctlName to ctlLabel

STATIC int halCtlStringToIndex(const char* label) {
    alsaHalMapT *halCtls = halSndCard->ctls;

    for (int idx = 0; halCtls[idx].tag != EndHalCrlTag; idx++) {
        if (halCtls[idx].label && !strcmp(halCtls[idx].label, label)) return idx;
    }

    // not found
    return -1;
}

STATIC int halCtlTagToIndex(halCtlsTagT tag) {
    alsaHalMapT *halCtls = halSndCard->ctls;

    for (int idx = 0; halCtls[idx].tag != EndHalCrlTag; idx++) {
        if (halCtls[idx].tag == tag) return idx;
    }

    // not found
    return -1;
}


// Return ALL HAL snd controls

PUBLIC void halListCtls(afb_req request) {
    alsaHalMapT *halCtls = halSndCard->ctls;
    json_object *ctlsHalJ = json_object_new_array();

    for (int idx = 0; halCtls[idx].ctl.numid; idx++) {
        json_object *ctlHalJ = json_object_new_object();

        if (halCtls[idx].label) json_object_object_add(ctlHalJ, "label", json_object_new_string(halCtls[idx].label));
        else json_object_object_add(ctlHalJ, "label", json_object_new_string("HAL Label Not Set "));
        json_object_object_add(ctlHalJ, "tag", json_object_new_int(halCtls[idx].tag));
        json_object_object_add(ctlHalJ, "count", json_object_new_int(halCtls[idx].ctl.count));

        json_object_array_add(ctlsHalJ, ctlHalJ);
    }

    afb_req_success(request, ctlsHalJ, NULL);
}

STATIC int halGetCtlIndex(afb_req request, json_object*ctlInJ) {
    json_object *tmpJ;
    int tag, index, done;

    // check 1st short command mode [tag1, tag2, ...]
    enum json_type jtype = json_object_get_type(ctlInJ);
    switch (jtype) {
        case json_type_array:
            tmpJ = json_object_array_get_idx(ctlInJ, 0);
            tag = json_object_get_int(tmpJ);
            index = halCtlTagToIndex(tag);
            break;

        case json_type_int:
            tag = json_object_get_int(ctlInJ);
            index = halCtlTagToIndex(tag);
            break;

        case json_type_object:
            done = json_object_object_get_ex(ctlInJ, "tag", &tmpJ);
            if (done) {
                tag = json_object_get_int(tmpJ);
                index = halCtlTagToIndex(tag);
            } else {
                const char *label;
                done = json_object_object_get_ex(ctlInJ, "label", &tmpJ);
                if (!done) goto OnErrorExit;
                label = json_object_get_string(tmpJ);
                index = halCtlStringToIndex(label);
            }
            break;

        default:
            goto OnErrorExit;
    }

    if (index < 0) goto OnErrorExit;

    // return corresponding lowlevel numid to querylist
    return index;

OnErrorExit:
    afb_req_fail_f(request, "ctl-invalid", "No Label/Tag given ctl='%s'", json_object_get_string(ctlInJ));
    return -1;
}

STATIC int halCallAlsaSetCtls(json_object *ctlsOutJ) {
    json_object *responseJ, *queryJ;
    int err;

    // Call now level CTL
    queryJ = json_object_new_object();
    json_object_object_add(queryJ, "devid", json_object_new_string(halSndCard->devid));
    json_object_object_add(queryJ, "ctl", ctlsOutJ);

    err = afb_service_call_sync("alsacore", "setctl", queryJ, &responseJ);
    json_object_put(responseJ); // let's ignore response

    return err;
}


// retrieve a single HAL control from its tag.

PUBLIC int halSetCtlByTag(halRampEnumT tag, int value) {
    json_object *ctlJ = json_object_new_array();
    alsaHalMapT *halCtls = halSndCard->ctls;
    int err, index;

    index = halCtlTagToIndex(tag);
    if (index < 0) goto OnErrorExit;

    json_object_array_add(ctlJ, json_object_new_int(halCtls[index].ctl.numid));
    json_object_array_add(ctlJ, volumeNormalise(ACTION_SET, &halCtls[index].ctl, json_object_new_int(value)));

    err = halCallAlsaSetCtls(ctlJ);

    return err;

OnErrorExit:
    return -1;
}


// Translate high level control to low level and call lower layer

PUBLIC void halSetCtls(afb_req request) {
    alsaHalMapT *halCtls = halSndCard->ctls;
    int err, done, index;
    json_object *ctlsInJ, *ctlsOutJ, *valuesJ;

    // get query from request
    ctlsInJ = afb_req_json(request);

    switch (json_object_get_type(ctlsInJ)) {
        case json_type_object:
        {
            ctlsOutJ = json_object_new_object();

            // control is in literal form {tag=xxx, label=xxx, value=xxxx}
            index = halGetCtlIndex(request, ctlsInJ);
            if (index < 0) goto OnErrorExit;

            done = json_object_object_get_ex(ctlsInJ, "val", &valuesJ);
            if (!done) {
                afb_req_fail_f(request, "ctl-invalid", "No val=[val1, ...] ctl='%s'", json_object_get_string(ctlsInJ));
                goto OnErrorExit;
            }

            json_object_object_add(ctlsOutJ, "id", json_object_new_int(halCtls[index].ctl.numid));
            json_object_object_add(ctlsOutJ, "val", volumeNormalise(ACTION_SET, &halCtls[index].ctl, valuesJ));
            break;
        }

        case json_type_array:
        {
            ctlsOutJ = json_object_new_array();

            for (int idx = 0; idx < json_object_array_length(ctlsInJ); idx++) {
                json_object *ctlInJ = json_object_array_get_idx(ctlsInJ, idx);
                index = halGetCtlIndex(request, ctlInJ);
                if (index < 0) goto OnErrorExit;

                done = json_object_object_get_ex(ctlInJ, "val", &valuesJ);
                if (!done) {
                    afb_req_fail_f(request, "ctl-invalid", "No val=[val1, ...] ctl='%s'", json_object_get_string(ctlsInJ));
                    goto OnErrorExit;
                }
                // let's create alsa low level set control request
                json_object *ctlOutJ = json_object_new_object();
                json_object_object_add(ctlOutJ, "id", json_object_new_int(halCtls[index].ctl.numid));
                json_object_object_add(ctlOutJ, "val", volumeNormalise(ACTION_SET, &halCtls[index].ctl, valuesJ));

                json_object_array_add(ctlsOutJ, ctlOutJ);
            }
            break;
        }

        default:
            afb_req_fail_f(request, "ctl-invalid", "Not a valid JSON ctl='%s'", json_object_get_string(ctlsInJ));
            goto OnErrorExit;
    }

    err = halCallAlsaSetCtls(ctlsOutJ);
    if (err) {
        afb_req_fail_f(request, "subcall:alsacore/setctl", "%s", json_object_get_string(ctlsOutJ));
        goto OnErrorExit;
    }

    afb_req_success(request, NULL, NULL);
    return;

OnErrorExit:
    return;
};

// Remap low level controls into HAL hight level ones

STATIC json_object *HalGetPrepareResponse(afb_req request, json_object *ctlsJ) {
    alsaHalMapT *halCtls = halSndCard->ctls;
    json_object *halResponseJ;
    int length;

    switch (json_object_get_type(ctlsJ)) {
        case json_type_array:
            // responseJ is a JSON array
            halResponseJ = json_object_new_array();
            length = json_object_array_length(ctlsJ);
            break;
        case json_type_object:
            halResponseJ = NULL;
            length = 1;
            break;
        default:
            afb_req_fail_f(request, "ctls-notarray", "Invalid Controls return from alsa/getcontrol ctlsJ=%s", json_object_get_string(ctlsJ));
            goto OnErrorExit;
    }

    // loop on array and store values into client context
    for (int idx = 0; idx < length; idx++) {
        json_object *sndCtlJ, *valJ, *numidJ;
        int numid;

        // extract control from array if any
        if (halResponseJ) sndCtlJ = json_object_array_get_idx(ctlsJ, idx);
        else sndCtlJ = ctlsJ;

        if (!json_object_object_get_ex(sndCtlJ, "id", &numidJ) || !json_object_object_get_ex(sndCtlJ, "val", &valJ)) {
            afb_req_fail_f(request, "ctl-invalid", "Invalid Control return from alsa/getcontrol ctl=%s", json_object_get_string(sndCtlJ));
            goto OnErrorExit;
        }

        // HAL and Business logic use the same AlsaMixerHal.h direct conversion
        numid = (halCtlsTagT) json_object_get_int(numidJ);

        for (int idx = 0; halCtls[idx].ctl.numid; idx++) {
            if (halCtls[idx].ctl.numid == numid) {
                // translate low level numid to HAL one and normalise values
                json_object *halCtlJ = json_object_new_object();
                json_object_object_add(halCtlJ, "label", json_object_new_string(halCtls[idx].label)); // idx+1 == HAL/NUMID
                json_object_object_add(halCtlJ, "tag", json_object_new_int(halCtls[idx].tag)); // idx+1 == HAL/NUMID
                json_object_object_add(halCtlJ, "val", volumeNormalise(ACTION_GET, &halCtls[idx].ctl, valJ));

                if (halResponseJ) json_object_array_add(halResponseJ, halCtlJ);
                else halResponseJ = halCtlJ;

                break;
            }
        }
        if (halCtls[idx].ctl.numid == 0) {
            afb_req_fail_f(request, "ctl-invalid", "Invalid Control numid=%d from alsa/getcontrol ctlJ=%s", numid, json_object_get_string(sndCtlJ));
            goto OnErrorExit;
        }
    }
    return halResponseJ;

OnErrorExit:
    return NULL;
}

STATIC json_object *halCallAlsaGetCtls(json_object *ctlsOutJ) {
    json_object *responseJ, *queryJ;
    int err, done;

    // Call now level CTL
    queryJ = json_object_new_object();
    json_object_object_add(queryJ, "devid", json_object_new_string(halSndCard->devid));
    json_object_object_add(queryJ, "ctl", ctlsOutJ);

    err = afb_service_call_sync("alsacore", "getctl", queryJ, &responseJ);
    if (err) goto OnErrorExit;

    // Let ignore info data if any and keep on response
    done = json_object_object_get_ex(responseJ, "response", &responseJ);
    if (!done) goto OnErrorExit;

    return responseJ;

OnErrorExit:
    return NULL;
}

// retrieve a single HAL control from its tag.

PUBLIC json_object *halGetCtlByTag(halRampEnumT tag) {
    json_object *responseJ, *valJ;
    alsaHalMapT *halCtls = halSndCard->ctls;
    int done, index;

    index = halCtlTagToIndex(tag);
    if (index < 0) goto OnErrorExit;
    responseJ = halCallAlsaGetCtls(json_object_new_int(halCtls[index].ctl.numid));

    done = json_object_object_get_ex(responseJ, "val", &valJ);
    if (!done) goto OnErrorExit;

    return volumeNormalise(ACTION_GET, &halCtls[index].ctl, valJ);

OnErrorExit:
    return NULL;
}


// Translate high level control to low level and call lower layer

PUBLIC void halGetCtls(afb_req request) {
    int index;
    alsaHalMapT *halCtls = halSndCard->ctls;
    json_object *ctlsInJ, *ctlsOutJ, *responseJ;

    // get query from request
    ctlsInJ = afb_req_json(request);
    ctlsOutJ = json_object_new_array();

    switch (json_object_get_type(ctlsInJ)) {
        case json_type_object:
        {

            index = halGetCtlIndex(request, ctlsInJ);
            if (index < 0) goto OnErrorExit;
            json_object_array_add(ctlsOutJ, json_object_new_int(halCtls[index].ctl.numid));
            break;
        }

        case json_type_array:
        {

            for (int idx = 0; idx < json_object_array_length(ctlsInJ); idx++) {
                json_object *ctlInJ = json_object_array_get_idx(ctlsInJ, idx);
                index = halGetCtlIndex(request, ctlInJ);
                if (index < 0) goto OnErrorExit;
                json_object_array_add(ctlsOutJ, json_object_new_int(halCtls[index].ctl.numid));
            }
            break;
        }

        default:
            afb_req_fail_f(request, "ctl-invalid", "Not a valid JSON ctl='%s'", json_object_get_string(ctlsInJ));
            goto OnErrorExit;
    }

    // Call now level CTL
    responseJ = halCallAlsaGetCtls(ctlsOutJ);
    if (!responseJ) {
        afb_req_fail_f(request, "subcall:alsacore/getctl", "%s", json_object_get_string(responseJ));
        goto OnErrorExit;
    }

    // map back low level response to HAL ctl with normalised values
    json_object *halResponse = HalGetPrepareResponse(request, responseJ);
    if (!halResponse) goto OnErrorExit;

    afb_req_success(request, halResponse, NULL);
    return;

OnErrorExit:
    return;
};

STATIC int UpdateOneSndCtl(alsaHalCtlMapT *ctl, json_object *sndCtlJ) {
    json_object *tmpJ, *ctlJ;

    json_object_object_get_ex(sndCtlJ, "name", &tmpJ);
    ctl->name = (char*) json_object_get_string(tmpJ);

    json_object_object_get_ex(sndCtlJ, "id", &tmpJ);
    ctl->numid = json_object_get_int(tmpJ);

    // make sure we face a valid Alsa Low level ctl
    if (!json_object_object_get_ex(sndCtlJ, "ctl", &ctlJ)) goto OnErrorExit;

    json_object_object_get_ex(ctlJ, "min", &tmpJ);
    ctl->minval = json_object_get_int(tmpJ);

    json_object_object_get_ex(ctlJ, "max", &tmpJ);
    ctl->maxval = json_object_get_int(tmpJ);

    json_object_object_get_ex(ctlJ, "step", &tmpJ);
    ctl->step = json_object_get_int(tmpJ);

    json_object_object_get_ex(ctlJ, "count", &tmpJ);
    ctl->count = json_object_get_int(tmpJ);

    json_object_object_get_ex(ctlJ, "type", &tmpJ);
    ctl->type = (snd_ctl_elem_type_t) json_object_get_int(tmpJ);

    // process dbscale TLV if any
    if (json_object_object_get_ex(sndCtlJ, "tlv", &tmpJ)) {
        json_object *dbscaleJ;

        if (!json_object_object_get_ex(tmpJ, "dbscale", &dbscaleJ)) {
            AFB_WARNING("TLV found but not DBscale attached ctl name=%s numid=%d", ctl->name, ctl->numid);
        } else {
            ctl->dbscale = malloc(sizeof (alsaHalDBscaleT));

            json_object_object_get_ex(dbscaleJ, "min", &tmpJ);
            ctl->dbscale->min = (snd_ctl_elem_type_t) json_object_get_int(tmpJ);

            json_object_object_get_ex(dbscaleJ, "max", &tmpJ);
            ctl->dbscale->max = (snd_ctl_elem_type_t) json_object_get_int(tmpJ);

            json_object_object_get_ex(dbscaleJ, "step", &tmpJ);
            ctl->dbscale->step = (snd_ctl_elem_type_t) json_object_get_int(tmpJ);

            json_object_object_get_ex(dbscaleJ, "mute", &tmpJ);
            ctl->dbscale->mute = (snd_ctl_elem_type_t) json_object_get_int(tmpJ);
        }
    }

    return 0;

OnErrorExit:
    return -1;
}

// this is call when after all bindings are loaded

PUBLIC int halServiceInit(const char *apiPrefix, alsaHalSndCardT *alsaHalSndCard) {
    int err;
    json_object *queryurl, *responseJ, *devidJ, *ctlsJ, *tmpJ;
    alsaHalMapT *halCtls = alsaHalSndCard->ctls;

    // if not volume normalisation CB provided use default one
    if (!alsaHalSndCard->volumeCB) alsaHalSndCard->volumeCB = volumeNormalise;
    halSndCard = alsaHalSndCard;

    err = afb_daemon_require_api("alsacore", 1);
    if (err) {
        AFB_ERROR("AlsaCore missing cannot use AlsaHAL");
        goto OnErrorExit;
    }

    // register HAL with Alsa Low Level Binder
    queryurl = json_object_new_object();
    json_object_object_add(queryurl, "prefix", json_object_new_string(apiPrefix));
    json_object_object_add(queryurl, "sndname", json_object_new_string(alsaHalSndCard->name));

    err = afb_service_call_sync("alsacore", "halregister", queryurl, &responseJ);
    json_object_put(queryurl);
    if (err) {
        AFB_NOTICE("Fail to register HAL to ALSA lowlevel binding Response='%s'", json_object_get_string(responseJ));
        goto OnErrorExit;
    }

    // extract sound devidJ from HAL registration
    if (!json_object_object_get_ex(responseJ, "response", &tmpJ) || !json_object_object_get_ex(tmpJ, "devid", &devidJ)) {
        AFB_ERROR("Ooops: Internal error no devidJ return from HAL registration Response='%s'", json_object_get_string(responseJ));
        goto OnErrorExit;
    }

    // save devid for future use
    halSndCard->devid = strdup(json_object_get_string(devidJ));

    // for each Non Alsa Control callback create a custom control
    ctlsJ = json_object_new_array();
    for (int idx = 0; (halCtls[idx].ctl.name || halCtls[idx].ctl.numid); idx++) {
        json_object *ctlJ;

        // Try to find best equivalent label for tag
        if (halCtls[idx].tag >StartHalCrlTag && halCtls[idx].tag < EndHalCrlTag && halCtls[idx].label != NULL) {
            halCtls[idx].label = halCtlsLabels[halCtls[idx].tag];
        } else {
            if (halCtls[idx].ctl.name) halCtls[idx].label=halCtls[idx].ctl.name;
            else if (halCtls[idx].info) halCtls[idx].label=halCtls[idx].info;
        }

        ctlJ = json_object_new_object();
        if (halCtls[idx].ctl.numid) json_object_object_add(ctlJ, "ctl", json_object_new_int(halCtls[idx].ctl.numid));
        if (halCtls[idx].ctl.name) json_object_object_add(ctlJ, "name", json_object_new_string(halCtls[idx].ctl.name));
        if (halCtls[idx].ctl.minval) json_object_object_add(ctlJ, "min", json_object_new_int(halCtls[idx].ctl.minval));
        if (halCtls[idx].ctl.maxval) json_object_object_add(ctlJ, "max", json_object_new_int(halCtls[idx].ctl.maxval));
        if (halCtls[idx].ctl.step) json_object_object_add(ctlJ, "step", json_object_new_int(halCtls[idx].ctl.step));
        if (halCtls[idx].ctl.type) json_object_object_add(ctlJ, "type", json_object_new_int(halCtls[idx].ctl.type));
        if (halCtls[idx].ctl.count) json_object_object_add(ctlJ, "count", json_object_new_int(halCtls[idx].ctl.count));
        if (halCtls[idx].ctl.value) json_object_object_add(ctlJ, "value", json_object_new_int(halCtls[idx].ctl.value));

        if (halCtls[idx].ctl.dbscale) {
            json_object *dbscaleJ = json_object_new_object();
            if (halCtls[idx].ctl.dbscale->max) json_object_object_add(dbscaleJ, "max", json_object_new_int(halCtls[idx].ctl.dbscale->max));
            if (halCtls[idx].ctl.dbscale->min) json_object_object_add(dbscaleJ, "min", json_object_new_int(halCtls[idx].ctl.dbscale->min));
            if (halCtls[idx].ctl.dbscale->step) json_object_object_add(dbscaleJ, "step", json_object_new_int(halCtls[idx].ctl.dbscale->step));
            if (halCtls[idx].ctl.dbscale->mute) json_object_object_add(dbscaleJ, "mute", json_object_new_int(halCtls[idx].ctl.dbscale->mute));
            json_object_object_add(ctlJ, "dbscale", dbscaleJ);
        }

        if (halCtls[idx].ctl.enums) {
            json_object *enumsJ = json_object_new_array();
            for (int jdx = 0; halCtls[idx].ctl.enums[jdx]; jdx++) {
                json_object_array_add(enumsJ, json_object_new_string(halCtls[idx].ctl.enums[jdx]));
            }
            json_object_object_add(ctlJ, "enums", enumsJ);
        }
        json_object_array_add(ctlsJ, ctlJ);
    }

    // Build new queryJ to add HAL custom control if any
    if (json_object_array_length(ctlsJ) > 0) {
        queryurl = json_object_new_object();
        json_object_get(devidJ); // make sure devidJ does not get free by 1st call.
        json_object_object_add(queryurl, "devid", devidJ);
        json_object_object_add(queryurl, "ctl", ctlsJ);
        json_object_object_add(queryurl, "mode", json_object_new_int(QUERY_COMPACT));
        err = afb_service_call_sync("alsacore", "addcustomctl", queryurl, &responseJ);
        if (err) {
            AFB_ERROR("Fail creating HAL Custom ALSA ctls Response='%s'", json_object_get_string(responseJ));
            goto OnErrorExit;
        }
    }

    // Make sure response is valid 
    json_object_object_get_ex(responseJ, "response", &ctlsJ);
    if (json_object_get_type(ctlsJ) != json_type_array) {
        AFB_ERROR("Response Invalid JSON array ctls Response='%s'", json_object_get_string(responseJ));
        goto OnErrorExit;
    }

    // update HAL data from JSON response
    for (int idx = 0; idx < json_object_array_length(ctlsJ); idx++) {
        json_object *ctlJ = json_object_array_get_idx(ctlsJ, idx);
        err = UpdateOneSndCtl(&halCtls[idx].ctl, ctlJ);
        if (err) {
            AFB_ERROR("Fail found MAP Alsa Low level=%s", json_object_get_string(ctlJ));
            goto OnErrorExit;
        }
    }


    // finally register for alsa lowlevel event
    queryurl = json_object_new_object();
    json_object_object_add(queryurl, "devid", devidJ);
    err = afb_service_call_sync("alsacore", "subscribe", queryurl, &responseJ);
    if (err) {
        AFB_ERROR("Fail subscribing to ALSA lowlevel events");
        goto OnErrorExit;
    }

    return (0);

OnErrorExit:
    return (1);
};


// This receive all event this binding subscribe to 

PUBLIC void halServiceEvent(const char *evtname, json_object *eventJ) {
    int numid;
    alsaHalMapT *halCtls = halSndCard->ctls;
    json_object *numidJ, *valuesJ;

    AFB_DEBUG("halServiceEvent evtname=%s [msg=%s]", evtname, json_object_get_string(eventJ));

    json_object_object_get_ex(eventJ, "id", &numidJ);
    numid = json_object_get_int(numidJ);
    if (!numid) {
        AFB_ERROR("halServiceEvent noid: evtname=%s [msg=%s]", evtname, json_object_get_string(eventJ));
        return;
    }
    json_object_object_get_ex(eventJ, "val", &valuesJ);

    // search it corresponding numid in halCtls attach a callback
    if (numid) {
        for (int idx = 0; halCtls[idx].ctl.numid; idx++) {
            if (halCtls[idx].ctl.numid == numid && halCtls[idx].cb.callback != NULL) {
                halCtls[idx].cb.callback(halCtls[idx].tag, &halCtls[idx].ctl, halCtls[idx].cb.handle, valuesJ);
            }
        }
    }
}

// Every HAL export the same API & Interface Mapping from SndCard to AudioLogic is done through alsaHalSndCardT
PUBLIC afb_verb_v2 halServiceApi[] = {
    /* VERB'S NAME         FUNCTION TO CALL         SHORT DESCRIPTION */
    { .verb = "ping", .callback = pingtest, .info = "ping test for API"},
    { .verb = "ctl-list", .callback = halListCtls, .info = "List AGL normalised Sound Controls"},
    { .verb = "ctl-get", .callback = halGetCtls, .info = "Get one/many sound controls"},
    { .verb = "ctl-set", .callback = halSetCtls, .info = "Set one/many sound controls"},
    { .verb = "evt-sub", .callback = halSubscribe, .info = "Subscribe to HAL events"},
    { .verb = NULL} /* marker for end of the array */
};