diff options
Diffstat (limited to 'HAL-afb')
-rw-r--r-- | HAL-afb/HAL-interface/CMakeLists.txt | 2 | ||||
-rw-r--r-- | HAL-afb/HAL-interface/hal-interface.c | 325 | ||||
-rw-r--r-- | HAL-afb/HAL-interface/hal-interface.h | 47 | ||||
-rw-r--r-- | HAL-afb/HAL-interface/hal-volramp.c | 166 | ||||
-rw-r--r-- | HAL-afb/HAL-interface/hal-volume.c (renamed from HAL-afb/HAL-interface/hal-volmap.c) | 7 | ||||
-rw-r--r-- | HAL-afb/HAL-plugin/HalPlugCtl.c | 2 | ||||
-rw-r--r-- | HAL-afb/HDA-intel/IntelHdaHAL.c | 33 | ||||
-rw-r--r-- | HAL-afb/Scarlett-Focusrite/ScarlettUsbHAL.c | 45 | ||||
-rw-r--r-- | HAL-afb/Unicens-USB/UnicensHAL.c | 15 | ||||
-rw-r--r-- | HAL-afb/Unicens-USB/UnicensVol.c | 4 |
10 files changed, 485 insertions, 161 deletions
diff --git a/HAL-afb/HAL-interface/CMakeLists.txt b/HAL-afb/HAL-interface/CMakeLists.txt index c367c5c..d20a283 100644 --- a/HAL-afb/HAL-interface/CMakeLists.txt +++ b/HAL-afb/HAL-interface/CMakeLists.txt @@ -21,7 +21,7 @@ PROJECT_TARGET_ADD(hal-interface) # Define targets - ADD_LIBRARY(${TARGET_NAME} STATIC hal-volmap.c hal-interface.c) + ADD_LIBRARY(${TARGET_NAME} STATIC hal-volume.c hal-volramp.c hal-interface.c) # Library properties SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES OUTPUT_NAME halinterface) diff --git a/HAL-afb/HAL-interface/hal-interface.c b/HAL-afb/HAL-interface/hal-interface.c index f4fc8df..76cdbd8 100644 --- a/HAL-afb/HAL-interface/hal-interface.c +++ b/HAL-afb/HAL-interface/hal-interface.c @@ -21,24 +21,37 @@ #define _GNU_SOURCE // needed for vasprintf #include <string.h> #include "hal-interface.h" +#include <systemd/sd-event.h> -static alsaHalMapT *halCtls; -static const char *halDevid; - - +alsaHalSndCardT *halSndCard; STATIC const char *halCtlsLabels[] = { - [StartHalCrlTag] = "NOT_USED", - [Master_Playback_Volume] = "Master_Playback_Volume", - [Master_OnOff_Switch] = "Master_OnOff_Switch", - [Master_Playback_Ramp] = "Master_Playback_Ramp", - [PCM_Playback_Volume] = "PCM_Playback_Volume", - [PCM_Playback_Switch] = "PCM_Playback_Switch", - [Capture_Volume] = "Capture_Volume", - - [EndHalCrlTag] = "NOT_USED" - + [Master_Playback_Volume] = "Master_Playback_Volume", + [Master_OnOff_Switch] = "Master_OnOff_Switch", + [Master_Playback_Ramp]= "Master_Playback_Ramp", + [PCM_Playback_Volume] = "PCM_Playback_Volume", + [PCM_Playback_Switch] = "PCM_Playback_Switch", + [Capture_Volume] = "Capture_Volume", + + [Vol_Ramp] = "Volume_Ramp", + [Vol_Ramp_Set_Mode] = "Volume_Ramp_Mode", + [Vol_Ramp_Set_Delay] = "Volume_Ramp_Delay", + [Vol_Ramp_Set_Down] = "Volume_Ramp_Down", + [Vol_Ramp_Set_Up] = "Volume_Ramp_Up", + + [EndHalCrlTag] = NULL +}; + +PUBLIC char *halVolRampModes[] = { + + [RAMP_VOL_NONE] = "None", + [RAMP_VOL_NORMAL] = "Normal", + [RAMP_VOL_SMOOTH] = "Smooth", + [RAMP_VOL_EMERGENCY] = "Emergency", + + [EndHalVolMod] = NULL, + }; // Force specific HAL to depend on ShareHalLib @@ -54,6 +67,7 @@ STATIC void halSubscribe(afb_req request) { // 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 (!strcmp (halCtls[idx].label, label)) return idx; @@ -64,6 +78,7 @@ STATIC int halCtlStringToIndex (const char* label) { } STATIC int halCtlTagToIndex (halCtlsEnumT tag) { + alsaHalMapT *halCtls= halSndCard->ctls; for (int idx = 0; halCtls[idx].tag != EndHalCrlTag; idx++) { if (halCtls[idx].tag == tag) return idx; @@ -76,10 +91,11 @@ STATIC int halCtlTagToIndex (halCtlsEnumT tag) { // Return ALL HAL snd controls PUBLIC void halListCtls(afb_req request) { - struct json_object *ctlsHalJ = json_object_new_array(); + alsaHalMapT *halCtls= halSndCard->ctls; + json_object *ctlsHalJ = json_object_new_array(); for (int idx = 0; halCtls[idx].ctl.numid; idx++) { - struct json_object *ctlHalJ = json_object_new_object(); + json_object *ctlHalJ = json_object_new_object(); json_object_object_add(ctlHalJ, "label", json_object_new_string(halCtls[idx].label)); json_object_object_add(ctlHalJ, "tag" , json_object_new_int(halCtls[idx].tag)); @@ -91,8 +107,8 @@ PUBLIC void halListCtls(afb_req request) { afb_req_success (request, ctlsHalJ, NULL); } -STATIC int halGetCtlIndex (afb_req request, struct json_object*ctlInJ) { - struct json_object *tmpJ; +STATIC int halGetCtlIndex (afb_req request, json_object*ctlInJ) { + json_object *tmpJ; int tag, index, done; // check 1st short command mode [tag1, tag2, ...] @@ -127,7 +143,6 @@ STATIC int halGetCtlIndex (afb_req request, struct json_object*ctlInJ) { goto OnErrorExit; } - if (index < 0) goto OnErrorExit; // return corresponding lowlevel numid to querylist @@ -139,10 +154,48 @@ STATIC int halGetCtlIndex (afb_req request, struct json_object*ctlInJ) { } +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; - struct json_object *ctlsInJ, *ctlsOutJ, *queryJ, *valuesJ, *responseJ; + json_object *ctlsInJ, *ctlsOutJ, *valuesJ; // get query from request ctlsInJ = afb_req_json(request); @@ -162,7 +215,7 @@ PUBLIC void halSetCtls(afb_req request) { } json_object_object_add (ctlsOutJ, "id", json_object_new_int(halCtls[index].ctl.numid)); - json_object_object_add (ctlsOutJ,"val", SetGetNormaliseVolumes (ACTION_SET, &halCtls[index].ctl, valuesJ)); + json_object_object_add (ctlsOutJ,"val", volumeNormalise (ACTION_SET, &halCtls[index].ctl, valuesJ)); break; } @@ -170,7 +223,7 @@ PUBLIC void halSetCtls(afb_req request) { ctlsOutJ = json_object_new_array(); for (int idx= 0; idx < json_object_array_length (ctlsInJ); idx++) { - struct json_object *ctlInJ = json_object_array_get_idx (ctlsInJ, idx); + json_object *ctlInJ = json_object_array_get_idx (ctlsInJ, idx); index= halGetCtlIndex (request, ctlInJ); if (index < 0) goto OnErrorExit; @@ -180,9 +233,9 @@ PUBLIC void halSetCtls(afb_req request) { goto OnErrorExit; } // let's create alsa low level set control request - struct json_object *ctlOutJ = json_object_new_object(); + 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", SetGetNormaliseVolumes (ACTION_SET, &halCtls[index].ctl, valuesJ)); + json_object_object_add (ctlOutJ, "val", volumeNormalise (ACTION_SET, &halCtls[index].ctl, valuesJ)); json_object_array_add (ctlsOutJ, ctlOutJ); } @@ -194,14 +247,9 @@ PUBLIC void halSetCtls(afb_req request) { goto OnErrorExit; } - // Call now level CTL - queryJ = json_object_new_object(); - json_object_object_add(queryJ, "devid", json_object_new_string (halDevid)); - json_object_object_add(queryJ, "numid", ctlsOutJ); - - err= afb_service_call_sync("alsacore", "setctl", queryJ, &responseJ); + err = halCallAlsaSetCtls (ctlsOutJ); if (err) { - afb_req_fail_f(request, "subcall:alsacore/setctl", "%s", json_object_get_string(responseJ)); + afb_req_fail_f(request, "subcall:alsacore/setctl", "%s", json_object_get_string(ctlsOutJ)); goto OnErrorExit; } @@ -213,24 +261,35 @@ OnErrorExit: }; // Remap low level controls into HAL hight level ones -STATIC json_object *HalGetPrepareResponse(afb_req request, struct json_object *ctlsJ) { - struct json_object *halResponseJ; - - // make sure return controls have a valid type - if (json_object_get_type(ctlsJ) != json_type_array) { - afb_req_fail_f(request, "ctls-notarray", "Invalid Controls return from alsa/getcontrol ctlsJ=%s", json_object_get_string(ctlsJ)); - goto OnErrorExit; +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; } - // responseJ is a JSON array - halResponseJ = json_object_new_array(); - // loop on array and store values into client context - for (int idx = 0; idx < json_object_array_length(ctlsJ); idx++) { - struct json_object *sndCtlJ, *valJ, *numidJ; + for (int idx = 0; idx < length; idx++) { + json_object *sndCtlJ, *valJ, *numidJ; int numid; - sndCtlJ = json_object_array_get_idx(ctlsJ, idx); + // 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; @@ -240,14 +299,16 @@ STATIC json_object *HalGetPrepareResponse(afb_req request, struct json_object *c numid= (halCtlsEnumT) json_object_get_int(numidJ); for (int idx = 0; halCtls[idx].ctl.numid; idx++) { - if (halCtls[idx].ctl.numid == numid) { - + if (halCtls[idx].ctl.numid == numid) { // translate low level numid to HAL one and normalise values - struct json_object *halCtlJ = json_object_new_object(); + 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" , SetGetNormaliseVolumes(ACTION_GET, &halCtls[idx].ctl, valJ)); - json_object_array_add(halResponseJ, halCtlJ); + 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; } } @@ -262,10 +323,55 @@ STATIC json_object *HalGetPrepareResponse(afb_req request, struct json_object *c 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 err, index; - struct json_object *ctlsInJ, *ctlsOutJ, *queryJ, *responseJ; + int index; + alsaHalMapT *halCtls= halSndCard->ctls; + json_object *ctlsInJ, *ctlsOutJ, *responseJ; // get query from request ctlsInJ = afb_req_json(request); @@ -283,7 +389,7 @@ PUBLIC void halGetCtls(afb_req request) { case json_type_array: { for (int idx= 0; idx < json_object_array_length (ctlsInJ); idx++) { - struct json_object *ctlInJ = json_object_array_get_idx (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)); @@ -297,21 +403,14 @@ PUBLIC void halGetCtls(afb_req request) { } // Call now level CTL - queryJ = json_object_new_object(); - json_object_object_add(queryJ, "devid", json_object_new_string (halDevid)); - json_object_object_add(queryJ, "numid", ctlsOutJ); - - err= afb_service_call_sync("alsacore", "getctl", queryJ, &responseJ); - if (err) { + responseJ = halCallAlsaGetCtls (ctlsOutJ); + if (!responseJ) { afb_req_fail_f(request, "subcall:alsacore/getctl", "%s", json_object_get_string(responseJ)); goto OnErrorExit; } - // Let ignore info data if any and keep on response - json_object_object_get_ex (responseJ, "response", &responseJ); - // map back low level response to HAL ctl with normalised values - struct json_object *halResponse = HalGetPrepareResponse(request, responseJ); + json_object *halResponse = HalGetPrepareResponse(request, responseJ); if (!halResponse) goto OnErrorExit; afb_req_success (request, halResponse, NULL); @@ -321,35 +420,9 @@ OnErrorExit: return; }; -// This receive all event this binding subscribe to -PUBLIC void halServiceEvent(const char *evtname, struct json_object *eventJ) { - int numid; - struct json_object *numidJ, *valuesJ, *valJ; - - AFB_NOTICE("halServiceEvent evtname=%s [msg=%s]", evtname, json_object_get_string(eventJ)); - - json_object_object_get_ex (eventJ, "val" , &valuesJ); - if (!valuesJ) { - AFB_ERROR("halServiceEvent novalues: evtname=%s [msg=%s]", evtname, json_object_get_string(eventJ)); - return; - } - - json_object_object_get_ex (valuesJ, "numid" , &numidJ); - numid = json_object_get_int (numidJ); - - // 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) { - json_object_object_get_ex (valuesJ, "val" , &valJ); - halCtls[idx].cb.callback (&halCtls[idx].ctl, halCtls[idx].cb.handle, valJ); - } - } - } -} -STATIC int UpdateOneSndCtl (alsaHalCtlMapT *ctl, struct json_object *sndCtlJ) { - struct json_object *tmpJ, *ctlJ; +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); @@ -377,7 +450,7 @@ STATIC int UpdateOneSndCtl (alsaHalCtlMapT *ctl, struct json_object *sndCtlJ) { // process dbscale TLV if any if (json_object_object_get_ex (sndCtlJ, "tlv" , &tmpJ)) { - struct json_object *dbscaleJ; + 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); @@ -407,8 +480,12 @@ STATIC int UpdateOneSndCtl (alsaHalCtlMapT *ctl, struct json_object *sndCtlJ) { // this is call when after all bindings are loaded PUBLIC int halServiceInit (const char *apiPrefix, alsaHalSndCardT *alsaHalSndCard) { int err; - struct json_object *queryurl, *responseJ, *devidJ, *ctlsJ, *tmpJ; - halCtls = alsaHalSndCard->ctls; // Get sndcard specific HAL control mapping + 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) { @@ -435,12 +512,12 @@ PUBLIC int halServiceInit (const char *apiPrefix, alsaHalSndCardT *alsaHalSndCar } // save devid for future use - halDevid = strdup (json_object_get_string(devidJ)); + 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++) { - struct json_object *ctlJ; + json_object *ctlJ; // map HAL ctlTad with halCrlLabel to simplify set/get ctl operations using Labels halCtls[idx].label = halCtlsLabels[halCtls[idx].tag]; @@ -452,10 +529,29 @@ PUBLIC int halServiceInit (const char *apiPrefix, alsaHalSndCardT *alsaHalSndCar 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); } else { ctlJ = json_object_new_object(); - if (halCtls[idx].ctl.numid) json_object_object_add(ctlJ, "numid" , json_object_new_int(halCtls[idx].ctl.numid)); + 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)); json_object_array_add(ctlsJ, ctlJ); } @@ -508,13 +604,40 @@ PUBLIC int halServiceInit (const char *apiPrefix, alsaHalSndCardT *alsaHalSndCar 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}, - { .verb = "ctl-list", .callback = halListCtls}, - { .verb = "ctl-get", .callback = halGetCtls}, - { .verb = "ctl-set", .callback = halSetCtls}, - { .verb = "evt-sub", .callback = halSubscribe}, + { .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 */ }; diff --git a/HAL-afb/HAL-interface/hal-interface.h b/HAL-afb/HAL-interface/hal-interface.h index c31c145..23944ad 100644 --- a/HAL-afb/HAL-interface/hal-interface.h +++ b/HAL-afb/HAL-interface/hal-interface.h @@ -22,12 +22,26 @@ #include <alsa/asoundlib.h> #include "audio-interface.h" +#include <systemd/sd-event.h> + typedef enum { - ACTION_SET, - ACTION_GET + ACTION_SET, + ACTION_GET } ActionSetGetT; +// VolRamp Handle Store current status for a given VolRam CB set +typedef struct { + halRampEnumT mode; + halCtlsEnumT slave; + int delay; // delay between volset in us + int stepDown; // linear % + int stepUp; // linear % + int current; // current volume for slave ctl + int target; // target volume + sd_event_source *evtsrc; // event loop timer source +} halVolRampT; + typedef struct { int min; int max; @@ -42,7 +56,9 @@ typedef struct { int count; int minval; int maxval; + int value; int step; + char **enums; alsaHalDBscaleT *dbscale; } alsaHalCtlMapT; @@ -50,8 +66,8 @@ typedef struct { typedef struct afb_service alsaHalServiceT; typedef struct { - struct json_object* (*callback)(alsaHalCtlMapT *control, void* handle, struct json_object *valuesJ); - void* handle; + void (*callback)(halCtlsEnumT tag, alsaHalCtlMapT *control, void* handle, json_object *valuesJ); + void* handle; } alsaHalCbMapT; typedef struct { @@ -62,19 +78,28 @@ typedef struct { char* info; } alsaHalMapT; - -typedef const struct { - char *name; +typedef struct { + const char *name; const char *info; - alsaHalMapT *ctls; + alsaHalMapT *ctls; + const char *devid; + json_object* (*volumeCB)(ActionSetGetT action, const alsaHalCtlMapT *halCtls, json_object *valuesJ); } alsaHalSndCardT; +// hal-interface.c extern afb_verb_v2 halServiceApi[]; -PUBLIC void halServiceEvent(const char *evtname, struct json_object *object); +extern char *halVolRampModes[]; +PUBLIC void halServiceEvent(const char *evtname, json_object *object); PUBLIC int halServiceInit (const char *apiPrefix, alsaHalSndCardT *alsaHalSndCard); +PUBLIC json_object *halGetCtlByTag (halRampEnumT tag); +PUBLIC int halSetCtlByTag (halRampEnumT tag, int value); + + +// hal-volramp.c +PUBLIC void volumeRamp (halCtlsEnumT halTag,alsaHalCtlMapT *control, void* handle, json_object *valJ); -// hal-volmap.c -PUBLIC struct json_object *SetGetNormaliseVolumes(ActionSetGetT action, const alsaHalCtlMapT *halCtls, struct json_object *valuesJ); +// hal-volume.c +PUBLIC json_object *volumeNormalise(ActionSetGetT action, const alsaHalCtlMapT *halCtls, json_object *valuesJ); #endif /* SHAREHALLIB_H */ diff --git a/HAL-afb/HAL-interface/hal-volramp.c b/HAL-afb/HAL-interface/hal-volramp.c new file mode 100644 index 0000000..6343a06 --- /dev/null +++ b/HAL-afb/HAL-interface/hal-volramp.c @@ -0,0 +1,166 @@ +/* + * 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. + * + */ + +#define _GNU_SOURCE // needed for vasprintf +#include "hal-interface.h" + +STATIC int RampTimerCB (sd_event_source* source,uint64_t timer, void* handle) { + halVolRampT *volRamp= (halVolRampT*)handle; + int err; + uint64_t usec; + + // RampDown + if (volRamp->current > volRamp->target) { + volRamp->current = volRamp->current - volRamp->stepDown; + if (volRamp->current < volRamp->target) volRamp->current = volRamp->target; + } + + // RampUp + if (volRamp->current < volRamp->target) { + volRamp->current = volRamp->current + volRamp->stepUp; + if (volRamp->current > volRamp->target) volRamp->current = volRamp->target; + } + + // request current Volume Level + err = halSetCtlByTag(volRamp->slave, volRamp->current); + if (err) goto OnErrorExit; + + // we reach target stop volram event + if (volRamp->current == volRamp->target) sd_event_source_unref(source); + else { + // otherwise validate timer for a new run + sd_event_now(afb_daemon_get_event_loop(), CLOCK_MONOTONIC, &usec); + sd_event_source_set_enabled(source, SD_EVENT_ONESHOT); + err=sd_event_source_set_time(source, usec+volRamp->delay); + } + + return 0; + + OnErrorExit: + AFB_WARNING ("RampTimerCB Fail to set HAL ctl tag=%d vol=%d", Master_Playback_Volume, volRamp->current); + sd_event_source_unref(source); // abandon VolRamp + return -1; +} + +/* UCS2 Interface Timer Callback */ +STATIC void SetRampTimer(void *handle) { + halVolRampT *volRamp= (halVolRampT*)handle; + + uint64_t usec; + /* set a timer with 250ms accuracy */ + sd_event_now(afb_daemon_get_event_loop(), CLOCK_MONOTONIC, &usec); + sd_event_add_time(afb_daemon_get_event_loop(), &volRamp->evtsrc, CLOCK_MONOTONIC, usec, 250, RampTimerCB, volRamp); +} + +STATIC int volumeDoRamp (halVolRampT *volRamp, int numid, json_object *volumeJ) { + json_object *responseJ; + + // request current Volume Level + responseJ = halGetCtlByTag(volRamp->slave); + if (!responseJ) { + AFB_WARNING ("volumeDoRamp Fail to get HAL ctl tag=%d", Master_Playback_Volume); + goto OnErrorExit; + } + + // use 1st volume value as target for ramping + switch (json_object_get_type(volumeJ)) { + case json_type_array: + volRamp->target=json_object_get_int(json_object_array_get_idx(volumeJ,0)); + break; + + case json_type_int: + volRamp->target=json_object_get_int(volumeJ); + break; + + default: + AFB_WARNING ("volumeDoRamp Invalid volumeJ=%s", json_object_get_string(volumeJ)); + goto OnErrorExit; + } + + // use 1st volume value as current for ramping + switch (json_object_get_type(responseJ)) { + case json_type_array: + volRamp->current=json_object_get_int(json_object_array_get_idx(responseJ,0)); + break; + + case json_type_int: + volRamp->current=json_object_get_int(responseJ); + break; + + default: + AFB_WARNING ("volumeDoRamp Invalid reponseJ=%s", json_object_get_string(responseJ)); + goto OnErrorExit; + } + + SetRampTimer (volRamp); + + return 0; + + OnErrorExit: + return -1; +} + + +PUBLIC void volumeRamp (halCtlsEnumT halTag, alsaHalCtlMapT *ctl, void* handle, json_object *valJ) { + halVolRampT *volRamp = (halVolRampT*) handle; + json_object *tmpJ; + + if (json_object_get_type(valJ) != json_type_array || volRamp==NULL) goto OnErrorExit; + + switch (halTag) { + case Vol_Ramp: + tmpJ = json_object_array_get_idx(valJ, 0); + volumeDoRamp (volRamp, ctl->numid, tmpJ); + break; + + case Vol_Ramp_Set_Mode: + tmpJ = json_object_array_get_idx(valJ, 0); + volRamp->mode = json_object_get_int (tmpJ); + break; + + case Vol_Ramp_Set_Slave: + tmpJ = json_object_array_get_idx(valJ, 0); + volRamp->slave = json_object_get_int (tmpJ); + break; + + case Vol_Ramp_Set_Delay: + tmpJ = json_object_array_get_idx(valJ, 0); + volRamp->delay = 1000*json_object_get_int (tmpJ); + break; + + case Vol_Ramp_Set_Down: + tmpJ = json_object_array_get_idx(valJ, 0); + volRamp->stepDown = json_object_get_int (tmpJ); + break; + + case Vol_Ramp_Set_Up: + tmpJ = json_object_array_get_idx(valJ, 0); + volRamp->stepUp = json_object_get_int (tmpJ); + break; + + default: + goto OnErrorExit; + } + + return; + + OnErrorExit: + AFB_WARNING ("Invalid Ctrl Event halCtlsEnumT=%d numid=%d name=%s value=%s", halTag, ctl->numid, ctl->name, json_object_get_string(valJ)); + return; +} +
\ No newline at end of file diff --git a/HAL-afb/HAL-interface/hal-volmap.c b/HAL-afb/HAL-interface/hal-volume.c index 2b619fd..593b74c 100644 --- a/HAL-afb/HAL-interface/hal-volmap.c +++ b/HAL-afb/HAL-interface/hal-volume.c @@ -16,6 +16,9 @@ * * references: * alsa-util/amixer.c + alsa-lib/simple.c + * snd_tlv_convert_from_dB + * nt snd_tlv_convert_to_dB + * snd_tlv_get_dB_range */ #define _GNU_SOURCE // needed for vasprintf @@ -58,7 +61,7 @@ STATIC int dbNormalizeVal (enumRandeModeDB_T normaliseMode, const alsaHalDBscale } // HAL normalise volume values to 0-100% -PUBLIC struct json_object *SetGetNormaliseVolumes(ActionSetGetT action, const alsaHalCtlMapT *halCtls, struct json_object *valuesJ) { +PUBLIC json_object *volumeNormalise(ActionSetGetT action, const alsaHalCtlMapT *halCtls, json_object *valuesJ) { enumRandeModeDB_T useNormalizeDB; int length; @@ -131,7 +134,7 @@ PUBLIC struct json_object *SetGetNormaliseVolumes(ActionSetGetT action, const al break; default: - AFB_NOTICE ("SetGetNormaliseVolumes: invalid action value=%d", (int)action); + AFB_NOTICE ("volumeNormalise: invalid action value=%d", (int)action); goto ExitOnError; } } diff --git a/HAL-afb/HAL-plugin/HalPlugCtl.c b/HAL-afb/HAL-plugin/HalPlugCtl.c index 3755876..4f76595 100644 --- a/HAL-afb/HAL-plugin/HalPlugCtl.c +++ b/HAL-afb/HAL-plugin/HalPlugCtl.c @@ -321,7 +321,7 @@ SND_CTL_PLUGIN_DEFINE_FUNC(afbhal) { return -EINVAL; } - err=snd_config_search(ctlconfig, "numid", &itemConf); + err=snd_config_search(ctlconfig, "ctl", &itemConf); if (!err) { if (snd_config_get_integer(itemConf, (long*)&plughandle->ctls[plughandle->ctlsCount].ctlNumid) < 0) { SNDERR("Not Integer: ctl:%s numid should be a valid integer", ctlLabel); diff --git a/HAL-afb/HDA-intel/IntelHdaHAL.c b/HAL-afb/HDA-intel/IntelHdaHAL.c index 66f241f..f7aec59 100644 --- a/HAL-afb/HDA-intel/IntelHdaHAL.c +++ b/HAL-afb/HDA-intel/IntelHdaHAL.c @@ -25,26 +25,27 @@ #include "hal-interface.h" #include "audio-interface.h" -STATIC struct json_object* MasterOnOff (alsaHalCtlMapT *control, void* handle, struct json_object *valJ) { - struct json_object *reponseJ; - - AFB_INFO ("Power Set value=%s", json_object_get_string(valJ)); - - reponseJ=json_object_new_object(); - json_object_object_add (reponseJ, "Callback", json_object_new_string("Hello From HAL")); - - return reponseJ; -} + +// Default Values for MasterVolume Ramping +STATIC halVolRampT volRampMaster= { + .mode = RAMP_VOL_NORMAL, + .slave = Master_Playback_Volume, + .delay = 100*1000, // ramping delay in us + .stepDown=1, + .stepUp =1, +}; // Map HAL hight sndctl with Alsa numid and optionally with a custom callback for non Alsa supported functionalities. STATIC alsaHalMapT alsaHalMap[]= { - { .tag=Master_Playback_Volume, .ctl={.numid=04 } }, - { .tag=PCM_Playback_Volume , .ctl={.numid=06 } }, - { .tag=PCM_Playback_Switch , .ctl={.numid=05 } }, - { .tag=Capture_Volume , .ctl={.numid=12 } }, - { .tag=Master_OnOff_Switch , .ctl={.numid=0, .type=SND_CTL_ELEM_TYPE_BOOLEAN, .count=1, .name="Power-Switch"}, .cb={.callback=MasterOnOff, .handle=NULL}}, - { .tag=Master_Playback_Ramp , .ctl={.numid=0, .type=SND_CTL_ELEM_TYPE_INTEGER, .count=2, .name="Volume-Switch"}, .cb={.callback=MasterOnOff, .handle=NULL}}, + { .tag=Master_Playback_Volume, . ctl={ .name="Master Playback Volume" } }, + { .tag=PCM_Playback_Volume , .ctl={ .name="PCM Playback Volume" } }, + { .tag=PCM_Playback_Switch , .ctl={ .name="Master Playback Switch" } }, + { .tag=Capture_Volume , .ctl={ .name="Capture Volume" } }, + { .tag=Vol_Ramp , .cb={.callback=volumeRamp, .handle=&volRampMaster}, .info="ramp volume linearly according to current ramp setting", + .ctl={.numid=0, .type=SND_CTL_ELEM_TYPE_INTEGER, .count=1, .minval=0, .maxval=100, .step=1, .name="Hal-VolRamp"} + }, + { .tag=EndHalCrlTag} /* marker for end of the array */ } ; diff --git a/HAL-afb/Scarlett-Focusrite/ScarlettUsbHAL.c b/HAL-afb/Scarlett-Focusrite/ScarlettUsbHAL.c index 10ef204..dd4499e 100644 --- a/HAL-afb/Scarlett-Focusrite/ScarlettUsbHAL.c +++ b/HAL-afb/Scarlett-Focusrite/ScarlettUsbHAL.c @@ -26,25 +26,41 @@ #include "hal-interface.h" #include "audio-interface.h" -STATIC struct json_object* MasterOnOff (alsaHalCtlMapT *control, void* handle, struct json_object *valJ) { - struct json_object *reponseJ; - - AFB_INFO ("Power Set value=%s", json_object_get_string(valJ)); - - reponseJ=json_object_new_object(); - json_object_object_add (reponseJ, "Callback", json_object_new_string("Hello From HAL")); - - return reponseJ; -} +// Default Values for MasterVolume Ramping +STATIC halVolRampT volRampMaster= { + .mode = RAMP_VOL_NORMAL, + .slave = Master_Playback_Volume, + .delay = 100*1000, // ramping delay in us + .stepDown=1, + .stepUp =1, +}; // Map HAL hight sndctl with Alsa numid and optionally with a custom callback for non Alsa supported functionalities. STATIC alsaHalMapT alsaHalMap[]= { - { .tag=Master_Playback_Volume, . ctl={.numid=04 } }, - { .tag=PCM_Playback_Volume , .ctl={.numid=06 } }, + { .tag=Master_Playback_Volume, . ctl={.name="Master Playback Volume" } }, + { .tag=PCM_Playback_Volume , .ctl={.name="Master 1 (Monitor) Playback Volume" } }, { .tag=PCM_Playback_Switch , .ctl={.numid=05 } }, { .tag=Capture_Volume , .ctl={.numid=12 } }, - { .tag=Master_OnOff_Switch , .ctl={.numid=0, .type=SND_CTL_ELEM_TYPE_BOOLEAN, .count=1, .name="AGL-Power-Switch"}, .cb={.callback=MasterOnOff, .handle=NULL}}, - { .tag=Master_Playback_Ramp , .ctl={.numid=0, .type=SND_CTL_ELEM_TYPE_INTEGER, .count=2, .name="AGL-Volume-Switch"}, .cb={.callback=MasterOnOff, .handle=NULL}}, + + { .tag=Vol_Ramp_Set_Mode , .cb={.callback=volumeRamp, .handle=&volRampMaster}, .info="select volramp speed", + .ctl={.numid=0, .type=SND_CTL_ELEM_TYPE_ENUMERATED, .count=1, .value=RAMP_VOL_NORMAL, .name="Hal-VolRamp-Mode", .enums=halVolRampModes} + }, + { .tag=Vol_Ramp_Set_Slave , .cb={.callback=volumeRamp, .handle=&volRampMaster}, .info="set slave volume master numid", + .ctl={.numid=0, .type=SND_CTL_ELEM_TYPE_INTEGER, .count=1, .value=Master_Playback_Volume, .name="Hal-VolRamp-Slave", .enums=halVolRampModes} + }, + { .tag=Vol_Ramp_Set_Delay , .cb={.callback=volumeRamp, .handle=&volRampMaster}, .info="set ramp delay [default 250ms]", + .ctl={.numid=0, .type=SND_CTL_ELEM_TYPE_INTEGER, .count=1, .minval=0, .maxval=1000, .step=100, .value=250, .name="Hal-VolRamp-Step-Down"} + }, + { .tag=Vol_Ramp_Set_Down , .cb={.callback=volumeRamp, .handle=&volRampMaster}, .info="set linear step down ramp [default 10]", + .ctl={.numid=0, .type=SND_CTL_ELEM_TYPE_INTEGER, .count=1, .minval=0, .maxval=100, .step=1, .value=10, .name="Hal-VolRamp-Step-Down"} + }, + { .tag=Vol_Ramp_Set_Up , .cb={.callback=volumeRamp, .handle=&volRampMaster}, .info="set linear step up ramp [default 10]", + .ctl={.numid=0, .type=SND_CTL_ELEM_TYPE_INTEGER, .count=1, .minval=0, .maxval=100, .step=1, .value=10, .name="Hal-VolRamp-Step-Up"} + }, + { .tag=Vol_Ramp , .cb={.callback=volumeRamp, .handle=&volRampMaster}, .info="ramp volume linearly according to current ramp setting", + .ctl={.numid=0, .type=SND_CTL_ELEM_TYPE_INTEGER, .count=1, .minval=0, .maxval=100, .step=1, .name="Hal-VolRamp"} + }, + { .tag=EndHalCrlTag} /* marker for end of the array */ } ; @@ -53,6 +69,7 @@ STATIC alsaHalSndCardT alsaHalSndCard = { .name = "Scarlett 18i8 USB", // WARNING: name MUST match with 'aplay -l' .info = "Hardware Abstraction Layer for Scarlett Focusrite USB professional music sound card", .ctls = alsaHalMap, + .volumeCB = NULL, // use default volume normalisation function }; diff --git a/HAL-afb/Unicens-USB/UnicensHAL.c b/HAL-afb/Unicens-USB/UnicensHAL.c index 66f241f..9a42afd 100644 --- a/HAL-afb/Unicens-USB/UnicensHAL.c +++ b/HAL-afb/Unicens-USB/UnicensHAL.c @@ -25,16 +25,7 @@ #include "hal-interface.h" #include "audio-interface.h" -STATIC struct json_object* MasterOnOff (alsaHalCtlMapT *control, void* handle, struct json_object *valJ) { - struct json_object *reponseJ; - - AFB_INFO ("Power Set value=%s", json_object_get_string(valJ)); - - reponseJ=json_object_new_object(); - json_object_object_add (reponseJ, "Callback", json_object_new_string("Hello From HAL")); - - return reponseJ; -} + // Map HAL hight sndctl with Alsa numid and optionally with a custom callback for non Alsa supported functionalities. STATIC alsaHalMapT alsaHalMap[]= { @@ -42,8 +33,6 @@ STATIC alsaHalMapT alsaHalMap[]= { { .tag=PCM_Playback_Volume , .ctl={.numid=06 } }, { .tag=PCM_Playback_Switch , .ctl={.numid=05 } }, { .tag=Capture_Volume , .ctl={.numid=12 } }, - { .tag=Master_OnOff_Switch , .ctl={.numid=0, .type=SND_CTL_ELEM_TYPE_BOOLEAN, .count=1, .name="Power-Switch"}, .cb={.callback=MasterOnOff, .handle=NULL}}, - { .tag=Master_Playback_Ramp , .ctl={.numid=0, .type=SND_CTL_ELEM_TYPE_INTEGER, .count=2, .name="Volume-Switch"}, .cb={.callback=MasterOnOff, .handle=NULL}}, { .tag=EndHalCrlTag} /* marker for end of the array */ } ; @@ -66,7 +55,7 @@ STATIC int sndServiceInit () { // API prefix should be unique for each snd card PUBLIC const struct afb_binding_v2 afbBindingV2 = { - .api = "intel-hda", + .api = "unicens-usb", .init = sndServiceInit, .verbs = halServiceApi, .onevent = halServiceEvent, diff --git a/HAL-afb/Unicens-USB/UnicensVol.c b/HAL-afb/Unicens-USB/UnicensVol.c index ab9787d..cf4f497 100644 --- a/HAL-afb/Unicens-USB/UnicensVol.c +++ b/HAL-afb/Unicens-USB/UnicensVol.c @@ -25,8 +25,8 @@ #include "hal-interface.h" #include "audio-interface.h" -STATIC struct json_object* MasterOnOff (alsaHalCtlMapT *control, void* handle, struct json_object *valJ) { - struct json_object *reponseJ; +STATIC json_object* MasterOnOff (alsaHalCtlMapT *control, void* handle, json_object *valJ) { + json_object *reponseJ; AFB_INFO ("Power Set value=%s", json_object_get_string(valJ)); |