afm_ctl.c example

Updated: May 06, 2022

This is the source for the afm_ctl utility.

Note: The afm_ctl utility is shipped with Acoustic Management Platform 3.0.

For information about using this utility, see afm_ctl in the QNX Neutrino Utilities Reference.

/*
 * Copyright 2016, QNX Software Systems Ltd. All Rights Reserved.
 *
 * This source code may contain confidential information of QNX Software
 * Systems Ltd.  (QSSL) and its licensors. Any use, reproduction,
 * modification, disclosure, distribution or transfer of this software,
 * or any software which includes or is based upon any of this code, is
 * prohibited unless expressly authorized by QSSL by written agreement. For
 * more information (including whether this source code file has been
 * published) please email licensing@qnx.com.
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/asoundlib.h>
#include <string.h>
#include <devctl.h>
#include <errno.h>
#include <ctype.h>
#include <ioctl.h>


//*****************************************************************************
/* *INDENT-OFF* */
#ifdef __USAGE
%C [Options]

Options:
    -a [card#:]<dev#>        the AFM card & device number to start/issue command
                             OR
    -a [name]                the AFM card name (e.g. voice, icc) to start/issue command

    -f <filename>            set wav file (full path) (recorder/player AFMs only)
    -m <mode>                set audio mode
    -c                       reset audio mode
    -t <dataset>             load runtime acoustic processing dataset
    -u                       clear runtime acoustic processing dataset
    -l <ms_offset>           start microphone latency test
    -r <ms_offset>           start reference latency test
    -v <rpm>                 set rpm VIN value
                             (diagnostic use only - only applicable if rpm is a base VIN)
    -x <param_id:chn>[:data] calls set/get data on afm for ap parameters of size int16_t
    -y <param_id:chn>[:data] calls set/get data on afm for ap parameters of size int32_t
    -z <param_id>[:data]     calls set/get data on afm for afm parameters of size int32_t
    -s                       stop AFM
#endif
/* *INDENT-ON* */
//*****************************************************************************

const char* optstring = "a:f:m:sl:r:cv:t:ux:y:z:";
#define LATENCY_TEST_MIC 0
#define LATENCY_TEST_REF 1

static snd_afm_t* setup_afm_handle(char* arg)
{
    int rtn;
    int dev = 0, card = 0;
    const char* name = NULL;
    snd_afm_t* afm_handle = NULL;

    if (strchr(arg, ':'))
    {
        card = atoi(arg);
        dev = atoi(strchr(arg, ':') + 1);
    }
    else if (isalpha(arg[0]) || (arg[0] == '/'))
        name = arg;
    else
        dev = atoi(arg);

    if (name)
    {
        printf("Using %s\n", name);
        rtn = snd_afm_open_name(&afm_handle, name);
    }
    else
    {
        printf("Using card %d device %d \n", card, dev);
        rtn = snd_afm_open(&afm_handle, card, dev);
    }
    if (rtn != EOK)
    {
        fprintf(stderr, "snd_afm_open failed: (%d) %s\n", rtn, snd_strerror(rtn));
        return NULL;
    }

    return afm_handle;
}

static int latency_test(snd_afm_t* afm_handle, int input_device, int ms_offset)
{
    int rtn = EOK;
    int fd;
    snd_afm_latency_test_t test;

    if ((fd = snd_afm_file_descriptor(afm_handle)) > 0)
    {
        test.input_device = input_device;  /* 0 = mic, 1 = ref */
        test.input_voice = 0;
        test.ms_offset = ms_offset;

        if (ioctl(fd, SND_AFM_IOCTL_START_LATENCY_TEST, &test) < 0) {
            printf("Failed to start latency test: (%d) %s\n", errno, strerror(errno));
            rtn = -errno; /* Negate errno to match snd_xxx error codes */
        }
    }
    else
    {
        printf("Failed to get AFM descriptor for latency test (%d) %s\n", errno, strerror(errno));
        rtn = -errno; /* Negate errno to match snd_xxx error codes */
    }
    return rtn;
}

static int set_audio_mode(snd_afm_t* afm_handle, const char* mode)
{
    int rtn;
    char str[64];

    if (mode[0]) {
        printf("Setting mode to %s\n", mode);
    } else {
        printf("Clearing mode\n");
    }
    if ((rtn = snd_afm_set_audio_mode(afm_handle, mode)) != EOK)
        printf("Failed to set mode: (%d) %s\n", rtn, snd_strerror(rtn));

    if ((rtn = snd_afm_get_audio_mode(afm_handle, str, sizeof(str))) != EOK)
        printf("Failed to get mode: (%d) %s\n", rtn, snd_strerror(rtn));
    else
        printf("Audio Mode = %s\n", str);
    return rtn;
}

static int set_dataset(snd_afm_t* afm_handle, const char* dataset)
{
    int rtn;
    int ap_status = 0;

    if (dataset[0])
        printf("Loading dataset %s\n", dataset);
     else
        printf("Clearing dataset\n");

    if ((rtn = snd_afm_load_ap_dataset(afm_handle, dataset, &ap_status)) != EOK)
        printf("Failed to set dataset: (%d) %s\n", rtn, snd_strerror(rtn));
    if (ap_status != 0)
        printf("Acoustic processing returned status=0x%04X\n", ap_status);
    return rtn;
}

static int set_rpm_vin(snd_afm_t* afm_handle, int rpm)
{
    int rtn;
    int vinCount = 0;
    if ((rtn = snd_afm_get_vin_list_count(afm_handle, &vinCount)) == EOK) {
        snd_afm_vin_list_item_t* vin_items = alloca( sizeof(snd_afm_vin_list_item_t) * vinCount);
        snd_afm_vin_pair_t* vin_pairs = alloca( sizeof(snd_afm_vin_pair_t) * vinCount);
        if (vin_items && vin_pairs)
        {
            memset(vin_pairs, 0, sizeof(snd_afm_vin_pair_t) * vinCount);
            if ((rtn = snd_afm_get_vin_list(afm_handle, vin_items, vinCount)) == EOK) {
                int i;
                for (i=0; i<vinCount; i++) {
                    vin_pairs[i].key = vin_items[i].key;
                    if (vin_items[i].is_rpm) {
                        vin_pairs[i].value = rpm;
                        printf("Vin 0x%X set to %d\n", vin_pairs[i].key,  vin_pairs[i].value);
                    }
                }
                rtn = snd_afm_set_vin_stream(afm_handle, vin_pairs, vinCount);
            }
        } else {
            rtn = -errno; /* Negate errno to match snd_xxx error codes */
        }
    }
    if (rtn != EOK)
    {
        printf("Failed to set RPM (%d) %s\n", rtn, snd_strerror(rtn));
    }
    return rtn;
}

/* Set/get acoustic processing parameter */
static int set_ap_data(snd_afm_t* afm_handle, char* arg, size_t data_size)
{
    int set_data, rtn;
    int32_t data;
    snd_afm_ap_param_t param = {
        .size = data_size,
    };

    param.dataId = strtol(arg, &arg, 0);
    if (errno == ERANGE || errno == EINVAL) {
        fprintf(stderr, "Invalid ap data id\n");
        return 1;
    }
    if (*arg) arg++;
    param.channel = strtol(arg, &arg, 0);
    if (errno == ERANGE || errno == EINVAL) {
        fprintf(stderr, "Invalid channel\n");
        return 1;
    }
    if (*arg) arg++;
    data = strtol(arg, &arg, 0);
    set_data = (errno != ERANGE && errno != EINVAL);
    errno = EOK;
    if (set_data) {
        printf("AFM set_ap_data id=0x%04x data=%d ", param.dataId, data);
        rtn = snd_afm_set_ap_data(afm_handle, &param, &data);
    } else {
        printf("AFM get_ap_data id=0x%04x ", param.dataId);
        rtn = snd_afm_get_ap_data(afm_handle, &param, &data);
    }
    printf("ret=%d ap_ret=%d data_ret=%d -- %s\n", rtn, param.status, data, strerror(-rtn));
    return rtn;
}


/* Set/get acoustic processing parameter */
static int set_afm_param(snd_afm_t* afm_handle, char* arg, size_t data_size)
{
    int set_data, rtn;
    int32_t data, param_id;

    param_id = strtol(arg, &arg, 0);
    if (errno == ERANGE || errno == EINVAL) {
        fprintf(stderr, "Invalid afm param data id\n");
        return 1;
    }
    if (*arg) arg++;
    data = strtol(arg, &arg, 0);
    set_data = (errno != ERANGE && errno != EINVAL);
    errno = EOK;
    if (set_data) {
        printf("AFM set_param id=0x%04x data=%d ", param_id, data);
        rtn = snd_afm_set_param(afm_handle, param_id, data_size, &data);
    } else {
        printf("AFM get_param id=0x%04x ", param_id);
        rtn = snd_afm_get_param(afm_handle, param_id, &data_size, &data);
    }
    printf("ret=%d data_ret=%d -- %s\n", rtn, data, strerror(-rtn));
    return rtn;
}


int main(int argc, char *argv[])
{
    int rtn = EOK;
    snd_afm_t *afm_handle = NULL;
    int start_afm = 1;
    int c;

    /* first setup afm handle */
    while ((c = getopt(argc, argv, optstring)) != EOF)
    {
        switch (c)
        {
        case 'a':
            if (!(afm_handle = setup_afm_handle(optarg))) {
                return EXIT_FAILURE;
            }
            break;
        default:
            /* handle other options instead of starting AFM */
            start_afm = 0;
            break;
        }
    }

    if (!afm_handle) {
        printf("No AFM specified, use -a option\n");
        return EXIT_FAILURE;
    }
    if (start_afm) {
        printf("Starting AFM \n");
        if ((rtn = snd_afm_start(afm_handle)) != EOK)
            printf("Failed to start AFM: (%d) %s\n", rtn, snd_strerror(rtn));

        snd_afm_close(afm_handle);
        return (rtn == EOK) ? EXIT_SUCCESS : EXIT_FAILURE;
    }

    /* reset optind and handle other options */
    optind = 1;
    while (((c = getopt(argc, argv, optstring)) != EOF) && (rtn == EOK))
    {
        switch (c)
        {
        case 'a':
            break;

        case 's':
            printf("Stopping AFM \n");
            if ((rtn = snd_afm_stop(afm_handle)) != EOK)
                printf("Failed to stop AFM: (%d) %s\n", rtn, snd_strerror(rtn));
            break;

        case 'l':
            rtn = latency_test(afm_handle, LATENCY_TEST_MIC, atoi(optarg));
            break;

        case 'r':
            rtn = latency_test(afm_handle, LATENCY_TEST_REF, atoi(optarg));
            break;

        case 'f':
            printf("Setting filename %s, len = %zu\n", optarg, strlen(optarg));
            if ((rtn = snd_afm_set_path(afm_handle, SND_AFM_WAV_FILE, optarg)) != EOK)
                printf("Failed to set filename: (%d) %s\n", rtn, snd_strerror(rtn));
            break;

        case 'm':
            rtn = set_audio_mode(afm_handle, optarg);
            break;

        case 'c':
            rtn = set_audio_mode(afm_handle, "");
            break;

        case 't':
            rtn = set_dataset(afm_handle, optarg);
            break;

        case 'u':
            rtn = set_dataset(afm_handle, "");
            break;

        case 'v':
            rtn = set_rpm_vin(afm_handle, strtoul(optarg, NULL, 0));
            break;

        case 'x':
            rtn = set_ap_data(afm_handle, optarg, sizeof(uint16_t));
            break;

        case 'y':
            rtn = set_ap_data(afm_handle, optarg, sizeof(uint32_t));
            break;

        case 'z':
            rtn = set_afm_param(afm_handle, optarg, sizeof(uint32_t));
            break;

        default:
            fprintf(stderr, "Invalid option '%c'\n", c);
            rtn = -1;
            break;
        }
    }

    snd_afm_close(afm_handle);

    return (rtn == EOK) ? EXIT_SUCCESS : EXIT_FAILURE;
}

#if defined(__QNXNTO__) && defined(__USESRCVERSION)
#include <sys/srcversion.h>
__SRCVERSION("$URL$ $Rev$")
#endif