• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Rockchip Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "alsa_audio.h"
18 static struct DevInfo devIn[SND_IN_SOUND_CARD_MAX];
19 static struct DevInfo devOut[SND_OUT_SOUND_CARD_MAX];
20 
21 struct DevProcInfo SPEAKER_OUT_NAME[] = {
22     {"realtekrt5616c", NULL},
23     {"realtekrt5651co", "rt5651-aif1"},
24     {"realtekrt5670c", NULL},
25     {"realtekrt5672c", NULL},
26     {"realtekrt5678co", NULL},
27     {"rkhdmianalogsnd", NULL},
28     {"rockchipcx2072x", NULL},
29     {"rockchipes8316c", NULL},
30     {"rockchipes8323c", NULL},
31     {"rockchipes8388c", NULL},
32     {"rockchipes8396c", NULL},
33     {"rockchiprk", NULL},
34     {"rockchiprk809co", NULL},
35     {"rockchiprk817co", NULL},
36     {"rockchiprt5640c", "rt5640-aif1"},
37     {"rockchiprt5670c", NULL},
38     {"rockchiprt5672c", NULL},
39     {NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
40 };
41 
42 struct DevProcInfo HDMI_OUT_NAME[] = {
43     {"realtekrt5651co", "i2s-hifi"},
44     {"realtekrt5670co", "i2s-hifi"},
45     {"rkhdmidpsound", NULL},
46     {"hdmisound", NULL},
47     {"rockchiphdmi", NULL},
48     {"rockchiprt5640c", "i2s-hifi"},
49     {NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
50 };
51 
52 
53 struct DevProcInfo SPDIF_OUT_NAME[] = {
54     {"ROCKCHIPSPDIF", "dit-hifi"},
55     {"rockchipspdif", NULL},
56     {"rockchipcdndp", NULL},
57     {NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
58 };
59 
60 struct DevProcInfo BT_OUT_NAME[] = {
61     {"rockchipbt", NULL},
62     {NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
63 };
64 
65 struct DevProcInfo MIC_IN_NAME[] = {
66     {"realtekrt5616c", NULL},
67     {"realtekrt5651co", "rt5651-aif1"},
68     {"realtekrt5670c", NULL},
69     {"realtekrt5672c", NULL},
70     {"realtekrt5678co", NULL},
71     {"rockchipes8316c", NULL},
72     {"rockchipes8323c", NULL},
73     {"rockchipes8396c", NULL},
74     {"rockchipes7210", NULL},
75     {"rockchipes7243", NULL},
76     {"rockchiprk", NULL},
77     {"rockchiprk809co", NULL},
78     {"rockchiprk817co", NULL},
79     {"rockchiprt5640c", NULL},
80     {"rockchiprt5670c", NULL},
81     {"rockchiprt5672c", NULL},
82     {NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
83 };
84 
85 
86 struct DevProcInfo HDMI_IN_NAME[] = {
87     {"realtekrt5651co", "tc358749x-audio"},
88     {"hdmiin", NULL},
89     {NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
90 };
91 
92 struct DevProcInfo BT_IN_NAME[] = {
93     {"rockchipbt", NULL},
94     {NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
95 };
96 static struct pcm_config g_renderPcmCfg;
97 
98 struct RenderParamcheckList {
99     enum pcm_param param;
100     uint32_t value;
101     char *paramName;
102     char *paramUnit;
103 };
SetDefaultDevInfo(struct DevInfo * info,int32_t size,int32_t rid)104 static void SetDefaultDevInfo(struct DevInfo *info, int32_t size, int32_t rid)
105 {
106     for (int32_t i = 0; i < size; i++) {
107         if (rid) {
108             info[i].id = NULL;
109         }
110         info[i].card = (int32_t)SND_OUT_SOUND_CARD_UNKNOWN;
111     }
112 }
113 
name_match(const char * dst,const char * src)114 static int32_t name_match(const char* dst, const char* src)
115 {
116     int32_t score = 0;
117     if (!strcmp(dst, src)) {
118         score = 100; // match 100%, total equal
119     } else  if (strstr(dst, src)) {
120         score = 50; //  match 50%, part equal
121     }
122 
123     return score;
124 }
125 
dev_id_match(const char * info,const char * did)126 static bool dev_id_match(const char *info, const char *did)
127 {
128     const char *deli = "id:";
129     char *id;
130     int32_t idx = 0;
131 
132     if (!did) {
133         return true;
134     }
135     if (!info) {
136         return false;
137     }
138     /* find str like-> id: ff880000.i2s-rt5651-aif1 rt5651-aif1-0 */
139     id = strstr(info, deli);
140     if (!id) {
141         return false;
142     }
143     id += strlen(deli);
144     while (id[idx] != '\0') {
145         if (id[idx] == '\r' || id[idx] == '\n') {
146             id[idx] = '\0';
147             break;
148         }
149         idx++;
150     }
151     if (strstr(id, did)) {
152         LOG_PARA_INFO("match dai!!!: %s", id, did);
153         return true;
154     }
155     return false;
156 }
157 
GetSpecifiedDevicesCheck(struct DevInfo * devinfo,int32_t card,const char * id,struct DevProcInfo * match,int32_t * index)158 static bool GetSpecifiedDevicesCheck(struct DevInfo *devinfo, int32_t card,
159     const char *id, struct DevProcInfo *match, int32_t *index)
160 {
161     int32_t i = 0;
162     int32_t better = 0;
163     /* parse card id */
164     if (!match) {
165         return true; /* match any */
166     }
167     while (match[i].cid) {
168         int32_t score = name_match(id, match[i].cid);
169         if (score > better) {
170             better = score;
171             *index = i;
172         }
173         i++;
174     }
175 
176     if (*index < 0) {
177         return false;
178     }
179     if (!match[*index].cid) {
180         return false;
181     }
182     if (!match[*index].did) { /* no exist dai info, exit */
183         devinfo->card = card;
184         devinfo->device = 0;
185         LOG_PARA_INFO("%s card, got card=%d,device=%d", devinfo->id,
186             devinfo->card, devinfo->device);
187         return true;
188     }
189     return true;
190 }
191 
GetSpecifiedOutDev(struct DevInfo * devinfo,int32_t card,const char * id,struct DevProcInfo * match)192 static bool GetSpecifiedOutDev(struct DevInfo *devinfo, int32_t card,
193     const char *id, struct DevProcInfo *match)
194 {
195     int32_t device;
196     char deviceInfoPath[32];
197     char info[256];
198     size_t len;
199     FILE* file = NULL;
200     int32_t index = -1;
201     bool ret = GetSpecifiedDevicesCheck(devinfo, card, id, match, &index);
202     if (!ret) {
203         return false;
204     }
205     /* parse device id */
206     for (device = 0; device < SNDRV_DEVICES; device++) {
207         int32_t ret = sprintf_s(deviceInfoPath, sizeof(deviceInfoPath) - 1,
208             "proc/asound/card%d/pcm%dp/info", card, device);
209         if (ret < 0) {
210             LOG_PARA_INFO("out card %d devices %d info path sprintf failed", card, device);
211             break;
212         }
213         if (access(deviceInfoPath, 0)) {
214             LOG_PARA_INFO("No exist %s, break and finish parsing", deviceInfoPath);
215             break;
216         }
217         file = fopen(deviceInfoPath, "r");
218         if (!file) {
219             LOG_PARA_INFO("Could reading %s property", deviceInfoPath);
220             continue;
221         }
222         len = fread(info, sizeof(char), sizeof(info) / sizeof(char), file);
223         if (fclose(file)) {
224             LOG_FUN_ERR("fclose(%s) failed", deviceInfoPath);
225         }
226         if (len == 0 || len > sizeof(info) / sizeof(char)) {
227             continue;
228         }
229         info[len - 1] = '\0';
230         /* parse device dai */
231         if (dev_id_match(info, match[index].did)) {
232             devinfo->card = card;
233             devinfo->device = device;
234             LOG_PARA_INFO("%s card, got card=%d,device=%d", devinfo->id,
235                 devinfo->card, devinfo->device);
236             return true;
237         }
238     }
239     return false;
240 }
241 
GetSpecifiedInDev(struct DevInfo * devinfo,int32_t card,const char * id,struct DevProcInfo * match)242 static bool GetSpecifiedInDev(struct DevInfo *devinfo, int32_t card,
243     const char *id, struct DevProcInfo *match)
244 {
245     int32_t device;
246     char deviceInfoPath[32];
247     char info[256];
248     size_t len;
249     FILE* file = NULL;
250     int32_t index = -1;
251     bool ret = GetSpecifiedDevicesCheck(devinfo, card, id, match, &index);
252     if (!ret) {
253         return false;
254     }
255     /* parse device id */
256     for (device = 0; device < SNDRV_DEVICES; device++) {
257         int32_t ret = sprintf_s(deviceInfoPath, sizeof(deviceInfoPath) - 1,
258             "proc/asound/card%d/pcm%dc/info", card, device);
259         if (ret < 0) {
260             LOG_PARA_INFO("in card %d devices %d info path sprintf failed", card, device);
261             break;
262         }
263         if (access(deviceInfoPath, 0)) {
264             LOG_PARA_INFO("No exist %s, break and finish parsing", deviceInfoPath);
265             break;
266         }
267         file = fopen(deviceInfoPath, "r");
268         if (!file) {
269             LOG_PARA_INFO("Could reading %s property", deviceInfoPath);
270             continue;
271         }
272         len = fread(info, sizeof(char), sizeof(info) / sizeof(char), file);
273         if (fclose(file)) {
274             LOG_FUN_ERR("fclose(%s) failed", deviceInfoPath);
275         }
276         if (len == 0 || len > sizeof(info) / sizeof(char)) {
277             continue;
278         }
279         info[len - 1] = '\0';
280         /* parse device dai */
281         if (dev_id_match(info, match[index].did)) {
282             devinfo->card = card;
283             devinfo->device = device;
284             LOG_PARA_INFO("%s card, got card=%d,device=%d", devinfo->id,
285                 devinfo->card, devinfo->device);
286             return true;
287         }
288     }
289     return false;
290 }
291 
dumpdev_info(const char * tag,struct DevInfo * devInfo,int32_t count)292 static void dumpdev_info(const char *tag, struct DevInfo  *devInfo, int32_t count)
293 {
294     LOG_PARA_INFO("dump %s device info", tag);
295     for (int32_t i = 0; i < count; i++) {
296         if (devInfo[i].id && devInfo[i].card != SND_OUT_SOUND_CARD_UNKNOWN) {
297             LOG_PARA_INFO("dev_info %s  card=%d, device:%d",
298                 devInfo[i].id, devInfo[i].card, devInfo[i].device);
299         }
300     }
301 }
302 
ReadInSoundCard(void)303 void ReadInSoundCard(void)
304 {
305     char sndCardId[32];
306     char CardIdInfo[20];
307     size_t cardIdLen;
308     FILE* file = NULL;
309     devIn[SND_IN_SOUND_CARD_MIC].id = "MIC";
310     devIn[SND_IN_SOUND_CARD_BT].id = "BT";
311     SetDefaultDevInfo(devIn, SND_IN_SOUND_CARD_UNKNOWN, 0);
312     for (int32_t card = 0; card < SNDRV_CARDS; card++) {
313         int32_t ret = sprintf_s(sndCardId, sizeof(sndCardId) - 1, "proc/asound/card%d/id", card);
314         if (ret < 0) {
315             LOG_PARA_INFO("in card %d idinfo path sprintf failed", card);
316             break;
317         }
318         if (access(sndCardId, 0)) {
319             LOG_PARA_INFO("No exist %s, break and finish parsing", sndCardId);
320                 break;
321         }
322         file = fopen(sndCardId, "r");
323         if (!file) {
324             LOG_PARA_INFO("Could reading %s property", sndCardId);
325             continue;
326         }
327         cardIdLen = fread(CardIdInfo, sizeof(char), sizeof(CardIdInfo) / sizeof(char), file);
328         if (fclose(file)) {
329             LOG_FUN_ERR("fclose(%s) failed", sndCardId);
330         }
331         if (cardIdLen == 0 || cardIdLen > sizeof(CardIdInfo) / sizeof(char)) {
332             continue;
333         }
334         CardIdInfo[cardIdLen - 1] = '\0';
335         GetSpecifiedInDev(&devIn[SND_IN_SOUND_CARD_MIC], card, CardIdInfo, MIC_IN_NAME);
336         /* set HDMI audio input info if need hdmi audio input */
337         GetSpecifiedInDev(&devIn[SND_IN_SOUND_CARD_HDMI], card, CardIdInfo, HDMI_IN_NAME);
338         GetSpecifiedInDev(&devIn[SND_IN_SOUND_CARD_BT], card, CardIdInfo, BT_IN_NAME);
339     }
340     dumpdev_info("in", devIn, SND_IN_SOUND_CARD_MAX);
341     return;
342 }
343 
ReadOutSoundCard(void)344 void ReadOutSoundCard(void)
345 {
346     char sndCardId[32];
347     char CardIdInfo[20];
348     size_t cardIdLen;
349     FILE* file = NULL;
350     devOut[SND_OUT_SOUND_CARD_SPEAKER].id = "SPEAKER";
351     devOut[SND_OUT_SOUND_CARD_HDMI].id = "HDMI";
352     devOut[SND_OUT_SOUND_CARD_SPDIF].id = "SPDIF";
353     devOut[SND_OUT_SOUND_CARD_BT].id = "BT";
354     SetDefaultDevInfo(devOut, SND_OUT_SOUND_CARD_UNKNOWN, 0);
355     for (int32_t card = 0; card < SNDRV_CARDS; card++) {
356         int32_t ret = sprintf_s(sndCardId, sizeof(sndCardId) - 1, "proc/asound/card%d/id", card);
357         if (ret < 0) {
358             LOG_PARA_INFO("out card %d idinfo path sprintf failed", card);
359             break;
360         }
361         if (access(sndCardId, 0)) {
362             LOG_PARA_INFO("No exist %s, break and finish parsing", sndCardId);
363             break;
364         }
365         file = fopen(sndCardId, "r");
366         if (!file) {
367             LOG_PARA_INFO("Could reading %s property", sndCardId);
368             continue;
369         }
370         cardIdLen = fread(CardIdInfo, sizeof(char), sizeof(CardIdInfo) / sizeof(char), file);
371         if (fclose(file)) {
372             LOG_FUN_ERR("fclose(%s) failed", sndCardId);
373         }
374         if (cardIdLen == 0 || cardIdLen > sizeof(CardIdInfo) / sizeof(char)) {
375             continue;
376         }
377         CardIdInfo[cardIdLen - 1] = '\0';
378         LOG_PARA_INFO("card%d id:%s", card, CardIdInfo);
379         GetSpecifiedOutDev(&devOut[SND_OUT_SOUND_CARD_SPEAKER], card, CardIdInfo, SPEAKER_OUT_NAME);
380         GetSpecifiedOutDev(&devOut[SND_OUT_SOUND_CARD_HDMI], card, CardIdInfo, HDMI_OUT_NAME);
381         GetSpecifiedOutDev(&devOut[SND_OUT_SOUND_CARD_SPDIF], card, CardIdInfo, SPDIF_OUT_NAME);
382         GetSpecifiedOutDev(&devOut[SND_OUT_SOUND_CARD_BT], card, CardIdInfo, BT_OUT_NAME);
383     }
384     dumpdev_info("out", devOut, SND_OUT_SOUND_CARD_MAX);
385     return;
386 }
387 
GetOutDevInfo(int32_t index,struct DevInfo * devInfo)388 int32_t GetOutDevInfo(int32_t index, struct DevInfo* devInfo)
389 {
390     for (int32_t i = 0; i < SND_OUT_SOUND_CARD_MAX; i++) {
391         if (i == index) {
392             devInfo->card = devOut[i].card;
393             devInfo->device = devOut[i].device;
394             return true;
395         }
396     }
397     return false;
398 }
399 
GetInDevInfo(int32_t index,struct DevInfo * devInfo)400 int32_t GetInDevInfo(int32_t index, struct DevInfo* devInfo)
401 {
402     for (int32_t i = 0; i < SND_IN_SOUND_CARD_MAX; i++) {
403         if (i == index) {
404             devInfo->card = devIn[i].card;
405             devInfo->device = devIn[i].device;
406             return true;
407         }
408     }
409     return false;
410 }
411 
AudioRenderParamCheck(struct pcm_params * params,uint32_t param,uint32_t value,char * paramName,char * paramUnit)412 int32_t AudioRenderParamCheck(struct pcm_params *params, uint32_t param, uint32_t value,
413     char *paramName, char *paramUnit)
414 {
415     int32_t paramIsOk = true;
416 
417     uint32_t min = pcm_params_get_min(params, param);
418     uint32_t max = pcm_params_get_max(params, param);
419     if ((value < min) || (value > max))  {
420         LOG_FUN_ERR("device supports %s\t min=%u%s \t max=%u%s, pls check it.\n", paramName,
421             min, paramUnit, max, paramUnit);
422         paramIsOk = false;
423     }
424 
425     return paramIsOk;
426 }
427 
IsPlayable(uint32_t card,uint32_t device,uint32_t bits)428 int32_t IsPlayable(uint32_t card, uint32_t device, uint32_t bits)
429 {
430     struct pcm_params *params;
431     int32_t paramIsOk = true;
432 
433     params = pcm_params_get(card, device, PCM_OUT);
434     if (params == NULL) {
435         LOG_FUN_ERR("Unable to open PCM device (/dev/snd/pcmC%uD%up)\n", card, device);
436         return 0;
437     }
438     struct RenderParamcheckList g_RenderParamcheckList[] = {
439         {PCM_PARAM_RATE, g_renderPcmCfg.rate, "Sample rate", "Hz"},
440         {PCM_PARAM_CHANNELS, g_renderPcmCfg.channels, "Sample", "channels"},
441         {PCM_PARAM_SAMPLE_BITS, bits, "Bitrate", "bits"},
442         {PCM_PARAM_PERIOD_SIZE, g_renderPcmCfg.period_size, "Period size", "frames"},
443         {PCM_PARAM_PERIODS, g_renderPcmCfg.period_count, "Period count", "periods"},
444     };
445     int32_t checkNums = sizeof(g_RenderParamcheckList) / sizeof(struct RenderParamcheckList);
446     for (int32_t i = 0; i < checkNums; i++) {
447         paramIsOk &= AudioRenderParamCheck(params, g_RenderParamcheckList[i].param, g_RenderParamcheckList[i].value,
448             g_RenderParamcheckList[i].paramName, g_RenderParamcheckList[i].paramUnit);
449     }
450 
451     pcm_params_free(params);
452     return paramIsOk;
453 }
454 
initRenderFormat(uint32_t bits)455 void initRenderFormat(uint32_t bits)
456 {
457     switch (bits) {
458         case TINYALSAPCM_32_BIT:
459             g_renderPcmCfg.format = PCM_FORMAT_S32_LE;
460             return;
461         case TINYALSAPCM_24_BIT:
462             g_renderPcmCfg.format = PCM_FORMAT_S24_3LE;
463             return;
464         case TINYALSAPCM_8_BIT:
465             g_renderPcmCfg.format = PCM_FORMAT_S8;
466             return;
467         default:
468         case TINYALSAPCM_16_BIT:
469             g_renderPcmCfg.format = PCM_FORMAT_S16_LE;
470             return;
471     }
472 }
473 
TinyAlsaPlayParamInit(uint32_t channels,uint32_t rate,uint32_t periodSize,uint32_t periodCount)474 void TinyAlsaPlayParamInit(uint32_t channels, uint32_t rate,
475     uint32_t periodSize, uint32_t periodCount)
476 {
477     (void)memset_s(&g_renderPcmCfg, sizeof(struct pcm_config), 0, sizeof(struct pcm_config));
478     g_renderPcmCfg.channels = channels;
479     g_renderPcmCfg.rate = rate;
480     g_renderPcmCfg.period_size = periodSize;
481     g_renderPcmCfg.period_count = periodCount;
482 
483     g_renderPcmCfg.start_threshold = 0;
484     g_renderPcmCfg.stop_threshold = 0;
485     g_renderPcmCfg.silence_threshold = 0;
486     return;
487 }
488 
RenderSample(struct pcm ** pcm,struct PcmRenderParam * param)489 void RenderSample(struct pcm **pcm, struct PcmRenderParam *param)
490 {
491     TinyAlsaPlayParamInit(param->channels, param->rate, param->periodSize, param->periodCount);
492     initRenderFormat(param->bits);
493     if (!IsPlayable(param->card, param->device, param->bits)) {
494         return;
495     }
496 
497     *pcm = pcm_open(param->card, param->device, PCM_OUT, &g_renderPcmCfg);
498     if (((*pcm) == NULL) || !pcm_is_ready(*pcm)) {
499         LOG_FUN_ERR("Unable to open PCM device (/dev/snd/pcmC%uD%up):(%s)\n",
500             param->card, param->device, pcm_get_error(*pcm));
501         return;
502     }
503     LOG_FUN_ERR("Playing sample: %u ch, %u hz, %u\n", param->channels, param->rate, param->bits);
504 }
505 
CaptureSample(struct pcm ** pcm,struct PcmCaptureParam * param)506 uint32_t CaptureSample(struct pcm **pcm, struct PcmCaptureParam *param)
507 {
508     struct pcm_config config;
509     (void)memset_s(&config, sizeof(config), 0, sizeof(config));
510     config.channels = param->channels;
511     config.rate = param->rate;
512     config.period_size = param->periodSize;
513     config.period_count = param->periodCount;
514     config.format = param->format;
515     config.start_threshold = 0;
516     config.stop_threshold = 0;
517     config.silence_threshold = 0;
518     *pcm = pcm_open(param->card, param->device, PCM_IN, &config);
519     if (((*pcm) == NULL) || !pcm_is_ready(*pcm)) {
520         LOG_FUN_ERR("Unable to open PCM device (/dev/snd/pcmC%uD%uc):(%s)\n",
521             param->card, param->device, pcm_get_error(*pcm));
522         return -1;
523     }
524     return 0;
525 }
526