/* * AlsaLibMapping -- 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: https://github.com/fulup-bzh/AlsaJsonGateway (original code) http://alsa-lib.sourcearchive.com/documentation/1.0.20/modules.html http://alsa-lib.sourcearchive.com/documentation/1.0.8/group__Control_gd48d44da8e3bfe150e928267008b8ff5.html http://alsa.opensrc.org/HowTo_access_a_mixer_control https://github.com/gch1p/alsa-volume-monitor/blob/master/main.c https://github.com/DongheonKim/android_hardware_alsa-sound/blob/master/ALSAControl.cpp (ALSA low level API) https://www.kernel.org/doc/html/v4.11/sound/index.html */ #define _GNU_SOURCE // needed for vasprintf #include "Alsa-ApiHat.h" PUBLIC void NumidsListParse(ActionSetGetT action, queryValuesT *queryValues, ctlRequestT *ctlRequest) { int length; for (int idx = 0; idx < queryValues->count; idx++) { json_object *jId, *valuesJ; ctlRequest[idx].used = 0; ctlRequest[idx].valuesJ = NULL; // when only one NUMID is provided it might not be encapsulated in a JSON array if (json_type_array == json_object_get_type(queryValues->numidsJ)) ctlRequest[idx].jToken = json_object_array_get_idx(queryValues->numidsJ, idx); else ctlRequest[idx].jToken = queryValues->numidsJ; enum json_type jtype = json_object_get_type(ctlRequest[idx].jToken); switch (jtype) { case json_type_int: // if NUMID is not an array then it should be an integer numid with no value ctlRequest[idx].numId = json_object_get_int(ctlRequest[idx].jToken); // Special SET simple short numid form [numid, [VAL1...VALX]] if (action == ACTION_SET && queryValues->count == 2) { ctlRequest[idx].valuesJ = json_object_array_get_idx(queryValues->numidsJ, 1); queryValues->count = 1; //In this form count==2 , when only one numid is to set idx++; continue; } else break; case json_type_array: // NUMID is an array 1st slot should be numid, optionally values may come after length = json_object_array_length(ctlRequest[idx].jToken); // numid must be in 1st slot of numid json array ctlRequest[idx].numId = json_object_get_int(json_object_array_get_idx(ctlRequest[idx].jToken, 0)); if (action == ACTION_GET) continue; // In Write mode second value should be the value if (action == ACTION_SET && length == 2) { ctlRequest[idx].valuesJ = json_object_array_get_idx(ctlRequest[idx].jToken, 1); continue; } // no numid value ctlRequest[idx].used = -1; break; case json_type_object: // numid+values formated as {id:xxx, val:[aa,bb...,nn]} if (!json_object_object_get_ex(ctlRequest[idx].jToken, "id", &jId) || !json_object_object_get_ex(ctlRequest[idx].jToken, "val", &valuesJ)) { AFB_NOTICE("Invalid Json=%s missing 'id'|'val'", json_object_get_string(ctlRequest[idx].jToken)); ctlRequest[idx].used = -1; } else { ctlRequest[idx].numId = json_object_get_int(jId); if (action == ACTION_SET) ctlRequest[idx].valuesJ = valuesJ; } break; default: ctlRequest[idx].used = -1; } } } STATIC json_object *DB2StringJsonOject(long dB) { char label [20]; if (dB < 0) { snprintf(label, sizeof (label), "-%li.%02lidB", -dB / 100, -dB % 100); } else { snprintf(label, sizeof (label), "%li.%02lidB", dB / 100, dB % 100); } // json function takes care of string copy return (json_object_new_string(label)); } // Direct port from amixer TLV decode routine. This code is too complex for me. // I hopefully did not break it when porting it. STATIC json_object *decodeTlv(unsigned int *tlv, unsigned int tlv_size, int mode) { char label[20]; unsigned int type = tlv[0]; unsigned int size; unsigned int idx = 0; const char *chmap_type = NULL; json_object * decodeTlvJson = json_object_new_object(); if (tlv_size < (unsigned int) (2 * sizeof (unsigned int))) { printf("TLV size error!\n"); return NULL; } type = tlv[idx++]; size = tlv[idx++]; tlv_size -= (unsigned int) (2 * sizeof (unsigned int)); if (size > tlv_size) { fprintf(stderr, "TLV size error (%i, %i, %i)!\n", type, size, tlv_size); return NULL; } switch (type) { case SND_CTL_TLVT_CONTAINER: { json_object * containerJson = json_object_new_array(); size += (unsigned int) (sizeof (unsigned int) - 1); size /= (unsigned int) (sizeof (unsigned int)); while (idx < size) { json_object *embedJson; if (tlv[idx + 1] > (size - idx) * sizeof (unsigned int)) { fprintf(stderr, "TLV size error in compound!\n"); return NULL; } embedJson = decodeTlv(tlv + idx, tlv[idx + 1] + 8, mode); json_object_array_add(containerJson, embedJson); idx += (unsigned int) (2 + (tlv[idx + 1] + sizeof (unsigned int) - 1) / sizeof (unsigned int)); } json_object_object_add(decodeTlvJson, "container", containerJson); break; } case SND_CTL_TLVT_DB_SCALE: { json_object * dbscaleJson = json_object_new_object(); if (size != 2 * sizeof (unsigned int)) { json_object * arrayJson = json_object_new_array(); while (size > 0) { if (mode >= QUERY_VERBOSE) { snprintf(label, sizeof (label), "0x%08x,", tlv[idx++]); json_object_array_add(arrayJson, json_object_new_string(label)); } else { json_object_array_add(arrayJson, json_object_new_int(tlv[idx++])); } size -= (unsigned int) sizeof (unsigned int); } json_object_object_add(dbscaleJson, "array", arrayJson); } else { if (mode >= QUERY_VERBOSE) { json_object_object_add(dbscaleJson, "min", DB2StringJsonOject((int) tlv[2])); json_object_object_add(dbscaleJson, "step", DB2StringJsonOject(tlv[3] & 0xffff)); json_object_object_add(dbscaleJson, "mute", DB2StringJsonOject((tlv[3] >> 16) & 1)); } else { json_object_object_add(dbscaleJson, "min", json_object_new_int((int) tlv[2])); json_object_object_add(dbscaleJson, "step", json_object_new_int(tlv[3] & 0xffff)); json_object_object_add(dbscaleJson, "mute", json_object_new_int((tlv[3] >> 16) & 1)); } } json_object_object_add(decodeTlvJson, "dbscale", dbscaleJson); break; } #ifdef SND_CTL_TLVT_DB_LINEAR case SND_CTL_TLVT_DB_LINEAR: { json_object * dbLinearJson = json_object_new_object(); if (size != 2 * sizeof (unsigned int)) { json_object * arrayJson = json_object_new_array(); while (size > 0) { if (mode >= QUERY_VERBOSE) { snprintf(label, sizeof (label), "0x%08x,", tlv[idx++]); json_object_array_add(arrayJson, jso
/*
* 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, something express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Reference:
* Json load using json_unpack https://jansson.readthedocs.io/en/2.9/apiref.html#parsing-and-validating-values
*/
#ifndef _CTL_CONFIG_INCLUDE_
#define _CTL_CONFIG_INCLUDE_
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "ctl-plugin.h"
#include <filescan-utils.h>
#include <wrap-json.h>
#ifdef CONTROL_SUPPORT_LUA
#include "ctl-lua.h"
#endif
#ifndef CONTROL_MAXPATH_LEN
#define CONTROL_MAXPATH_LEN 255
#endif
#ifndef CONTROL_CONFIG_PRE
#define CONTROL_CONFIG_PRE "onload"
#endif
#ifndef CTL_PLUGIN_EXT
#define CTL_PLUGIN_EXT ".ctlso"
#define CTL_SCRIPT_EXT ".lua"
#endif
#define LUA_ACTION_PREFIX "lua://"
#define API_ACTION_PREFIX "api://"
#define PLUGIN_ACTION_PREFIX "plugin://"
typedef struct ConfigSectionS {
const char *key;
const char *uid;
const char *info;
int (*loadCB)(AFB_ApiT apihandle, struct ConfigSectionS *section, json_object *sectionJ);
void *handle;
CtlActionT *actions;
} CtlSectionT;
typedef struct {
const char* api;
const char* uid;
const char *info;
const char *version;
json_object *configJ;
json_object *requireJ;
CtlSectionT *sections;
void *external;
} CtlConfigT;
// This should not be global as application may want to define their own sections
typedef enum {
CTL_SECTION_PLUGIN,
CTL_SECTION_ONLOAD,
CTL_SECTION_CONTROL,
CTL_SECTION_EVENT,
CTL_SECTION_HAL,
CTL_SECTION_ENDTAG,
} SectionEnumT;
// ctl-action.c
CtlActionT *ActionConfig(AFB_ApiT apiHandle, json_object *actionsJ, int exportApi);
void ActionExecUID(AFB_ReqT request, CtlConfigT *ctlConfig, const char *uid, json_object *queryJ);
int ActionExecOne( CtlSourceT *source, CtlActionT* action, json_object *queryJ);
int ActionLoadOne(AFB_ApiT apiHandle, CtlActionT *action, json_object *, int exportApi);
int ActionLabelToIndex(CtlActionT* actions, const char* actionLabel);
// ctl-config.c
int CtlConfigMagicNew();
json_object* CtlConfigScan(const char *dirList, const char *prefix) ;
char* ConfigSearch(AFB_ApiT apiHandle, json_object *responseJ);
char* CtlConfigSearch(AFB_ApiT apiHandle, const char *dirList, const char *prefix) ;
int CtlConfigExec(AFB_ApiT apiHandle, CtlConfigT *ctlConfig) ;
CtlConfigT *CtlLoadMetaData(AFB_ApiT apiHandle,const char* filepath) ;
int CtlLoadSections(AFB_ApiT apiHandle, CtlConfigT *ctlHandle, CtlSectionT *sections);
// ctl-event.c
int EventConfig(AFB_ApiT apihandle, CtlSectionT *section, json_object *actionsJ);
#ifdef AFB_BINDING_PREV3
void CtrlDispatchApiEvent (AFB_ApiT apiHandle, const char *evtLabel, struct json_object *eventJ);
#else
void CtrlDispatchV2Event(const char *evtLabel, json_object *eventJ);
#endif
// ctl-control.c
int ControlConfig(AFB_ApiT apiHandle, CtlSectionT *section, json_object *actionsJ);
// ctl-onload.c
int OnloadConfig(AFB_ApiT apiHandle, CtlSectionT *section, json_object *actionsJ);
// ctl-plugin.c
int PluginConfig(AFB_ApiT UNUSED_ARG(apiHandle), CtlSectionT *section, json_object *pluginsJ);
int PluginGetCB (AFB_ApiT apiHandle, CtlActionT *action , json_object *callbackJ);
#ifdef __cplusplus
}
#endif
#endif /* _CTL_CONFIG_INCLUDE_ */