summaryrefslogtreecommitdiffstats
path: root/plugins/media/media-api.h
blob: 70dcc1e650eb26debf580a634a535161fb86c600 (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
/*
 * Copyright (C) 2016 "IoT.bzh"
 * Author "Manuel Bachmann"
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef MEDIA_API_H
#define MEDIA_API_H

#include "media-rygel.h"

/* -------------- PLUGIN DEFINITIONS ----------------- */

/* private client context [will be destroyed when client leaves] */
typedef struct {
  void *media_server;          /* handle to implementation (Rygel...) */
  unsigned int index;          /* currently selected media file       */
} mediaCtxHandleT;

PUBLIC char* _rygel_list (mediaCtxHandleT *);

#endif /* MEDIA_API_H */
f2 } /* 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 */ }
/*
 * AlsaUseCase -- provide low level interface with ALSA lib (extracted from alsa-json-gateway code)
 * Copyright (C) 2015,2016,2017, 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.

   References:
      http://www.alsa-project.org/alsa-doc/alsa-lib/group__ucm.html
      https://www.alsa-project.org/main/index.php/DAPM
      http://alsa-lib.sourcearchive.com/documentation/1.0.24.1-2/group__Use_ga4332c6bb50481bbdaf21be11551fb930.html
      https://android.googlesource.com/platform/hardware/qcom/audio/+/jb-mr1-dev/libalsa-intf/alsa_ucm.h
 
   Sample alsaucm commands using /usr/share/alsa/ucm/PandaBoard 
    - alsaucm  -c PandaBoard list _verbs
    - alsaucm  -c PandaBoard list _devices/HiFi
    - alsaucm  -c PandaBoard list _modifiers/HiFi  #need to uncomment modifiers section
    - alsaucm  -c PandaBoard list TQ/HiFi
    - alsaucm  -c PandaBoard get TQ/HiFi/Voice
    - alsaucm  -c PandaBoard get PlaybackPCM//HiFi
    - alsaucm  -c PandaBoard set _verb HiFi
    - alsaucm  -c PandaBoard set _verb HiFi _enadev Headset
    - alsaucm  -c 'HDA Intel PCH'  set _verb HiFi set  _enadev Headphone set _enamod RecordMedia
    - alsaucm  -c 'HDA Intel PCH'  set _verb HiFi get OutputDspName//

*/
#define _GNU_SOURCE  // needed for vasprintf

#include <alsa/asoundlib.h>
#include "AlsaCoreBinding.h"
#include <alsa/asoundlib.h>
#include <alsa/use-case.h>

typedef struct {
     snd_use_case_mgr_t *ucm;
     int  cardId;
     char *cardName;
}    ucmHandleT;

static ucmHandleT ucmHandles[MAX_SND_CARD];

// Cache opened UCM handles 
STATIC int alsaUseCaseOpen (struct afb_req request, queryValuesT *queryValues, int allowNewMgr) {
    snd_ctl_t   *ctlDev;
    snd_ctl_card_info_t *cardinfo;
    snd_use_case_mgr_t *ucmHandle;
    const char *cardName;
    int cardId, idx, idxFree=-1, err;
    
    // open control interface for devid
    err = snd_ctl_open(&ctlDev, queryValues->devid, SND_CTL_READONLY);
    if (err < 0) {
        ctlDev=NULL;
        afb_req_fail_f (request, "devid-unknown", "SndCard devid=[%s] Not Found err=%d", queryValues->devid, err);
        goto OnErrorExit;
    }
    
    snd_ctl_card_info_alloca(&cardinfo);    
    if ((err = snd_ctl_card_info(ctlDev, cardinfo)) < 0) {        
        afb_req_fail_f (request, "devid-invalid", "SndCard devid=[%s] Not Found err=%s", queryValues->devid, snd_strerror(err));
        goto OnErrorExit;
    }
    
    // search for an existing subscription and mark 1st free slot
    cardId   = snd_ctl_card_info_get_card(cardinfo); 
    for (idx= 0; idx < MAX_SND_CARD; idx ++) {
      if (ucmHandles[idx].ucm != NULL) {
          if (ucmHandles[idx].cardId == cardId) goto OnSuccessExit;
      } else if (idxFree == -1) idxFree= idx;
    };
    
    if (!allowNewMgr) {
        afb_req_fail_f (request, "ucm-nomgr", "SndCard devid=[%s] no exiting UCM manager session", queryValues->devid);
        goto OnErrorExit;                
    }

    if (idxFree < 0 && idx == MAX_SND_CARD) {
        afb_req_fail_f (request, "ucm-toomany", "SndCard devid=[%s] too many open UCM Max=%d", queryValues->devid, MAX_SND_CARD);
        goto OnErrorExit;        
    }
    
    idx = idxFree;
    cardName = snd_ctl_card_info_get_name(cardinfo); 
    err = snd_use_case_mgr_open(&ucmHandle, cardName);
    if (err) {
       afb_req_fail_f (request, "ucm-open", "SndCard devid=[%s] name=[%s] No UCM Profile err=%s", queryValues->devid, cardName, snd_strerror(err));
       goto OnErrorExit; 
    }
    ucmHandles[idx].ucm = ucmHandle;
    ucmHandles[idx].cardId = cardId;        
    ucmHandles[idx].cardName = strdup(cardName);        

  OnSuccessExit:
    if (ctlDev) snd_ctl_close(ctlDev);            
    return idx;
    
  OnErrorExit:   
    if (ctlDev) snd_ctl_close(ctlDev);            
    return -1;
}
      

PUBLIC void alsaUseCaseQuery(struct afb_req request) { 
    int err, verbCount, ucmIdx;
    const char **verbList;
    snd_use_case_mgr_t *ucmHandle;
    queryValuesT queryValues;
    json_object *ucmJs;
    const char *cardName;

    
    err = alsaCheckQuery (request, &queryValues);
    if (err) goto OnErrorExit;
    
    ucmIdx = alsaUseCaseOpen (request, &queryValues, TRUE);
    if (ucmIdx < 0) goto OnErrorExit;
    ucmHandle = ucmHandles [ucmIdx].ucm;
    cardName  = ucmHandles [ucmIdx].cardName;
    
    verbCount = snd_use_case_get_list (ucmHandle, "_verbs", &verbList);
    if (verbCount < 0) {
       afb_req_fail_f (request, "ucm-list", "SndCard devid=[%s] name=[%s] No UCM Verbs", queryValues.devid, cardName);
       goto OnErrorExit; 
    }
    
    ucmJs = json_object_new_array();
    for (int idx=0; idx < verbCount; idx +=2) {
        int devCount, modCount, tqCount;
        const char **devList, **modList, **tqList;
        json_object *ucmJ = json_object_new_object();
        char identifier[32];
        
        json_object_object_add (ucmJ, "verb", json_object_new_string(verbList[idx]));
        if (verbList[idx+1]) json_object_object_add (ucmJ, "info", json_object_new_string(verbList[idx+1]));
        
        DEBUG (afbIface, "Verb[%d] Action=%s Info=%s", idx, verbList[idx], verbList[idx+1]);

        snprintf (identifier, sizeof(identifier), "_devices/%s", verbList[idx]);
        devCount = snd_use_case_get_list (ucmHandle, identifier, &devList);
        if (devCount > 0) {
            json_object *devsJ = json_object_new_array();
            
            for (int jdx=0; jdx < devCount; jdx+=2) {
                json_object *devJ = json_object_new_object();
                DEBUG (afbIface, "device[%d] Action=%s Info=%s", jdx, devList[jdx], devList[jdx+1]);
                json_object_object_add (devJ, "dev", json_object_new_string(devList[jdx]));
                if (devList[jdx+1]) json_object_object_add (devJ, "info", json_object_new_string(devList[jdx+1]));                
                json_object_array_add (devsJ, devJ);
            }
            json_object_object_add(ucmJ,"devices", devsJ);
            snd_use_case_free_list(devList, err);
        }
        
        snprintf (identifier, sizeof(identifier), "_modifiers/%s", verbList[idx]);
        modCount = snd_use_case_get_list (ucmHandle, identifier, &modList);
        if (modCount > 0) {
            json_object *modsJ = json_object_new_array();
            
            for (int jdx=0; jdx < modCount; jdx+=2) {
                json_object *modJ = json_object_new_object();
                DEBUG (afbIface, "modifier[%d] Action=%s Info=%s", jdx, modList[jdx], modList[jdx+1]);
                json_object_object_add (modJ, "mod", json_object_new_string(modList[jdx]));
                if (modList[jdx+1]) json_object_object_add (modJ, "info", json_object_new_string(modList[jdx+1]));                
                json_object_array_add (modsJ, modJ);
            }
            json_object_object_add(ucmJ,"modifiers", modsJ);
            snd_use_case_free_list(modList, err);
        }

        snprintf (identifier, sizeof(identifier), "TQ/%s", verbList[idx]);
        tqCount = snd_use_case_get_list (ucmHandle, identifier, &tqList);
        if (tqCount > 0) {
            json_object *tqsJ = json_object_new_array();
            
            for (int jdx=0; jdx < tqCount; jdx+=2) {
                json_object *tqJ = json_object_new_object();
                DEBUG (afbIface, "toneqa[%d] Action=%s Info=%s", jdx, tqList[jdx], tqList[jdx+1]);
                json_object_object_add (tqJ, "tq", json_object_new_string(tqList[jdx]));
                if (tqList[jdx+1]) json_object_object_add (tqJ, "info", json_object_new_string(tqList[jdx+1]));                
                json_object_array_add (tqsJ, tqJ);
            }
            json_object_object_add(ucmJ,"tqs", tqsJ);
            snd_use_case_free_list(tqList, err);
        }
        
        json_object_array_add (ucmJs, ucmJ);
    }
    
    afb_req_success (request, ucmJs, NULL);
    snd_use_case_free_list(verbList, err);
        
  OnErrorExit:
    return;
}

STATIC json_object *ucmGetValue (ucmHandleT *ucmHandle, const char *verb, const char *mod, const char *label) {
    char identifier[80];
    char *value;
    int err;
    json_object *jValue;
    
    // handle optional parameters
    if (!mod)  mod=""; 
    if (!verb) verb="";
    
    if (!label) {
        NOTICE (afbIface, "ucmGetValue cardname=[%s] value label missing", ucmHandle->cardName);
        goto OnErrorExit;        
    }
    
    snprintf (identifier, sizeof(identifier), "%s/%s/%s", label, mod, verb);
    err = snd_use_case_get (ucmHandle->ucm, identifier, (const char**)&value); // Note: value casting is a known "FEATURE" of AlsaUCM API
    if (err) {
        DEBUG (afbIface, "ucmGetValue cardname=[%s] identifier=[%s] error=%s", ucmHandle->cardName, identifier, snd_strerror (err));
        goto OnErrorExit;
    }
    
    // copy value into json object and free string
    jValue = json_object_new_string (value);
    free (value);
    return (jValue);    
    
  OnErrorExit:
    return (NULL);
}

PUBLIC void alsaUseCaseGet (struct afb_req request) {
    int err, ucmIdx, labelCount;
    queryValuesT queryValues;
    json_object *jResponse = json_object_new_object();
    json_object *jWarnings = json_object_new_array();
    const char *warnings=NULL;
    
    err = alsaCheckQuery (request, &queryValues);
    if (err) goto OnErrorExit;
    
    ucmIdx = alsaUseCaseOpen (request, &queryValues, TRUE);
    if (ucmIdx < 0) goto OnErrorExit;
    
    const char *cardName= ucmHandles[ucmIdx].cardName;
    
    const char *verb  = afb_req_value(request, "verb");
    const char *mod   = afb_req_value(request, "mod");
    const char *dev   = afb_req_value(request, "dev");
    
    if (dev && mod) {
        afb_req_fail_f (request, "ucmget-labels", "SndCard devid=[%s] name=[%s] UCM mod+dev incompatible", queryValues.devid, cardName);
        goto OnErrorExit;          
    } 
    
    // device selection is handle as a modifier
    if (dev) mod=dev;

    const char *labels = afb_req_value(request, "values");
    if (!labels) {
        afb_req_fail_f (request, "ucmget-labels", "SndCard devid=[%s] name=[%s] UCM values name missing", queryValues.devid, cardName);
        goto OnErrorExit;  
    } 
    
    json_object *jLabels = json_tokener_parse(labels);
    if (!jLabels) {
        afb_req_fail_f (request, "ucmget-notjson","labels=%s not a valid json entry", labels);
        goto OnErrorExit;        
    };
    
    enum json_type jtype= json_object_get_type(jLabels);
    switch (jtype) {
        json_object *jTmp;
        
        case json_type_array:
            labelCount = json_object_array_length (jLabels);
            break;
            
        case json_type_string:
            jTmp = json_object_new_array ();
            labelCount = 1;
            json_object_array_add (jTmp, jLabels);
            jLabels=jTmp;
            break;
        
        default:           
            afb_req_fail_f (request, "ucmget-notarray","labels=%s not valid JSON array", labels);
            goto OnErrorExit;        
    }

    for (int idx=0; idx < labelCount; idx++) {
        json_object *jValue, *jLabel;
        const char *label ;
        
        jLabel= json_object_array_get_idx (jLabels, idx);
        label= json_object_get_string (jLabel);
        jValue = ucmGetValue (&ucmHandles[ucmIdx], verb, mod, label);
        if (jValue) json_object_object_add (jResponse, label, jValue);
        else {
            json_object_array_add (jWarnings, jLabel);
        }
    }
    
    // use info section to notified not found values label
    if (json_object_array_length (jWarnings) > 0) {
       json_object *jTmp =  json_object_new_object ();
       json_object_object_add (jTmp, "no-context", jWarnings);
       warnings= json_object_to_json_string (jTmp);
    }
    afb_req_success (request, jResponse, warnings);
    
  OnErrorExit:
    return;        
}
    
PUBLIC void alsaUseCaseSet(struct afb_req request) { 
    int err, ucmIdx;
    queryValuesT queryValues;
    json_object *jResponse = json_object_new_object();
    
    err = alsaCheckQuery (request, &queryValues);
    if (err) goto OnErrorExit;
    
    ucmIdx = alsaUseCaseOpen (request, &queryValues, TRUE);
    if (ucmIdx < 0) goto OnErrorExit;
    
    snd_use_case_mgr_t *ucmMgr= ucmHandles[ucmIdx].ucm;
    const char *cardName= ucmHandles[ucmIdx].cardName;
    
    const char *verb = afb_req_value(request, "verb");
    const char *mod  = afb_req_value(request, "mod");
    const char *dev  = afb_req_value(request, "dev");
    // Known identifiers: _verb - set current verb = value _enadev - enable given device = value _disdev - disable given device = value _swdev/{old_device} - new_device = value

    if (verb) {
        err = snd_use_case_set (ucmMgr, "_verb", verb);
        if (err) {        
            afb_req_fail_f (request, "ucmset-verb", "SndCard devid=[%s] name=[%s] Invalid UCM verb=[%s] err=%s", queryValues.devid, cardName, verb, snd_strerror(err));
            goto OnErrorExit;
        }
    }

    if (dev) {
        err = snd_use_case_set (ucmMgr, "_enadev", dev);
        if (err) {        
            afb_req_fail_f (request, "ucmset-dev", "SndCard devid=[%s] name=[%s] Invalid UCMverb=[%s] dev=%s err=%s", queryValues.devid, cardName, verb, dev, snd_strerror(err));
            goto OnErrorExit;
        }
    }
    
    if (mod) {
        err = snd_use_case_set (ucmMgr, "_enamod", mod);
        if (err) {        
            afb_req_fail_f (request, "ucmset-mod", "SndCard devid=[%s] name=[%s] Invalid UCM verb=[%s] mod=[%s] err=%s", queryValues.devid, cardName, verb, mod, snd_strerror(err));
            goto OnErrorExit;
        }
    }
    
    // label are requested transfert request to get
    if (afb_req_value(request, "values")) return alsaUseCaseGet(request);
    
    if (queryValues.quiet <= 3) {
        json_object *jValue;

        jValue = ucmGetValue (&ucmHandles[ucmIdx], verb, dev, "OutputDspName");
        if (jValue) json_object_object_add (jResponse, "OutputDspName", jValue);
        
        jValue = ucmGetValue (&ucmHandles[ucmIdx], verb, dev, "PlaybackPCM");
        if (jValue) json_object_object_add (jResponse, "PlaybackPCM", jValue);

        jValue = ucmGetValue (&ucmHandles[ucmIdx], verb, mod, "CapturePCM");
        if (jValue) json_object_object_add (jResponse, "CapturePCM", jValue);
    }
    afb_req_success (request, jResponse, NULL);
 
  OnErrorExit:
    return;
}



PUBLIC void alsaUseCaseReset (struct afb_req request) {
    int err, ucmIdx;
    queryValuesT queryValues;
    
    err = alsaCheckQuery (request, &queryValues);
    if (err) goto OnErrorExit;
    
    ucmIdx = alsaUseCaseOpen (request, &queryValues, FALSE);
    if (ucmIdx < 0) goto OnErrorExit;
    
    err= snd_use_case_mgr_reset (ucmHandles[ucmIdx].ucm);
    if (err) {
        afb_req_fail_f (request, "ucmreset-fail","devid=%s Card Name=%s", queryValues.devid, ucmHandles[ucmIdx].cardName);
        goto OnErrorExit;        
    }
    
    afb_req_success (request, NULL, NULL);
    
  OnErrorExit:
    return;        
}
    
PUBLIC void alsaUseCaseClose (struct afb_req request) {
    int err, ucmIdx;
    queryValuesT queryValues;
    
    err = alsaCheckQuery (request, &queryValues);
    if (err) goto OnErrorExit;
    
    ucmIdx = alsaUseCaseOpen (request, &queryValues, FALSE);
    if (ucmIdx < 0) goto OnErrorExit;
    
    err= snd_use_case_mgr_close (ucmHandles[ucmIdx].ucm);
    if (err) {
        afb_req_fail_f (request, "ucmreset-close","devid=%s Card Name=%s", queryValues.devid, ucmHandles[ucmIdx].cardName);
        goto OnErrorExit;        
    }
    
    // do not forget to release sound card name string
    free (ucmHandles[ucmIdx].cardName);
    
    afb_req_success (request, NULL, NULL);
    
  OnErrorExit:
    return;        
}