| Updated: October 28, 2024 |
This is the source for the afm_ctl utility.
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[,data]]
calls set/get data on afm for acoustic processing parameters of size int16_t
up to 10 comma separated data values can be set
-y <param_id:chn>[:data[,data]]
calls set/get data on afm for acoustic processing parameters of size int32_t
up to 10 comma separated data values can be set
-z <param_id>[:data] calls set/get data on afm for afm parameters of size int32_t
-s stop AFM
-i display AFM info
#endif
/* *INDENT-ON* */
//*****************************************************************************
const char* optstring = "a:f:m:sl:r:cv:t:ux:y:z:i";
#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;
char *tmp = strchr(arg, ':');
if (tmp)
{
card = atoi(arg);
dev = atoi(tmp + 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 inline char* afm_state_str(int state) {
switch (state) {
case SND_AFM_STATE_IDLE: return "SND_AFM_STATE_IDLE";
case SND_AFM_STATE_RUNNING: return "SND_AFM_STATE_RUNNING";
case SND_AFM_STATE_RUNNING_PCM: return "SND_AFM_STATE_RUNNING_PCM";
case SND_AFM_STATE_SHUTDOWN: return "SND_AFM_STATE_SHUTDOWN";
default: return "Unknown";
}
}
static int display_info(snd_afm_t* const afm_handle)
{
int rtn = EOK;
snd_afm_info_t info;
snd_afm_status_t status;
if ((rtn = snd_afm_info(afm_handle, &info)) == EOK) {
printf("name: %s\n", info.name);
printf("device: afmC%dD%d\n", info.card, info.device);
printf("cardname: %s\n", info.cardname);
if ((rtn = snd_afm_status(afm_handle, &status)) == EOK) {
printf("state: %s\n", afm_state_str(status.state));
printf("timestamp: %" PRId64 "\n", status.ms_processed);
} else {
fprintf(stderr, "Failed to retreive AFM status: (%d) %s\n", rtn, snd_strerror(rtn));
}
} else {
fprintf(stderr, "Failed to retreive AFM info: (%d) %s\n", rtn, snd_strerror(rtn));
}
return rtn;
}
static int latency_test(snd_afm_t* const 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) {
fprintf(stderr, "Failed to start latency test: (%d) %s\n", errno, strerror(errno));
rtn = -errno; /* Negate errno to match snd_xxx error codes */
}
}
else
{
fprintf(stderr, "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)
fprintf(stderr, "Failed to set mode: (%d) %s\n", rtn, snd_strerror(rtn));
if ((rtn = snd_afm_get_audio_mode(afm_handle, str, sizeof(str))) != EOK)
fprintf(stderr, "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)
fprintf(stderr, "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)
{
fprintf(stderr, "Failed to set RPM (%d) %s\n", rtn, snd_strerror(rtn));
}
return rtn;
}
static void printout_data(void* data, int count, uint32_t data_size)
{
int i;
if (data_size == sizeof(int16_t)) {
int16_t* data16 = (int16_t*)data;
printf("%d", data16[0]);
if (data16[0] %lt; 0) {
printf("(%uU)", (uint16_t)data16[0]);
}
for (i=1; i<count; i++) {
printf(",%d", data16[i]);
if (data16[i] < 0) {
printf("(%uU)", (uint16_t)data16[i]);
}
}
} else {
int32_t* data32 = (int32_t*)data;
printf("%d", data32[0]);
if (data32[0] < 0) {
printf("(%uU)", (uint32_t)data32[0]);
}
for (i=1; i<count; i++) {
printf(",%d", data32[i]);
if (data32[i] < 0) {
printf("(%uU)", (uint32_t)data32[i]);
}
}
}
}
/* Set/get acoustic processing parameter */
#define MAX_VALS 10
static int set_ap_data(snd_afm_t* afm_handle, char* arg, uint32_t data_size)
{
int set_data = 0, rtn;
int32_t data[MAX_VALS] = {0};
snd_afm_ap_param_t param = {
.size = data_size,
};
int count = 0;
int32_t* data32 = (int32_t*)data;
int16_t* data16 = (int16_t*)data;
char *tokStr, *val;
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++;
/* Scan string for data */
tokStr = strdup(arg);
if (tokStr != NULL)
{
val = strtok(tokStr, ",");
while (val != NULL) {
if (count == MAX_VALS) {
fprintf(stderr, "Too many values\n");
free(tokStr);
return 1;
}
if (data_size == sizeof(int16_t)) {
data16[count] = strtol(val, NULL, 0);
} else {
data32[count] = strtol(val, NULL, 0);
}
if (errno != ERANGE && errno != EINVAL) {
count++;
val = strtok(NULL, ",");
} else {
/* Fallback to get_data on error */
count = 0;
val = NULL;
}
}
set_data = (count != 0);
free(tokStr);
}
if (set_data) {
param.size = count * data_size;
printf("AFM set_ap_data id=0x%04x data=", param.dataId);
printout_data(data, count, data_size);
rtn = snd_afm_set_ap_data(afm_handle, ¶m, data);
} else {
/* Provide the entire buffer for the get data result */
param.size = sizeof(data);
printf("AFM get_ap_data id=0x%04x", param.dataId);
rtn = snd_afm_get_ap_data(afm_handle, ¶m, data);
count = param.size / data_size;
}
printf(" ret=%d ap_ret=%d data_ret=", rtn, param.status);
printout_data(data, count, data_size);
printf(" -- %s\n", param.status ? "AP Error" : strerror(-rtn));
return rtn;
}
/* Set/get acoustic processing parameter */
static int set_afm_param(snd_afm_t* afm_handle, char* arg, uint32_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) {
fprintf(stderr, "Multiple AFMs specified, use one at a time\n");
snd_afm_close(afm_handle);
return EXIT_FAILURE;
}
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) {
fprintf(stderr, "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)
fprintf(stderr, "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)
fprintf(stderr, "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)
fprintf(stderr, "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 'i':
rtn = display_info(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