From 3d5816a07c22dd6c655a60000fb0f175d613e484 Mon Sep 17 00:00:00 2001 From: fulup Date: Wed, 5 Jul 2017 22:31:54 +0200 Subject: Added a POC for ALSA/HAL plugin --- HAL-afb/HAL-plugin/CMakeLists.txt | 30 ++- HAL-afb/HAL-plugin/HalPlug.h | 92 +++++++ HAL-afb/HAL-plugin/HalPlugCb.c | 64 +++++ HAL-afb/HAL-plugin/HalPlugCtl.c | 546 ++++++++++++++++++++++++-------------- HAL-afb/HAL-plugin/HalPlugPcm.c | 430 ------------------------------ HAL-afb/HAL-plugin/README.md | 44 +++ 6 files changed, 573 insertions(+), 633 deletions(-) create mode 100644 HAL-afb/HAL-plugin/HalPlug.h create mode 100644 HAL-afb/HAL-plugin/HalPlugCb.c delete mode 100644 HAL-afb/HAL-plugin/HalPlugPcm.c create mode 100644 HAL-afb/HAL-plugin/README.md (limited to 'HAL-afb') diff --git a/HAL-afb/HAL-plugin/CMakeLists.txt b/HAL-afb/HAL-plugin/CMakeLists.txt index 73390b8..5923935 100644 --- a/HAL-afb/HAL-plugin/CMakeLists.txt +++ b/HAL-afb/HAL-plugin/CMakeLists.txt @@ -16,26 +16,40 @@ # limitations under the License. ########################################################################### +# Needed to remove undefined snd_dlsym_start in plugin share object +add_compile_options(-DPIC) # Add target to project dependency list -PROJECT_TARGET_ADD(hal-ctl-plugin) +PROJECT_TARGET_ADD(ctl_afbhal) # Define targets - ADD_LIBRARY(hal-ctl-plugin MODULE HalPlugCtl.c) + ADD_LIBRARY(${TARGET_NAME} MODULE HalPlugCtl.c) # Alsa Plugin properties - SET_TARGET_PROPERTIES(hal-ctl-plugin PROPERTIES - PREFIX "alsa-" + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + PREFIX "libasound_module_" OUTPUT_NAME ${TARGET_NAME} ) -PROJECT_TARGET_ADD(hal-pcm-plugin) + # Library dependencies (include updates automatically) + TARGET_LINK_LIBRARIES(${TARGET_NAME} + ${link_libraries} + ) + install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib/alsa-lib) + +PROJECT_TARGET_ADD(cb_sample) # Define targets - ADD_LIBRARY(hal-pcm-plugin MODULE HalPlugPcm.c) + ADD_LIBRARY(${TARGET_NAME} MODULE HalPlugCb.c) # Alsa Plugin properties - SET_TARGET_PROPERTIES(hal-pcm-plugin PROPERTIES - PREFIX "alsa-" + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + PREFIX "afbhal_" OUTPUT_NAME ${TARGET_NAME} ) + + # Library dependencies (include updates automatically) + TARGET_LINK_LIBRARIES(${TARGET_NAME} + ${link_libraries} + ) + install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib) diff --git a/HAL-afb/HAL-plugin/HalPlug.h b/HAL-afb/HAL-plugin/HalPlug.h new file mode 100644 index 0000000..5e3da6f --- /dev/null +++ b/HAL-afb/HAL-plugin/HalPlug.h @@ -0,0 +1,92 @@ +/* + * 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. + * + * AfbCallBack (snd_ctl_hal_t *handle, int numid, void **response); + * AfbHalInit is mandatory and called with numid=0 + * + * Syntaxe in .asoundrc file + * CrlLabel { cb MyFunctionName name "My_Second_Control" } + */ + + +#include +#include +#include + +#ifndef SOUND_HAL_MAX_CTLS +#define SOUND_HAL_MAX_CTLS 255 +#endif + + +typedef enum { + CTLCB_INIT , + CTLCB_CLOSE , + CTLCB_ELEM_COUNT , + CTLCB_ELEM_LIST , + CTLCB_FIND_ELEM , + CTLCB_FREE_KEY , + CTLCB_GET_ATTRIBUTE , + CTLCB_GET_INTEGER_INFO , + CTLCB_GET_INTEGER64_INFO , + CTLCB_GET_ENUMERATED_INFO , + CTLCB_GET_ENUMERATED_NAME , + CTLCB_READ_INTEGER , + CTLCB_READ_INTEGER64 , + CTLCB_READ_ENUMERATED , + CTLCB_READ_BYTES , + CTLCB_READ_IEC958 , + CTLCB_WRITE_INTEGER , + CTLCB_WRITE_INTEGER64 , + CTLCB_WRITE_ENUMERATED , + CTLCB_WRITE_BYTES , + CTLCB_WRITE_IEC958 , + CTLCB_SUBSCRIBE_EVENTS , + CTLCB_READ_EVENT , + CTLCB_POLL_DESCRIPTORS_COUNT , + CTLCB_POLL_DESCRIPTORS +} snd_ctl_action_t; + +typedef struct { + int ctlNumid; + const char *ctlName; +} snd_ctl_conf_t; + +typedef struct { + int type; + int acc; + unsigned count; +} snd_ctl_get_attrib_t; + +typedef struct { + int imin; + int imax; + int istep; +} snd_ctl_get_int_info_t; + +typedef int(*snd_ctl_cb_t)(void *handle, snd_ctl_action_t action, snd_ctl_ext_key_t key, void *response); + +typedef struct snd_ctl_hal { + snd_ctl_ext_t ext; + char *devid; + snd_ctl_t *ctlDev; + unsigned int ctlsCount; + void *dlHandle; + snd_ctl_conf_t ctls[SOUND_HAL_MAX_CTLS]; + snd_ctl_elem_info_t *infos[SOUND_HAL_MAX_CTLS]; + snd_ctl_cb_t cbs[SOUND_HAL_MAX_CTLS]; +} snd_ctl_hal_t; + + diff --git a/HAL-afb/HAL-plugin/HalPlugCb.c b/HAL-afb/HAL-plugin/HalPlugCb.c new file mode 100644 index 0000000..362858d --- /dev/null +++ b/HAL-afb/HAL-plugin/HalPlugCb.c @@ -0,0 +1,64 @@ +/* + * 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. + * + * AfbCallBack (snd_ctl_hal_t *handle, int numid, void **response); + * AfbHalInit is mandatory and called with numid=0 + * + * Syntax in .asoundrc file + * CrlLabel { cb MyFunctionName name "My_Second_Control" } + * + * Testing: + * amixer -Dagl_hal controls + * amixer -Dagl_hal cget name=My_Sample_Callback + */ + + +#include "HalPlug.h" +#include + +int AfbHalInitCB (void *handle, snd_ctl_action_t action, int numid, void*response) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*)handle; + + fprintf (stdout, "\n - CB AfbHalInit devid=%s\n", plughandle->devid); + return 0; +} + +int AfbHalSampleCB (void *handle, snd_ctl_action_t action, snd_ctl_ext_key_t key, void*response) { + + switch (action) { + case CTLCB_GET_ATTRIBUTE: { + fprintf (stdout, " - AfbHalSampleCB CTLCB_GET_ATTRIBUTE numid=%d\n", (int)key+1); + snd_ctl_get_attrib_t *item = (snd_ctl_get_attrib_t*)response; + item->type = 1; + item->count= 2; + item->acc = SND_CTL_EXT_ACCESS_READWRITE; + break; + } + + case CTLCB_GET_INTEGER_INFO: { + fprintf (stdout, " - AfbHalSampleCB CTLCB_GET_INTEGER_INFO numid=%d\n", (int)key+1); + snd_ctl_get_int_info_t *item = (snd_ctl_get_attrib_t*)response; + item->istep= 10; + item->imin = 20; + item->imax = 200; + break; + } + + default: + fprintf (stdout, "CB AfbHalSampleCB unsupported action=%d numid=%d\n", action, (int)key+1); + } + return 0; +} diff --git a/HAL-afb/HAL-plugin/HalPlugCtl.c b/HAL-afb/HAL-plugin/HalPlugCtl.c index 21e7cfc..01feedd 100644 --- a/HAL-afb/HAL-plugin/HalPlugCtl.c +++ b/HAL-afb/HAL-plugin/HalPlugCtl.c @@ -14,188 +14,202 @@ * See the License for the specific language governing permissions and * limitations under the License. * + * Testing: + * 1) Copy generated plugin [libasound_module_pcm_afbhal.so] in alsa-lib/ dir visible from LD_LIBRARY_PATH (eg: /usr/lib64/alsa-lib) + * 2) Create a ~/.asounrc file base on following template + * ctl.agl_hal { + * type afbhal + * devid "hw:4" + * cblib "afbhal_cb_sample.so" + * ctls { + * # ctlLabel {numid integer name "Alsa Ctl Name"} + * MasterSwitch { numid 4 name "My_First_Control" } + * MasterVol { numid 5 name "My_Second_Control" } + * CB_sample { ctlcb @AfbHalSampleCB name "My_Sample_Callback"} + * } + * pcm.agl_hal { + * type copy # Copy PCM + * slave "hw:4" # Slave name + * } + * + * } + * 3) Test with + * - amixer -Dagl_hal controls # should list all your controls + * - amixer -Dagl_hal cget numid=1 + * - amixer -Dagl_hal cset numid=1 '10,20' */ #include #include -#include -#include -#include - -typedef struct afbHalPlug { - snd_ctl_ext_t ext; - char *device; - unsigned int num_vol_ctls; - unsigned int num_rec_items; - unsigned int vol_ctl[SOUND_MIXER_NRDEVICES]; - unsigned int rec_item[SOUND_MIXER_NRDEVICES]; -} afbHalPlug_t; - -static const char *const vol_devices[SOUND_MIXER_NRDEVICES] = { - [SOUND_MIXER_VOLUME] = "Master Playback Volume", - [SOUND_MIXER_BASS] = "Tone Control - Bass", - [SOUND_MIXER_TREBLE] = "Tone Control - Treble", - [SOUND_MIXER_SYNTH] = "Synth Playback Volume", - [SOUND_MIXER_PCM] = "PCM Playback Volume", - [SOUND_MIXER_SPEAKER] = "PC Speaker Playback Volume", - [SOUND_MIXER_LINE] = "Line Playback Volume", - [SOUND_MIXER_MIC] = "Mic Playback Volume", - [SOUND_MIXER_CD] = "CD Playback Volume", - [SOUND_MIXER_IMIX] = "Monitor Mix Playback Volume", - [SOUND_MIXER_ALTPCM] = "Headphone Playback Volume", - [SOUND_MIXER_RECLEV] = "Capture Volume", - [SOUND_MIXER_IGAIN] = "Capture Volume", - [SOUND_MIXER_OGAIN] = "Playback Volume", - [SOUND_MIXER_LINE1] = "Aux Playback Volume", - [SOUND_MIXER_LINE2] = "Aux1 Playback Volume", - [SOUND_MIXER_LINE3] = "Line1 Playback Volume", - [SOUND_MIXER_DIGITAL1] = "IEC958 Playback Volume", - [SOUND_MIXER_DIGITAL2] = "Digital Playback Volume", - [SOUND_MIXER_DIGITAL3] = "Digital1 Playback Volume", - [SOUND_MIXER_PHONEIN] = "Phone Playback Volume", - [SOUND_MIXER_PHONEOUT] = "Master Mono Playback Volume", - [SOUND_MIXER_VIDEO] = "Video Playback Volume", - [SOUND_MIXER_RADIO] = "Radio Playback Volume", - [SOUND_MIXER_MONITOR] = "Monitor Playback Volume", -}; - -static const char *const rec_devices[SOUND_MIXER_NRDEVICES] = { - [SOUND_MIXER_VOLUME] = "Mix Capture Switch", - [SOUND_MIXER_SYNTH] = "Synth Capture Switch", - [SOUND_MIXER_PCM] = "PCM Capture Switch", - [SOUND_MIXER_LINE] = "Line Capture Switch", - [SOUND_MIXER_MIC] = "Mic Capture Switch", - [SOUND_MIXER_CD] = "CD Capture Switch", - [SOUND_MIXER_LINE1] = "Aux Capture Switch", - [SOUND_MIXER_LINE2] = "Aux1 Capture Switch", - [SOUND_MIXER_LINE3] = "Line1 Capture Switch", - [SOUND_MIXER_DIGITAL1] = "IEC958 Capture Switch", - [SOUND_MIXER_DIGITAL2] = "Digital Capture Switch", - [SOUND_MIXER_DIGITAL3] = "Digital1 Capture Switch", - [SOUND_MIXER_PHONEIN] = "Phone Capture Switch", - [SOUND_MIXER_VIDEO] = "Video Capture Switch", - [SOUND_MIXER_RADIO] = "Radio Capture Switch", -}; - -static const char *const rec_items[SOUND_MIXER_NRDEVICES] = { - [SOUND_MIXER_VOLUME] = "Mix", - [SOUND_MIXER_SYNTH] = "Synth", - [SOUND_MIXER_PCM] = "PCM", - [SOUND_MIXER_LINE] = "Line", - [SOUND_MIXER_MIC] = "Mic", - [SOUND_MIXER_CD] = "CD", - [SOUND_MIXER_LINE1] = "Aux", - [SOUND_MIXER_LINE2] = "Aux1", - [SOUND_MIXER_LINE3] = "Line1", - [SOUND_MIXER_DIGITAL1] = "IEC958", - [SOUND_MIXER_DIGITAL2] = "Digital", - [SOUND_MIXER_DIGITAL3] = "Digital1", - [SOUND_MIXER_PHONEIN] = "Phone", - [SOUND_MIXER_VIDEO] = "Video", - [SOUND_MIXER_RADIO] = "Radio", -}; - - - - -static snd_ctl_ext_key_t AfbHalElemFind(snd_ctl_ext_t *ext, - const snd_ctl_elem_id_t *id) { - - snd_ctl_hal_t *ctlHal = ext->private_data; - const char *name; - unsigned int i, key, numid; - - numid = snd_ctl_elem_id_get_numid(id); - if (numid > 0) { - numid--; - if (numid < afbHalPlug->num_vol_ctls) - return afbHalPlug->vol_ctl[numid]; - numid -= afbHalPlug->num_vol_ctls; - if (afbHalPlug->exclusive_input) { - if (!numid) - return OSS_KEY_CAPTURE_MUX; - } else if (numid < afbHalPlug->num_rec_items) - return afbHalPlug->rec_item[numid] | - OSS_KEY_CAPTURE_FLAG; - } - - name = snd_ctl_elem_id_get_name(id); - if (! strcmp(name, "Capture Source")) { - if (afbHalPlug->exclusive_input) - return OSS_KEY_CAPTURE_MUX; - else - return SND_CTL_EXT_KEY_NOT_FOUND; - } - for (i = 0; i < afbHalPlug->num_vol_ctls; i++) { - key = afbHalPlug->vol_ctl[i]; - if (! strcmp(name, vol_devices[key])) - return key; - } - for (i = 0; i < afbHalPlug->num_rec_items; i++) { - key = afbHalPlug->rec_item[i]; - if (! strcmp(name, rec_devices[key])) - return key | OSS_KEY_CAPTURE_FLAG; - } - return SND_CTL_EXT_KEY_NOT_FOUND; +#include "HalPlug.h" +#include + + +static snd_ctl_ext_key_t AfbHalElemFind(snd_ctl_ext_t *ext, const snd_ctl_elem_id_t *id) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + snd_ctl_ext_key_t key; + + int numid = snd_ctl_elem_id_get_numid(id); + if (numid > 0) { + if (numid > plughandle->ctlsCount) goto OnErrorExit; + key= (snd_ctl_ext_key_t) numid -1; + goto SucessExit; + } + + const char *ctlName= snd_ctl_elem_id_get_name(id); + if (ctlName == NULL) goto OnErrorExit; + + for (int idx=0; idx < plughandle->ctlsCount; idx++) { + if (! strcmp(ctlName, plughandle->ctls[idx].ctlName)) { + key = idx; + goto SucessExit; + } + } + + SucessExit: + return key; + + OnErrorExit: + return -1; } -static int AfbHalGetAttrib(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, - int *type, unsigned int *acc, unsigned int *count) { - return 0; +static int AfbHalGetAttrib(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, int *type, unsigned int *acc, unsigned int *count) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + snd_ctl_elem_info_t *elemInfo = plughandle->infos[key]; + snd_ctl_cb_t callback = plughandle->cbs[key]; + + + // search for equivalent NUMID in effective sound card + if (elemInfo) { + *type = snd_ctl_elem_info_get_type(elemInfo); + *count = snd_ctl_elem_info_get_count(elemInfo); + *acc = SND_CTL_EXT_ACCESS_READWRITE; // Future ToBeDone + return 0; + } + + if (callback) { + snd_ctl_get_attrib_t item; + + int err = callback(plughandle, CTLCB_GET_ATTRIBUTE, key, &item); + if (!err) { + *type = item.type; + *acc = item.acc; + *count= item.count; + } + return err; + } + + return -1; } -static int AfbHalGetInfo(snd_ctl_ext_t *ext ATTRIBUTE_UNUSED, - snd_ctl_ext_key_t key ATTRIBUTE_UNUSED, - long *imin, long *imax, long *istep) { - - return 0; +static int AfbHalGetIntInfo(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *imin, long *imax, long *istep) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + snd_ctl_elem_info_t *elemInfo = plughandle->infos[key]; + snd_ctl_cb_t callback = plughandle->cbs[key]; + + if (elemInfo) { + + // Should be normalised to make everything 0-100% + *imin = (long)snd_ctl_elem_info_get_min(elemInfo); + *imax = (long)snd_ctl_elem_info_get_min(elemInfo); + *istep= (long)snd_ctl_elem_info_get_min(elemInfo); + return 0; + } + + if (callback) { + snd_ctl_get_int_info_t item; + + int err = callback(plughandle, CTLCB_GET_INTEGER_INFO, key, &item); + if (!err) { + *imin = item.imin; + *imax = item.imax; + *istep= item.istep; + } + return err; + } + + return -1; } -static int AfbHalGetEnumInfo(snd_ctl_ext_t *ext, - snd_ctl_ext_key_t key ATTRIBUTE_UNUSED, - unsigned int *items) { - - return 0; +static int AfbHalGetEnumInfo(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int *items) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + snd_ctl_elem_info_t *elemInfo = plughandle->infos[key]; + snd_ctl_cb_t callback = plughandle->cbs[key]; + + if(elemInfo) *items= snd_ctl_elem_info_get_items(elemInfo); + if(callback) callback(plughandle, CTLCB_GET_ENUMERATED_INFO, key, items); + + return 0; } -static int AfbHalGetEnumName(snd_ctl_ext_t *ext, - snd_ctl_ext_key_t key ATTRIBUTE_UNUSED, - unsigned int item, char *name, - size_t name_max_len) { - - return 0; +static int AfbHalGetEnumName(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int item, char *name, size_t name_max_len) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + snd_ctl_elem_info_t *elemInfo = plughandle->infos[key]; + + //name= snd_ctl_elem_info_get_item_name(elemInfo); + return 0; } static int AfbHalReadInt(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + snd_ctl_elem_info_t *elemInfo = plughandle->infos[key]; - return 0; + return 0; } -static int AfbHalReadEnumerate(snd_ctl_ext_t *ext, - snd_ctl_ext_key_t key ATTRIBUTE_UNUSED, - unsigned int *items) { +static int AfbHalReadEnumerate(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int *items) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; - return 0; + return 0; } static int AfbHalWriteInt(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, long *value) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; return 0; } -static int AfbHalWriteEnum(snd_ctl_ext_t *ext, - snd_ctl_ext_key_t key ATTRIBUTE_UNUSED, - unsigned int *items) { +static int AfbHalWriteEnum(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, unsigned int *items) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; return 0; } -static int AfbHalEventRead(snd_ctl_ext_t *ext ATTRIBUTE_UNUSED, - snd_ctl_elem_id_t *id ATTRIBUTE_UNUSED, - unsigned int *event_mask ATTRIBUTE_UNUSED) -{ - return -EAGAIN; +static int AfbHalEventRead(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id, unsigned int *event_mask) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + + return -EAGAIN; +} + +static int AfbHalElemList(snd_ctl_ext_t *ext, unsigned int offset, snd_ctl_elem_id_t *id) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + + snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); + snd_ctl_elem_id_set_name(id, plughandle->ctls[offset].ctlName); + + return 0; +} + +static int AfbHalElemCount(snd_ctl_ext_t *ext) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + int count = plughandle->ctlsCount; + return count; +} + +static void AfbHalClose(snd_ctl_ext_t *ext) { + snd_ctl_hal_t *plughandle = (snd_ctl_hal_t*) ext->private_data; + int err; + + for (int idx=0; idx < plughandle->ctlsCount; idx++) { + if (plughandle->ctls[idx].ctlName) free((void*)plughandle->ctls[idx].ctlName); + } + + err = snd_ctl_close(plughandle->ctlDev); + if (err) SNDERR("Fail Close sndctl: devid=%s err=%s", plughandle->devid, snd_strerror(err)); + + if (plughandle->devid) free(plughandle->devid); + free(plughandle); } static snd_ctl_ext_callback_t afbHalCBs = { @@ -204,7 +218,7 @@ static snd_ctl_ext_callback_t afbHalCBs = { .elem_list = AfbHalElemList, .find_elem = AfbHalElemFind, .get_attribute = AfbHalGetAttrib, - .get_integer_info = AfbHalGetInfo, + .get_integer_info = AfbHalGetIntInfo, .get_enumerated_info = AfbHalGetEnumInfo, .get_enumerated_name = AfbHalGetEnumName, .read_integer = AfbHalReadInt, @@ -214,54 +228,196 @@ static snd_ctl_ext_callback_t afbHalCBs = { .read_event = AfbHalEventRead, }; - -SND_CTL_PLUGIN_DEFINE_FUNC(afb_hal) { +SND_CTL_PLUGIN_DEFINE_FUNC(afbhal) { snd_config_iterator_t it, next; - afbHalPlug_t *afbHalPlug; + snd_ctl_hal_t *plughandle; int err; - - snd_config_for_each(it, next, conf) { - snd_config_t *n = snd_config_iterator_entry(it); - const char *id; - if (snd_config_get_id(n, &id) < 0) - continue; - if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 || strcmp(id, "hint") == 0) - continue; - if (strcmp(id, "slave") == 0) { - if (snd_config_get_string(n, &device) < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } - continue; - } - SNDERR("Unknown field %s", id); - return -EINVAL; - } - - // Create ALSA control plugin structure - afbHalPlug->ext.version = SND_CTL_EXT_VERSION; - afbHalPlug->ext.card_idx = 0; /* FIXME */ - strcpy(afbHalPlug->ext.id, "AFB-HAL-CTL"); - strcpy(afbHalPlug->ext.driver, "AFB-HAL"); - strcpy(afbHalPlug->ext.name, "AFB-HAL Control Plugin"); - strcpy(afbHalPlug->ext.mixername, "AFB-HAL Mixer Plugin"); - strcpy(afbHalPlug->ext.longname, "Automotive-Linux Sound Abstraction Control Plugin"); - afbHalPlug->ext.poll_fd = -1; - afbHalPlug->ext.callback = &afbHalCBs; - afbHalPlug->ext.private_data = afbHalPlug; - - - err = snd_ctl_ext_create(&afbHalPlug->ext, name, mode); - if (err < 0) goto OnErrorExit; - - // Plugin register controls update handlep before exiting - *handlep = afbHalPlug->ext.handle; - return 0; + snd_ctl_cb_t AfbHalInitCB; + const char *libname; + + plughandle = calloc(1, sizeof(snd_ctl_hal_t)); + + snd_config_for_each(it, next, conf) { + snd_config_t *node = snd_config_iterator_entry(it); + const char *id; + + // ignore comment en empty lines + if (snd_config_get_id(node, &id) < 0) continue; + if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 || strcmp(id, "hint") == 0) continue; + + // devid should point onto a valid sound card + if (strcmp(id, "devid") == 0) { + const char *devid; + if (snd_config_get_string(node, &devid) < 0) { + SNDERR("Invalid string type for %s", id); + return -EINVAL; + } + plughandle->devid=strdup(devid); + + // open control interface for devid + err = snd_ctl_open(&plughandle->ctlDev, plughandle->devid, 0); + if (err < 0) { + SNDERR("Fail to open control device for devid=%s", plughandle->devid); + return -EINVAL; + } + continue; + } + + if (strcmp(id, "cblib") == 0) { + if (snd_config_get_string(node, &libname) < 0) { + SNDERR("Invalid libname string for %s", id); + return -EINVAL; + } + + plughandle->dlHandle= dlopen(libname, RTLD_NOW); + if (!plughandle->dlHandle) { + SNDERR("Fail to open callback sharelib=%s error=%s", libname, dlerror()); + return -EINVAL; + } + + AfbHalInitCB = dlsym(plughandle->dlHandle, "AfbHalInitCB"); + if (!AfbHalInitCB) { + SNDERR("Fail find 'AfbHalInitCB' symbol into callbacks sharelib=%s", libname); + return -EINVAL; + } + + err = (*AfbHalInitCB)(plughandle,CTLCB_INIT, 0,0); + if (err) { + SNDERR("Fail AfbHalInitCB err=%d", err); + return -EINVAL; + } + + continue; + } + + if (strcmp(id, "ctls") == 0) { + const char *ctlConf; + snd_config_type_t ctype; + snd_config_iterator_t currentCtl, follow; + snd_config_t *itemConf; + + ctype = snd_config_get_type (node); + if (ctype != SND_CONFIG_TYPE_COMPOUND) { + snd_config_get_string (node, &ctlConf); + SNDERR("Invalid compound type for %s", node); + return -EINVAL; + } + + // loop on each ctl within ctls + snd_config_for_each (currentCtl, follow, node) { + snd_config_t *ctlconfig = snd_config_iterator_entry(currentCtl); + snd_ctl_elem_info_t *elemInfo; + const char *ctlLabel, *ctlName; + + // ignore empty line + if (snd_config_get_id(ctlconfig, &ctlLabel) < 0) continue; + + // each clt should be a valid config compound + ctype = snd_config_get_type (ctlconfig); + if (ctype != SND_CONFIG_TYPE_COMPOUND) { + snd_config_get_string (node, &ctlConf); + SNDERR("Invalid ctl config for %s", ctlLabel); + return -EINVAL; + } + + err=snd_config_search(ctlconfig, "numid", &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); + return -EINVAL; + } + + // Make sure than numid is valid on slave snd card + snd_ctl_elem_info_malloc(&elemInfo); + snd_ctl_elem_info_set_numid(elemInfo, (int)plughandle->ctls[plughandle->ctlsCount].ctlNumid); + plughandle->infos[plughandle->ctlsCount]= elemInfo; + + err = snd_ctl_elem_info(plughandle->ctlDev, elemInfo); + if (err) { + SNDERR("Not Found: 'numid=%d' for 'devid=%s'", plughandle->ctls[plughandle->ctlsCount].ctlNumid, plughandle->devid); + return -EINVAL; + } + } + + err=snd_config_search(ctlconfig, "ctlcb", &itemConf); + if (!err) { + const char *funcname; + void *funcaddr; + + if (snd_config_get_string(itemConf, &funcname) < 0) { + SNDERR("Not string: ctl:%s cbname should be a valid string", ctlLabel); + return -EINVAL; + } + + if (funcname[0] != '@') { + SNDERR("Not string: ctl:%s cbname=%s should be prefixed with '@' ", ctlLabel, funcname); + return -EINVAL; + } + + if (!plughandle->dlHandle) { + SNDERR("No CB: ctl:%s cblib:/my/libcallback missing from asoundrc", ctlLabel); + return -EINVAL; + } + + funcaddr = dlsym(plughandle->dlHandle, &funcname[1]); + if (!funcaddr) { + SNDERR("NotFound CB: ctl:%s cbname='%s' no symbol into %s", ctlLabel, &funcname[1], libname); + return -EINVAL; + } + plughandle->cbs[plughandle->ctlsCount]=funcaddr; + } + + err=snd_config_search(ctlconfig, "name", &itemConf); + if (err) { + SNDERR("Not Found: 'name' mandatory in ctl config"); + return -EINVAL; + } + + if (snd_config_get_string(itemConf, &ctlName) < 0) { + SNDERR("Not String: ctl:%s 'name' should be a valie string", ctlLabel); + return -EINVAL; + } + plughandle->ctls[plughandle->ctlsCount].ctlName = strdup(ctlName); + + // move to next ctl if any + plughandle->ctlsCount++; + } // end for each ctl + continue; + } + SNDERR("Unknown field %s", id); + return -EINVAL; + } + + + + // Create ALSA control plugin structure + plughandle->ext.version = SND_CTL_EXT_VERSION; + plughandle->ext.card_idx = 0; /* FIXME */ + strcpy(plughandle->ext.id , "AFB-HAL-CTL"); + strcpy(plughandle->ext.driver , "AFB-HAL"); + strcpy(plughandle->ext.name , "AFB-HAL Control Plugin"); + strcpy(plughandle->ext.mixername, "AFB-HAL Mixer Plugin"); + strcpy(plughandle->ext.longname , "Automotive-Linux Sound Abstraction Control Plugin"); + plughandle->ext.poll_fd = -1; + plughandle->ext.callback = &afbHalCBs; + plughandle->ext.private_data = (void*)plughandle; + + + + err = snd_ctl_ext_create(&plughandle->ext, name, mode); + if (err < 0) { + SNDERR("Fail Register sndctl for devid=%s", plughandle->devid); + goto OnErrorExit; + } + + // Plugin register controls update handlep before exiting + *handlep = plughandle->ext.handle; + return 0; OnErrorExit: - free(afbHalPlug); + free(plughandle); return -1; } -SND_CTL_PLUGIN_SYMBOL(afb_hal); +SND_CTL_PLUGIN_SYMBOL(afbhal); diff --git a/HAL-afb/HAL-plugin/HalPlugPcm.c b/HAL-afb/HAL-plugin/HalPlugPcm.c deleted file mode 100644 index d43b44c..0000000 --- a/HAL-afb/HAL-plugin/HalPlugPcm.c +++ /dev/null @@ -1,430 +0,0 @@ -/* - * ALSA <-> OSS PCM I/O plugin - * - * Copyright (c) 2005 by Takashi Iwai - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include - -typedef struct snd_pcm_oss { - snd_pcm_ioplug_t io; - char *device; - int fd; - int fragment_set; - int caps; - int format; - unsigned int period_shift; - unsigned int periods; - unsigned int frame_bytes; -} snd_pcm_oss_t; - -static snd_pcm_sframes_t oss_write(snd_pcm_ioplug_t *io, - const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, - snd_pcm_uframes_t size) -{ - snd_pcm_oss_t *oss = io->private_data; - const char *buf; - ssize_t result; - - /* we handle only an interleaved buffer */ - buf = (char *)areas->addr + (areas->first + areas->step * offset) / 8; - size *= oss->frame_bytes; - result = write(oss->fd, buf, size); - if (result <= 0) - return result; - return result / oss->frame_bytes; -} - -static snd_pcm_sframes_t oss_read(snd_pcm_ioplug_t *io, - const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, - snd_pcm_uframes_t size) -{ - snd_pcm_oss_t *oss = io->private_data; - char *buf; - ssize_t result; - - /* we handle only an interleaved buffer */ - buf = (char *)areas->addr + (areas->first + areas->step * offset) / 8; - size *= oss->frame_bytes; - result = read(oss->fd, buf, size); - if (result <= 0) - return result; - return result / oss->frame_bytes; -} - -static snd_pcm_sframes_t oss_pointer(snd_pcm_ioplug_t *io) -{ - snd_pcm_oss_t *oss = io->private_data; - struct count_info info; - int ptr; - - if (ioctl(oss->fd, io->stream == SND_PCM_STREAM_PLAYBACK ? - SNDCTL_DSP_GETOPTR : SNDCTL_DSP_GETIPTR, &info) < 0) { - fprintf(stderr, "*** OSS: oss_pointer error\n"); - return 0; - } - ptr = snd_pcm_bytes_to_frames(io->pcm, info.ptr); - return ptr; -} - -static int oss_start(snd_pcm_ioplug_t *io) -{ - snd_pcm_oss_t *oss = io->private_data; - int tmp = io->stream == SND_PCM_STREAM_PLAYBACK ? - PCM_ENABLE_OUTPUT : PCM_ENABLE_INPUT; - - if (ioctl(oss->fd, SNDCTL_DSP_SETTRIGGER, &tmp) < 0) { - fprintf(stderr, "*** OSS: trigger failed\n"); - if (io->stream == SND_PCM_STREAM_CAPTURE) - /* fake read to trigger */ - read(oss->fd, &tmp, 0); - } - return 0; -} - -static int oss_stop(snd_pcm_ioplug_t *io) -{ - snd_pcm_oss_t *oss = io->private_data; - int tmp = 0; - - ioctl(oss->fd, SNDCTL_DSP_SETTRIGGER, &tmp); - return 0; -} - -static int oss_drain(snd_pcm_ioplug_t *io) -{ - snd_pcm_oss_t *oss = io->private_data; - - if (io->stream == SND_PCM_STREAM_PLAYBACK) - ioctl(oss->fd, SNDCTL_DSP_SYNC); - return 0; -} - -static int oss_prepare(snd_pcm_ioplug_t *io) -{ - snd_pcm_oss_t *oss = io->private_data; - int tmp; - - ioctl(oss->fd, SNDCTL_DSP_RESET); - - tmp = io->channels; - if (ioctl(oss->fd, SNDCTL_DSP_CHANNELS, &tmp) < 0) { - perror("SNDCTL_DSP_CHANNELS"); - return -EINVAL; - } - tmp = oss->format; - if (ioctl(oss->fd, SNDCTL_DSP_SETFMT, &tmp) < 0) { - perror("SNDCTL_DSP_SETFMT"); - return -EINVAL; - } - tmp = io->rate; - if (ioctl(oss->fd, SNDCTL_DSP_SPEED, &tmp) < 0 || - tmp > io->rate * 1.01 || tmp < io->rate * 0.99) { - perror("SNDCTL_DSP_SPEED"); - return -EINVAL; - } - return 0; -} - -static int oss_hw_params(snd_pcm_ioplug_t *io, - snd_pcm_hw_params_t *params ATTRIBUTE_UNUSED) -{ - snd_pcm_oss_t *oss = io->private_data; - int i, tmp, err; - unsigned int period_bytes; - long oflags, flags; - - oss->frame_bytes = (snd_pcm_format_physical_width(io->format) * io->channels) / 8; - switch (io->format) { - case SND_PCM_FORMAT_U8: - oss->format = AFMT_U8; - break; - case SND_PCM_FORMAT_S16_LE: - oss->format = AFMT_S16_LE; - break; - case SND_PCM_FORMAT_S16_BE: - oss->format = AFMT_S16_BE; - break; - default: - fprintf(stderr, "*** OSS: unsupported format %s\n", snd_pcm_format_name(io->format)); - return -EINVAL; - } - period_bytes = io->period_size * oss->frame_bytes; - oss->period_shift = 0; - for (i = 31; i >= 4; i--) { - if (period_bytes & (1U << i)) { - oss->period_shift = i; - break; - } - } - if (! oss->period_shift) { - fprintf(stderr, "*** OSS: invalid period size %d\n", (int)io->period_size); - return -EINVAL; - } - oss->periods = io->buffer_size / io->period_size; - - _retry: - tmp = oss->period_shift | (oss->periods << 16); - if (ioctl(oss->fd, SNDCTL_DSP_SETFRAGMENT, &tmp) < 0) { - if (! oss->fragment_set) { - perror("SNDCTL_DSP_SETFRAGMENT"); - fprintf(stderr, "*** period shift = %d, periods = %d\n", oss->period_shift, oss->periods); - return -EINVAL; - } - /* OSS has no proper way to reinitialize the fragments */ - /* try to reopen the device */ - close(oss->fd); - oss->fd = open(oss->device, io->stream == SND_PCM_STREAM_PLAYBACK ? - O_WRONLY : O_RDONLY); - if (oss->fd < 0) { - err = -errno; - SNDERR("Cannot reopen the device %s", oss->device); - return err; - } - io->poll_fd = oss->fd; - io->poll_events = io->stream == SND_PCM_STREAM_PLAYBACK ? - POLLOUT : POLLIN; - snd_pcm_ioplug_reinit_status(io); - oss->fragment_set = 0; - goto _retry; - } - oss->fragment_set = 1; - - if ((flags = fcntl(oss->fd, F_GETFL)) < 0) { - err = -errno; - perror("F_GETFL"); - } else { - oflags = flags; - if (io->nonblock) - flags |= O_NONBLOCK; - else - flags &= ~O_NONBLOCK; - if (flags != oflags && - fcntl(oss->fd, F_SETFL, flags) < 0) { - err = -errno; - perror("F_SETFL"); - } - } - - return 0; -} - -#define ARRAY_SIZE(ary) (sizeof(ary)/sizeof(ary[0])) - -static int oss_hw_constraint(snd_pcm_oss_t *oss) -{ - snd_pcm_ioplug_t *io = &oss->io; - static const snd_pcm_access_t access_list[] = { - SND_PCM_ACCESS_RW_INTERLEAVED, - SND_PCM_ACCESS_MMAP_INTERLEAVED - }; - unsigned int nformats; - unsigned int format[5]; - unsigned int nchannels; - unsigned int channel[6]; - /* period and buffer bytes must be power of two */ - static const unsigned int bytes_list[] = { - 1U<<8, 1U<<9, 1U<<10, 1U<<11, 1U<<12, 1U<<13, 1U<<14, 1U<<15, - 1U<<16, 1U<<17, 1U<<18, 1U<<19, 1U<<20, 1U<<21, 1U<<22, 1U<<23 - }; - int i, err, tmp; - - /* check trigger */ - oss->caps = 0; - if (ioctl(oss->fd, SNDCTL_DSP_GETCAPS, &oss->caps) >= 0) { - if (! (oss->caps & DSP_CAP_TRIGGER)) - fprintf(stderr, "*** OSS: trigger is not supported!\n"); - } - - /* access type - interleaved only */ - if ((err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, - ARRAY_SIZE(access_list), access_list)) < 0) - return err; - - /* supported formats */ - tmp = 0; - ioctl(oss->fd, SNDCTL_DSP_GETFMTS, &tmp); - nformats = 0; - if (tmp & AFMT_U8) - format[nformats++] = SND_PCM_FORMAT_U8; - if (tmp & AFMT_S16_LE) - format[nformats++] = SND_PCM_FORMAT_S16_LE; - if (tmp & AFMT_S16_BE) - format[nformats++] = SND_PCM_FORMAT_S16_BE; - if (tmp & AFMT_MU_LAW) - format[nformats++] = SND_PCM_FORMAT_MU_LAW; - if (! nformats) - format[nformats++] = SND_PCM_FORMAT_S16; - if ((err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT, - nformats, format)) < 0) - return err; - - /* supported channels */ - nchannels = 0; - for (i = 0; i < 6; i++) { - tmp = i + 1; - if (ioctl(oss->fd, SNDCTL_DSP_CHANNELS, &tmp) >= 0) - channel[nchannels++] = tmp; - } - if (! nchannels) /* assume 2ch stereo */ - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, - 2, 2); - else - err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_CHANNELS, - nchannels, channel); - if (err < 0) - return err; - - /* supported rates */ - /* FIXME: should query? */ - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, 8000, 480000); - if (err < 0) - return err; - - /* period size (in power of two) */ - err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, - ARRAY_SIZE(bytes_list), bytes_list); - if (err < 0) - return err; - /* periods */ - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, 2, 1024); - if (err < 0) - return err; - /* buffer size (in power of two) */ - err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_BUFFER_BYTES, - ARRAY_SIZE(bytes_list), bytes_list); - if (err < 0) - return err; - - return 0; -} - - -static int oss_close(snd_pcm_ioplug_t *io) -{ - snd_pcm_oss_t *oss = io->private_data; - - close(oss->fd); - free(oss->device); - free(oss); - return 0; -} - -static const snd_pcm_ioplug_callback_t oss_playback_callback = { - .start = oss_start, - .stop = oss_stop, - .transfer = oss_write, - .pointer = oss_pointer, - .close = oss_close, - .hw_params = oss_hw_params, - .prepare = oss_prepare, - .drain = oss_drain, -}; - -static const snd_pcm_ioplug_callback_t oss_capture_callback = { - .start = oss_start, - .stop = oss_stop, - .transfer = oss_read, - .pointer = oss_pointer, - .close = oss_close, - .hw_params = oss_hw_params, - .prepare = oss_prepare, - .drain = oss_drain, -}; - - -SND_PCM_PLUGIN_DEFINE_FUNC(oss) -{ - snd_config_iterator_t i, next; - const char *device = "/dev/dsp"; - int err; - snd_pcm_oss_t *oss; - - snd_config_for_each(i, next, conf) { - snd_config_t *n = snd_config_iterator_entry(i); - const char *id; - if (snd_config_get_id(n, &id) < 0) - continue; - if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 || strcmp(id, "hint") == 0) - continue; - if (strcmp(id, "device") == 0) { - if (snd_config_get_string(n, &device) < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } - continue; - } - SNDERR("Unknown field %s", id); - return -EINVAL; - } - - oss = calloc(1, sizeof(*oss)); - if (! oss) { - SNDERR("cannot allocate"); - return -ENOMEM; - } - - oss->device = strdup(device); - if (oss->device == NULL) { - SNDERR("cannot allocate"); - free(oss); - return -ENOMEM; - } - oss->fd = open(device, stream == SND_PCM_STREAM_PLAYBACK ? - O_WRONLY : O_RDONLY); - if (oss->fd < 0) { - err = -errno; - SNDERR("Cannot open device %s", device); - goto error; - } - - oss->io.version = SND_PCM_IOPLUG_VERSION; - oss->io.name = "ALSA <-> OSS PCM I/O Plugin"; - oss->io.poll_fd = oss->fd; - oss->io.poll_events = stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN; - oss->io.mmap_rw = 0; - oss->io.callback = stream == SND_PCM_STREAM_PLAYBACK ? - &oss_playback_callback : &oss_capture_callback; - oss->io.private_data = oss; - - err = snd_pcm_ioplug_create(&oss->io, name, stream, mode); - if (err < 0) - goto error; - - if ((err = oss_hw_constraint(oss)) < 0) { - snd_pcm_ioplug_delete(&oss->io); - return err; - } - - *pcmp = oss->io.pcm; - return 0; - - error: - if (oss->fd >= 0) - close(oss->fd); - free(oss->device); - free(oss); - return err; -} - -SND_PCM_PLUGIN_SYMBOL(oss); diff --git a/HAL-afb/HAL-plugin/README.md b/HAL-afb/HAL-plugin/README.md new file mode 100644 index 0000000..1c1e7a2 --- /dev/null +++ b/HAL-afb/HAL-plugin/README.md @@ -0,0 +1,44 @@ +Hal-Plugin + +Object: provide an ALSA a HAL (Hardware Abstraction Layer) to automotive sound cards +Status: Current version is proof of concept and not a usable product +Author: Fulup Ar Foll +Date : June-2017 + +HAL-plugin allows: + - to expose any existing NUMID under a customized label name, this is order to abstract sound card config. + - to add non alsa NUMID supported through a callback mechanism (eg: volume rampdown, power off, ...) + +installation + - Plugin should be placed where ever alsaplugins are located (typically: /usr/share/alsa-lib) + - Callback sharelib directory should be declare in $LD_LIBRARY_PATH + +Config +``` +cat ~/.asoundrc + ctl.agl_hal { + type afbhal + devid "hw:4" + cblib "afbhal_cb_sample.so" + ctls { + # ctlLabel {numid integer name "Alsa Ctl Name"} + MasterSwitch { numid 4 name "My_First_Control" } + MasterVol { numid 5 name "My_Second_Control" } + CB_sample { ctlcb @AfbHalSampleCB name "My_Sample_Callback"} + } + pcm.agl_hal { + type copy # Copy PCM + slave "hw:4" # Slave name + } +``` + +With such a config file + - numid=4 from sndcard=hw:4 is renamed "My_First_Control" + - numid=4 from sndcard=hw:4 is renamed "My_Second_Control" + - numid=4 will call AfbHalSampleCB from afbhal_cb_sample.so + +Note: to really implement a usable HAL at minimum every ALSA call should be implemented and read/write of values should be normalised from 0 to 100% with a step of 1. + +Conclusion: This demonstrate that implementing a HAL that both abstract ALSA get/set and enable non ALSA support is possible at an acceptable cost +without having to hack ALSA source code. The beauty of the model is than it is fully transparent to any ALSA application. The limit is that the plugin is loaded +within every application context, thus interaction with an external event loop remains complete as well as conflict management in case of share resources. -- cgit 1.2.3-korg