/* * Copyright (C) 2016 "IoT.bzh" * Author Fulup Ar Foll * * 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. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "AudioLogicLib.h" static struct afb_service srvitf; STATIC int cbCheckResponse (struct afb_req request, int iserror, struct json_object *result) { struct json_object *response, *status, *info; if (iserror) { // on error proxy information we got from lower layer if (result) { if (json_object_object_get_ex(result, "request", &response)) { json_object_object_get_ex(response, "info" , &info); json_object_object_get_ex(response, "status", &status); afb_req_fail(request, json_object_get_string(status), json_object_get_string(info)); goto OnExit; } } else { afb_req_fail(request, "cbCheckFail", "No Result inside API response" ); } goto OnErrorExit; } return (0); OnErrorExit: return (1); } PUBLIC void audioLogicSetVol(struct afb_req request) { struct json_object *queryurl; // keep request for callback to respond struct afb_req *handle = afb_req_store(request); // get client context AudioLogicCtxT *ctx = afb_req_context_get(request); const char *vol = afb_req_value(request, "vol"); if (vol == NULL) { afb_req_fail_f (request, "argument-missing", "vol=+-%[0,100] missing"); goto OnExit; } switch (vol[0]) { case '+': break; case '-': break; case '%': break; default: afb_req_fail_f (request, "value-invalid", "volume should be (+-%[0-100]xxx) vol=%s", vol); goto OnExit; } if (!ctx->halapi) { afb_req_fail_f(request, "context-invalid", "No valid halapi in client context"); goto OnExit; } // ********** Caluler le volume en % de manière intelligente queryurl=json_object_new_object(); json_object_object_add(ctx->queryurl, "pcm",json_object_new_int(Master_Playback_Volume)); json_object_object_add(ctx->queryurl, "value",json_object_new_int(volume)); // subcontract HAL API to process volume afb_service_call(srvitf, ctx->halapi, "volume", queryurl, audioLogicSetVolCB, handle); // final success/failure messages handle from callback OnExit: return; } // This callback is fired when afb_service_call for api/alsacore/subctl returns STATIC void alsaSubcribeCB (void *handle, int iserror, struct json_object *result) { struct afb_req request = afb_req_unstore(handle); if (!cbCheckResponse (request, iserror, result)) goto OnExit OnExit: return; } // Create and subscribe to alsacore ctl events PUBLIC void audioLogicMonitor(struct afb_req request) { // keep request for callback to respond struct afb_req *handle = afb_req_store(request); // get client context AudioLogicCtxT *ctx = afb_req_context_get(request); // push request to low level binding NOTICE (afbIface, "audioLogicMonitor ctx->devid=%s [ctx->queryurl=%s]", ctx->devid, json_object_to_json_string(ctx->queryurl)); if (ctx->queryurl) { json_object_get (ctx->queryurl); // Make sure usage count does not fall to zero afb_service_call(srvitf, "alsacore", "subscribe", ctx->queryurl, alsaSubcribeCB, handle); } else afb_req_fail_f(request, "context-invalid", "No valid queryurl in client context"); // success/failure messages return from callback } // Subscribe to AudioBinding events PUBLIC void audioLogicSubscribe (struct afb_req request) { return; } // Call when all bindings are loaded and ready to accept request PUBLIC void audioLogicGetVol(struct afb_req request) { // Should call here Hardware Alsa Abstraction Layer for corresponding Sound Card afb_req_success (request, NULL, NULL); return; } // This callback is fired when afb_service_call for api/alsacore/subctl returns STATIC void audioLogicOpenCB2 (void *handle, int iserror, struct json_object *result) { struct json_object *response; // Make sure we got a response from API struct afb_req request = afb_req_unstore(handle); if (!cbCheckResponse (request, iserror, result)) goto OnExit // Get response from object json_object_object_get_ex(result, "response", &response); if (!response) { afb_req_fail_f(request, "response-notfound", "No Controls return from HAL/getcontrol devid=[%] name=[%s]", ctx->devid, ctx->shortname); goto OnExit; } // get client context AudioLogicCtxT *ctx = afb_req_context_get(request); if (!ctx) { afb_req_fail_f(request, "ctx-notfound", "No Client Context HAL/getcontrol devid=[%] name=[%s]", ctx->devid, ctx->shortname); goto OnExit; } // extract sounds controls information from received Object struct json_object *ctls; json_object_object_get_ex(response, "ctls", &ctls); if (!ctls) { afb_req_fail_f(request, "ctls-notfound", "No Controls return from HAL/getcontrol devid=[%] name=[%s]", ctx->devid, ctx->shortname); goto OnExit; } // make sure return controls have a valid type if (json_object_get_type (ctls) != json_type_array ) { afb_req_fail_f(request, "ctls-notarray", "Invalid Controls return from HAL/getcontrol devid=[%] name=[%s]", ctx->devid, ctx->shortname); goto OnExit; } // loop on array and store values into client context for (int idx=0; idx < json_object_array_length (ctls), idx++) { struct json_object *ctl; halControlEnumT control; int value; ctl = json_object_array_get_idx(ctls, idx); if (json_object_array_length(ctl != 2)) { afb_req_fail_f(request, "ctl-invalid", "Invalid Control return from HAL/getcontrol devid=[%] name=[%s] ctl=%s" , ctx->devid, ctx->shortname, json_object_get_string(ctl)); goto OnExit; } // As HAL and Business logic use the same AlsaMixerHal.h direct conversion is not an issue control = (halControlEnumT)json_object_get_int(json_object_array_get_idx(ctl,0)); value = json_object_get_int(json_object_array_get_idx(ctl,0)); switch(control) { case Master_Playback_Volume: ctx->volumes.masterPlaybackVolume = value; break; case PCM_Playback_Volume: ctx->volumes.pcmPlaybackVolume = value; break; case PCM_Playback_Switch: ctx->volumes.pcmPlaybackSwitch = value; break; case Capture_Volume: ctx->volumes.captureVolume = value; break; default: NOTICE (afbIface, "audioLogicOpenCB2 unknown HAL control=[%s]", json_object_get_string(ctl)): } } OnExit: afb_req_context_set(request, ctx, free); return; } // This callback is fired when afb_service_call for api/alsacore/subctl returns STATIC void audioLogicOpenCB1 (void *handle, int iserror, struct json_object *result) { struct json_object *response, *subobj; // Make sure we got a valid API response struct afb_req request = afb_req_unstore(handle); if (!cbCheckResponse (request, iserror, result)) goto OnExit // Get response from object json_object_object_get_ex(result, "response", &response); if (!response) { afb_req_fail_f(request, "response-notfound", "No Controls return from HAL/getcontrol devid=[%] name=[%s]", ctx->devid, ctx->shortname); goto OnExit; } // attach client context to session AudioLogicCtxT *ctx = malloc (sizeof(AudioLogicCtxT)); // extract information from Json Alsa Object json_object_object_get_ex(response, "cardid", &subobj); if (subobj) ctx->cardid= json_object_get_int(subobj); // store devid as an object for further alsa request json_object_object_get_ex(response, "devid", &subobj); if (subobj) ctx->devid= strdup(json_object_get_string(subobj)); json_object_object_get_ex(response, "halapi", &subobj); if (subobj) ctx->halapi= strdup(json_object_get_string(subobj)); json_object_object_get_ex(response, "shortname", &subobj); if (subobj)ctx->shortname=strdup(json_object_get_string(subobj)); json_object_object_get_ex(response, "longname", &subobj); if (subobj)ctx->longname=strdup(json_object_get_string(subobj)); // save queryurl with devid only for further ALSA request ctx->queryurl=json_object_new_object(); json_object_object_add(ctx->queryurl, "devid",json_object_new_string(ctx->devid)); afb_req_context_set(request, ctx, free); // sound card was find let's store keycontrols into client session if (!ctx->halapi) { afb_req_fail_f(request, "hal-notfound", "No HAL found devid=[%] name=[%s]", ctx->devid, ctx->shortname); goto OnExit; } struct json_object *queryurl =json_object_new_object(); struct json_object *ctls =json_object_new_object(); // add sound controls we want to keep track of into client session context json_object_object_add(ctls, json_object_new_int((int)Master_Playback_Volume)); json_object_object_add(ctls, json_object_new_int((int)PCM_Playback_Volume)); json_object_object_add(ctls, json_object_new_int((int)PCM_Playback_Switch)); json_object_object_add(ctls, json_object_new_int((int)Capture_Volume)); // send request to soundcard HAL binding json_object_array_add(queryurl, ctx->queryurl); json_object_object_add(queryurl, "ctls",ctls); afb_service_call(srvitf, ctx->halapi, "getControl", queryurl, audioLogicOpenCB2, handle); afb_req_success(request, response, NULL); OnExit: // release original calling request afb_req_unref(request); return; } // Delegate to lowerlevel the mapping of soundcard name with soundcard ID PUBLIC void audioLogicOpen(struct afb_req request) { // Delegate query to lower level struct afb_req *handle = afb_req_store(request); if (!handle) afb_req_fail(request, "error", "out of memory"); else afb_service_call(srvitf, "alsacore", "getCardId", json_object_get(afb_req_json(request)), audioLogicOpenCB1, handle); } // Free client context create from audioLogicOpenCB PUBLIC void audioLogicClose (struct afb_req request) { // retrieve current client context to print debug info AudioLogicCtxT *ctx = (AudioLogicCtxT*) afb_req_context_get(request); DEBUG (afbIface, "audioLogicClose cardid=%d devid=%s shortname=%s longname=%s", ctx->cardid, ctx->devid, ctx->shortname, ctx->longname); } // this function is call after all binder are loaded and initialised PUBLIC int audioLogicInit (struct afb_service service) { srvitf = service; return 0; }