Files

565 lines
17 KiB
C
Raw Permalink Normal View History

2026-08-13 11:28:40 +08:00
/*
* Copyright (C) 2018 Amlogic Corporation.
*
* 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 LOG_TAG "audio_hw_utils_alsaparser"
#include <stdint.h>
#include <dirent.h>
#include <sys/types.h>
#include <errno.h>
#include <inttypes.h>
#include <sys/time.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdbool.h>
#include "aml_audio_log.h"
#include <string.h>
#include "alsa_device_parser.h"
#include "aml_malloc_debug.h"
#define READ_BUFFER_SIZE (512)
#define NAME_LEN (128)
/* patch */
#define ALSASOUND_CARD_PATH "/proc/asound/cards"
#define ALSASOUND_PCM_PATH "/proc/asound/pcm"
/* sound card name */
#define CARD_NAME_AUGE "AMLAUGESOUND"
#define CARD_NAME_MESON "AMLMESONAUDIO"
#define CARD_NAME_M8 "AMLM8AUDIO"
#define CARD_NAME_TV "AMLTVAUDIO"
#define CARD_AML_KEYWORD "AML"
/* for parse device port
* for i2s, i2s_playback, i2s_capture, when select device, firstly to check i2s_playback,
* i2s_capure, then i2s
*/
#define ALSAPORT_PCM "alsaPORT-pcm" /* usally for bt pcm */
#define ALSAPORT_I2S "alsaPORT-i2s" /* i2s0, Playback,Capture */
#define ALSAPORT_I2SPLAYPLAYBACK "alsaPORT-i2s1" /* i2s1 */
#define ALSAPORT_I2SCAPTURE "alsaPORT-i2s2" /* i2s2 */
#define ALSAPORT_TDM "alsaPORT-tdm"
#define ALSAPORT_PDM "alsaPORT-pdm"
#define ALSAPORT_SPDIF "alsaPORT-spdif"
#define ALSAPORT_SPDIFB "alsaPORT-spdifb"
#define ALSAPORT_I2S2HDMI "alsaPORT-i2s2hdmi" /* virtual link */
#define ALSAPORT_TV "alsaPORT-tv" /* Now for TV input */
#define ALSAPORT_LPBK "alsaPORT-loopback"
#define ALSAPORT_BUILTINMIC "builtinmic"
#define ALSAPORT_EARC "alsaPORT-earc"
#define ALSAPORT_I2S4HDMIRX "i2s4hdmirx"
#define ALSAPORT_I2S4PARSER "alsaPORT-i2s4parser"
#define ALSAPORT_HW_MIXER "alsaPORT-mixer" /* hw mixer */
struct AudioDeviceDescriptor {
char name[NAME_LEN];
int mCardindex;
int mPcmIndex;
int mPlayback;
int mCapture;
};
struct alsa_info {
int alsa_card_index;
/* check whether auge sound card */
int is_auge;
/* whether auge pcm devices are checked */
int deviced_checked;
struct AudioDeviceDescriptor *pcm_descrpt;
struct AudioDeviceDescriptor *i2s_descrpt;
struct AudioDeviceDescriptor *i2s1_descrpt;
struct AudioDeviceDescriptor *i2s2_descrpt;
struct AudioDeviceDescriptor *tdm_descrpt;
struct AudioDeviceDescriptor *pdm_descrpt;
struct AudioDeviceDescriptor *spdif_descrpt;
struct AudioDeviceDescriptor *spdifb_descrpt;
struct AudioDeviceDescriptor *i2s2hdmi_descrpt;
struct AudioDeviceDescriptor *tvin_descrpt;
struct AudioDeviceDescriptor *lpbk_descrpt;
struct AudioDeviceDescriptor *builtinmic_descrpt;
struct AudioDeviceDescriptor *earc_descrpt;
struct AudioDeviceDescriptor *i2s4hdmirx_descrpt;
struct AudioDeviceDescriptor *i2s4parser_descrpt;
struct AudioDeviceDescriptor *hw_mixer_descrpt;
};
static struct alsa_info *p_aml_alsa_info = NULL;
static void alsa_device_get_pcm_index();
static void alsa_device_parser_pcm_string(struct alsa_info *p_info, char *InputBuffer);
static struct alsa_info *alsa_device_get_info()
{
return p_aml_alsa_info;
}
bool alsa_device_is_auge(void)
{
struct alsa_info *p_info = alsa_device_get_info();
if (p_info && p_info->is_auge)
return true;
return false;
}
int alsa_device_get_card_index_by_name(void *name)
{
FILE *mCardFile = NULL;
int mCardIndex = -1;
char* mRet = NULL;
char tempbuffer[READ_BUFFER_SIZE];
if (!name)
return -1;
mCardFile = fopen(ALSASOUND_CARD_PATH, "r");
if (mCardFile) {
while (!feof(mCardFile)) {
mRet = fgets(tempbuffer, READ_BUFFER_SIZE, mCardFile);
/* this line contain '[' character */
if (strchr(tempbuffer, '[')) {
char *remaining_string = tempbuffer;
char *Rch = strtok_r(tempbuffer, "[", &remaining_string);
int id = atoi(Rch);
ALOGD("\tcurrent card id = %d, Rch = %s", id, Rch);
Rch = strtok_r(NULL, " ]", &remaining_string);
ALOGD("\tcurrent sound card name = %s", Rch);
if (strcmp(Rch, name) == 0) {
ALOGD("\t sound cardIndex found = %d", id);
mCardIndex = id;
break;
}
}
memset((void *)tempbuffer, 0, READ_BUFFER_SIZE);
}
fclose(mCardFile);
}
if (-1 == mCardIndex) {
char str[3];
mCardFile = fopen(ALSASOUND_PCM_PATH, "r");
while (fgets(tempbuffer, 100, mCardFile)) {
if (strstr(tempbuffer, name) != NULL) {
strncpy(str, tempbuffer, 2);
str[2] = '\0';
mCardIndex = atoi(str);
ALOGD("sound PcmIndex found = %d", mCardIndex);
break;
}
}
fclose(mCardFile);
}
return mCardIndex;
}
int alsa_device_get_pcm_index_by_name(void *name)
{
FILE *mCardFile = NULL;
int mPcmIndex = -1;
char buf[100];
char str[3];
if (!name)
return -1;
mCardFile = fopen(ALSASOUND_PCM_PATH, "r");
while (fgets(buf, 100, mCardFile)) {
if (strstr(buf, name) != NULL) {
strncpy(str, buf + 3, 2);
str[2] = '\0';
mPcmIndex = atoi(str);
ALOGD("sound PcmIndex found = %d", mPcmIndex);
break;
}
}
fclose(mCardFile);
return mPcmIndex;
}
/*
* cat /proc/asound/cards
* 0 [AMLAUGESOUND ]: AML-AUGESOUND - AML-AUGESOUND
AML-AUGESOUND
* mCardIndex = 0;
*/
int alsa_device_get_card_index()
{
struct alsa_info *p_info = alsa_device_get_info();
FILE *mCardFile = NULL;
bool isCardIndexFound = false;
int mCardIndex = -1;
if (p_info)
return p_info->alsa_card_index;
mCardFile = fopen(ALSASOUND_CARD_PATH, "r");
if (mCardFile) {
char tempbuffer[READ_BUFFER_SIZE];
ALOGD("card open success");
if (!p_aml_alsa_info) {
p_aml_alsa_info = aml_audio_calloc(1, sizeof(struct alsa_info));
if (!p_aml_alsa_info) {
ALOGE ("NOMEM for alsa info\n");
fclose(mCardFile);
return -1;
}
}
while (!feof(mCardFile)) {
if (fgets(tempbuffer, READ_BUFFER_SIZE, mCardFile) != NULL) {
/* this line contain '[' character */
if (strchr(tempbuffer, '[') && strstr(tempbuffer, CARD_AML_KEYWORD)) {
char *remaining_string = tempbuffer;
char *Rch = strtok_r(tempbuffer, "[", &remaining_string);
mCardIndex = atoi(Rch);
ALOGD("\tcurrent mCardIndex = %d, Rch = %s", mCardIndex, Rch);
Rch = strtok_r(NULL, " ]", &remaining_string);
ALOGD("\tcurrent sound card name = %s", Rch);
if (strcmp(Rch, CARD_NAME_AUGE) == 0) {
ALOGD("\t auge sound cardIndex found = %d", mCardIndex);
isCardIndexFound = true;
p_aml_alsa_info->is_auge = 1;
break;
} else if (strcmp(Rch, CARD_NAME_MESON) == 0) {
ALOGD("\t meson sound cardIndex found = %d", mCardIndex);
isCardIndexFound = true;
p_aml_alsa_info->is_auge = 0;
break;
}
}
}
memset((void *)tempbuffer, 0, READ_BUFFER_SIZE);
}
ALOGD("reach EOF");
fclose(mCardFile);
} else
ALOGE("Pcm open fail errno %d", errno);
if (isCardIndexFound == true) {
p_aml_alsa_info->alsa_card_index = mCardIndex;
} else
ALOGE("ParseCardIndex doesn't found card index");
ALOGD("%s() parse card index:%d\n", __FUNCTION__, mCardIndex);
alsa_device_get_pcm_index();
return mCardIndex;
}
/*
* alsaPORT is kept same with meson sound card
* cat /proc/asound/pcm
* 00-00: TDM-A-dummy-alsaPORT-pcm multicodec-0 : : playback 1 : capture 1
* 00-01: TDM-B-dummy-alsaPORT-i2s multicodec-1 : : playback 1 : capture 1
* 00-02: TDM-C-dummy multicodec-2 : : playback 1 : capture 1
* 00-03: PDM-dummy-alsaPORT-pdm dummy-3 : : capture 1
* 00-04: SPDIF-dummy-alsaPORT-spdif dummy-4 : : playback 1 : capture 1
* 00-05: SPDIF-B-dummy-alsaPORT-hdmi dummy-5 : : playback 1
*/
static void alsa_device_get_pcm_index()
{
struct alsa_info *p_info = alsa_device_get_info();
if (p_info && (!p_info->deviced_checked)) {
FILE *mPcmFile = NULL;
char tempbuffer[READ_BUFFER_SIZE];
mPcmFile = fopen(ALSASOUND_PCM_PATH, "r");
if (mPcmFile) {
ALOGD("Pcm open success");
while (!feof(mPcmFile)) {
if (fgets(tempbuffer, READ_BUFFER_SIZE, mPcmFile) != NULL) {
alsa_device_parser_pcm_string(p_info, tempbuffer);
memset((void *)tempbuffer, 0, READ_BUFFER_SIZE);
}
}
ALOGD("reach EOF");
fclose(mPcmFile);
p_info->deviced_checked = 1;
} else
ALOGD("Pcm open fail");
}
}
static void alsa_device_parser_pcm_string(struct alsa_info *p_info, char *InputBuffer)
{
char *Rch;
char mStreamName[256];
char *PortName = NULL;
char *remaining_string = InputBuffer;
ALOGD("AddPcmString p_info:%p, InputBuffer = %s", p_info, InputBuffer);
if (!p_info) {
ALOGE("alsa device info is NULL\n");
return;
}
Rch = strtok_r(InputBuffer, "-", &remaining_string);
/* parse for stream name */
if (Rch != NULL) {
struct AudioDeviceDescriptor *mAudioDeviceDescriptor =
aml_audio_calloc(1, sizeof(struct AudioDeviceDescriptor));
if (!mAudioDeviceDescriptor) {
ALOGE("%s no memory for device descriptor\n", __FUNCTION__);
return;
}
mAudioDeviceDescriptor->mCardindex = atoi(Rch);
Rch = strtok_r(NULL, ":", &remaining_string);
mAudioDeviceDescriptor->mPcmIndex = atoi(Rch);
Rch = strtok_r(NULL, ": ", &remaining_string);
if (Rch) {
memcpy(mStreamName, Rch, 256);
PortName = strstr(mStreamName, "alsaPORT-");
if (PortName) {
memcpy(mAudioDeviceDescriptor->name, PortName, sizeof(mAudioDeviceDescriptor->name));
if (!strncmp(PortName, ALSAPORT_PCM, strlen(ALSAPORT_PCM)) &&
p_info->pcm_descrpt == NULL) {
p_info->pcm_descrpt = mAudioDeviceDescriptor;
} else if (!strncmp(PortName, ALSAPORT_I2S2HDMI, strlen(ALSAPORT_I2S2HDMI)) &&
p_info->i2s2hdmi_descrpt == NULL) {
p_info->i2s2hdmi_descrpt = mAudioDeviceDescriptor;
} else if (!strncmp(PortName, ALSAPORT_I2SPLAYPLAYBACK, strlen(ALSAPORT_I2SPLAYPLAYBACK)) &&
p_info->i2s1_descrpt == NULL) {
p_info->i2s1_descrpt = mAudioDeviceDescriptor;
} else if (!strncmp(PortName, ALSAPORT_I2SCAPTURE, strlen(ALSAPORT_I2SCAPTURE)) &&
p_info->i2s2_descrpt == NULL) {
p_info->i2s2_descrpt = mAudioDeviceDescriptor;
} else if (!strncmp(PortName, ALSAPORT_I2S4PARSER, strlen(ALSAPORT_I2S4PARSER)) &&
p_info->i2s4parser_descrpt == NULL) {
p_info->i2s4parser_descrpt = mAudioDeviceDescriptor;
} else if (!strncmp(PortName, ALSAPORT_I2S, strlen(ALSAPORT_I2S)) &&
p_info->i2s_descrpt == NULL) {
p_info->i2s_descrpt = mAudioDeviceDescriptor;
if (strstr(PortName, ALSAPORT_I2S4HDMIRX))
p_info->i2s4hdmirx_descrpt = mAudioDeviceDescriptor;
} else if (!strncmp(PortName, ALSAPORT_TDM, strlen(ALSAPORT_TDM)) &&
p_info->tdm_descrpt == NULL) {
p_info->tdm_descrpt = mAudioDeviceDescriptor;
} else if (!strncmp(PortName, ALSAPORT_PDM, strlen(ALSAPORT_PDM)) &&
p_info->pdm_descrpt == NULL) {
p_info->pdm_descrpt = mAudioDeviceDescriptor;
} else if (!strncmp(PortName, ALSAPORT_SPDIFB, strlen(ALSAPORT_SPDIFB)) &&
p_info->spdifb_descrpt == NULL) {
p_info->spdifb_descrpt = mAudioDeviceDescriptor;
} else if (!strncmp(PortName, ALSAPORT_SPDIF, strlen(ALSAPORT_SPDIF)) &&
p_info->spdif_descrpt == NULL) {
p_info->spdif_descrpt = mAudioDeviceDescriptor;
} else if (!strncmp(PortName, ALSAPORT_TV, strlen(ALSAPORT_TV)) &&
p_info->tvin_descrpt == NULL) {
p_info->tvin_descrpt = mAudioDeviceDescriptor;
} else if (!strncmp(PortName, ALSAPORT_LPBK, strlen(ALSAPORT_LPBK)) &&
p_info->lpbk_descrpt == NULL) {
p_info->lpbk_descrpt = mAudioDeviceDescriptor;
} else if (!strncmp(PortName, ALSAPORT_EARC, strlen(ALSAPORT_EARC)) &&
p_info->earc_descrpt == NULL) {
p_info->earc_descrpt = mAudioDeviceDescriptor;
} else if (!strncmp(PortName, ALSAPORT_HW_MIXER, strlen(ALSAPORT_HW_MIXER)) &&
p_info->hw_mixer_descrpt == NULL) {
p_info->hw_mixer_descrpt = mAudioDeviceDescriptor;
} else {
ALOGD("%s(), port:%s, not used for any desc", __func__, PortName);
aml_audio_free(mAudioDeviceDescriptor);
mAudioDeviceDescriptor = NULL;
}
if (strstr(PortName, ALSAPORT_BUILTINMIC) != NULL) {
p_info->builtinmic_descrpt = mAudioDeviceDescriptor;
}
} else {
ALOGD("\tstream no alsaPORT prefix name, StreamName:%s\n", mStreamName);
}
}
if (mAudioDeviceDescriptor != NULL) {
ALOGD("%s Desc:%p mCardindex:%d, mPcmindex:%d, PortName:%s\n", __FUNCTION__, mAudioDeviceDescriptor,
mAudioDeviceDescriptor->mCardindex, mAudioDeviceDescriptor->mPcmIndex, PortName);
}
Rch = strtok_r(NULL, ": ", &remaining_string);
}
}
void dump_alsa_device_desc(struct alsa_info *p_info)
{
ALOGD("%s %s: %p", __func__, ALSAPORT_PCM, p_info->pcm_descrpt);
ALOGD("%s %s: %p", __func__, ALSAPORT_I2S, p_info->i2s_descrpt);
ALOGD("%s %s: %p", __func__, ALSAPORT_I2SPLAYPLAYBACK, p_info->i2s1_descrpt);
ALOGD("%s %s: %p", __func__, ALSAPORT_I2SCAPTURE, p_info->i2s2_descrpt);
ALOGD("%s %s: %p", __func__, ALSAPORT_TDM, p_info->tdm_descrpt);
ALOGD("%s %s: %p", __func__, ALSAPORT_PDM, p_info->pdm_descrpt);
ALOGD("%s %s: %p", __func__, ALSAPORT_SPDIF, p_info->spdif_descrpt);
ALOGD("%s %s: %p", __func__, ALSAPORT_SPDIFB, p_info->spdifb_descrpt);
ALOGD("%s %s: %p", __func__, ALSAPORT_I2S2HDMI, p_info->i2s2hdmi_descrpt);
ALOGD("%s %s: %p", __func__, ALSAPORT_TV, p_info->tvin_descrpt);
ALOGD("%s %s: %p", __func__, ALSAPORT_LPBK, p_info->lpbk_descrpt);
ALOGD("%s %s: %p", __func__, ALSAPORT_BUILTINMIC, p_info->builtinmic_descrpt);
ALOGD("%s %s: %p", __func__, ALSAPORT_EARC, p_info->earc_descrpt);
}
int alsa_device_update_pcm_index(int alsaPORT, int stream)
{
struct alsa_info *p_info = alsa_device_get_info();
int new_port = -1;
struct AudioDeviceDescriptor *pADD = NULL;
if (!p_info || !p_info->is_auge) {
/* fix to spdif port */
if (alsaPORT >= 2)
alsaPORT = 1;
return alsaPORT;
}
alsa_device_get_pcm_index();
switch (alsaPORT) {
case PORT_I2S:
pADD = p_info->i2s_descrpt;
break;
case PORT_SPDIF:
pADD = p_info->spdif_descrpt;
break;
case PORT_PCM:
pADD = p_info->pcm_descrpt;
break;
case PROT_TDM:
pADD = p_info->tdm_descrpt;
break;
case PROT_PDM:
pADD = p_info->pdm_descrpt;
break;
case PORT_SPDIFB:
pADD = p_info->spdifb_descrpt;
break;
case PORT_I2S2HDMI:
pADD = p_info->i2s2hdmi_descrpt;
break;
case PORT_TV:
pADD = p_info->tvin_descrpt;
break;
case PORT_I2S1:
pADD = p_info->i2s1_descrpt;
break;
case PORT_I2S2:
pADD = p_info->i2s2_descrpt;
break;
case PORT_LOOPBACK:
case PORT_ECHO_REFERENCE:
pADD = p_info->lpbk_descrpt;
break;
case PORT_BUILTINMIC:
pADD = p_info->builtinmic_descrpt;
if (!pADD) {
pADD = p_info->i2s_descrpt;
}
break;
case PORT_EARC:
pADD = p_info->earc_descrpt;
break;
case PORT_I2S4HDMIRX:
pADD = p_info->i2s4hdmirx_descrpt;
break;
case PORT_I2S4PARSER:
pADD = p_info->i2s4parser_descrpt;
break;
case PORT_HW_MIXER:
pADD = p_info->hw_mixer_descrpt;
break;
default:
pADD = p_info->i2s_descrpt;
ALOGD("Default port is I2s\n");
break;
}
if (pADD)
new_port = pADD->mPcmIndex;
ALOGD("auge sound card, pAdd=%p fix alsaPORT:%d to :%d\n",pADD, alsaPORT, new_port);
return new_port;
}
bool is_earc_descrpt(void)
{
return p_aml_alsa_info->earc_descrpt ? 1 : 0;
}
int release_alsa_device(void)
{
struct alsa_info *p_info = alsa_device_get_info();
if (p_info) {
if (p_info->pcm_descrpt)
aml_audio_free(p_info->pcm_descrpt);
if (p_info->i2s2hdmi_descrpt)
aml_audio_free(p_info->i2s2hdmi_descrpt);
if (p_info->i2s1_descrpt)
aml_audio_free(p_info->i2s1_descrpt);
if (p_info->i2s2_descrpt)
aml_audio_free(p_info->i2s2_descrpt);
if (p_info->i2s4parser_descrpt)
aml_audio_free(p_info->i2s4parser_descrpt);
if (p_info->i2s_descrpt)
aml_audio_free(p_info->i2s_descrpt);
//if (p_info->i2s4hdmirx_descrpt)
// aml_audio_free(p_info->i2s4hdmirx_descrpt);
if (p_info->tdm_descrpt)
aml_audio_free(p_info->tdm_descrpt);
if (p_info->pdm_descrpt)
aml_audio_free(p_info->pdm_descrpt);
if (p_info->spdifb_descrpt)
aml_audio_free(p_info->spdifb_descrpt);
if (p_info->spdif_descrpt)
aml_audio_free(p_info->spdif_descrpt);
if (p_info->tvin_descrpt)
aml_audio_free(p_info->tvin_descrpt);
if (p_info->lpbk_descrpt)
aml_audio_free(p_info->lpbk_descrpt);
if (p_info->earc_descrpt)
aml_audio_free(p_info->earc_descrpt);
//if (p_info->builtinmic_descrpt)
// aml_audio_free(p_info->builtinmic_descrpt);
aml_audio_free(p_info);
}
return 0;
}
bool alsa_device_has_hwmixer(void)
{
bool exist = false;
int device_id = alsa_device_update_pcm_index(PORT_HW_MIXER, PLAYBACK);
if (device_id >= 0) {
AM_LOGI("hwmixer sound device %d", device_id);
exist = true;
} else {
AM_LOGI("hwmixer sound device does not exist");
exist = false;
}
return exist;
}