355 lines
8.5 KiB
C
Executable file
355 lines
8.5 KiB
C
Executable file
/*
|
|
*
|
|
*/
|
|
#define LOG_TAG "audio_hw_primary_homlet"
|
|
#define LOG_NDEBUG 0
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <linux/netlink.h>
|
|
#include <sys/select.h>
|
|
#include <signal.h>
|
|
#include <asm/types.h>
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <tinyalsa/asoundlib.h>
|
|
#include <cutils/log.h>
|
|
|
|
//#include "list.h"
|
|
|
|
/* Netlink allow 32 bus max */
|
|
#define NETLINK_PCM_SUNXI 31
|
|
/* maximum payload size, enough pcm xrun communcations */
|
|
#define MAX_PAYLOAD 1024
|
|
|
|
static struct sockaddr_nl src_addr, dest_addr;
|
|
static struct nlmsghdr *nlhdr = NULL;
|
|
static struct iovec iov;
|
|
static struct msghdr msg;
|
|
volatile sig_atomic_t running = 1;
|
|
|
|
#define SUNXI_MAX_CARDS 4
|
|
#define AUDIO_MAP_CNT 16
|
|
#define NAME_LEN 20
|
|
#define PATH_LEN 30
|
|
|
|
typedef struct name_map_t
|
|
{
|
|
char name[NAME_LEN];
|
|
int card_num;
|
|
int active;
|
|
}name_map;
|
|
|
|
static name_map audio_name_map[AUDIO_MAP_CNT] =
|
|
{
|
|
{"sndacx00codec", -1, 1},
|
|
{"sndhdmi", -1, 1},
|
|
{"sndspdif", -1, 0},
|
|
{"snddaudio2", -1, 1},
|
|
};
|
|
int int_map()
|
|
{
|
|
char path[PATH_LEN] = {0};
|
|
char name[NAME_LEN] = {0};
|
|
int index = 0;
|
|
int ret = -1;
|
|
int fd = -1;
|
|
for(; index < AUDIO_MAP_CNT; index++)
|
|
{
|
|
memset(path, 0, PATH_LEN);
|
|
memset(name, 0, NAME_LEN);
|
|
sprintf(path, "/proc/asound/card%d/id", index);
|
|
|
|
fd = open(path, O_RDONLY, 0);
|
|
if(fd < 0)
|
|
{return -1;}
|
|
|
|
ret = read(fd, name, NAME_LEN);
|
|
if(ret < 0)
|
|
{return -2;};
|
|
|
|
int innerindex = 0;
|
|
for(; innerindex < AUDIO_MAP_CNT; innerindex++)
|
|
{
|
|
if((strlen(audio_name_map[innerindex].name) != 0) && (strncmp(audio_name_map[innerindex].name, name, strlen(audio_name_map[innerindex].name)) == 0))
|
|
{
|
|
audio_name_map[innerindex].card_num = index;
|
|
}
|
|
else
|
|
{}
|
|
}
|
|
close(fd);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int ret_codec_card()
|
|
{
|
|
return audio_name_map[0].card_num;
|
|
}
|
|
int ret_hdmi_card()
|
|
{
|
|
return audio_name_map[1].card_num;
|
|
}
|
|
int ret_daudio2_card()
|
|
{
|
|
return audio_name_map[3].card_num;
|
|
}
|
|
|
|
/* plat configure, should be using as config load dymic */
|
|
int pcm_plat[SUNXI_MAX_CARDS] = {-1, -1, -1, -1};
|
|
|
|
void init_pcm_plat()
|
|
{
|
|
pcm_plat[1] = ret_hdmi_card();
|
|
pcm_plat[3] = ret_codec_card();
|
|
pcm_plat[2] = ret_daudio2_card();
|
|
}
|
|
|
|
struct pcm *aw_pcm[2 * SUNXI_MAX_CARDS];
|
|
|
|
static int pcm_thread_process(int card, int dir, int open, struct pcm_config *config)
|
|
{
|
|
int device = 0;
|
|
struct pcm *pcm;
|
|
int prepare_error;
|
|
/* if open */
|
|
if (open)
|
|
{
|
|
pcm = pcm_open(card, device, dir, config);
|
|
if (!pcm || !pcm_is_ready(pcm)) {
|
|
fprintf(stderr, "Unable to open PCM device %d (%s)\n",
|
|
device, pcm_get_error(pcm));
|
|
return 0;
|
|
}
|
|
|
|
prepare_error = pcm_prepare(pcm);
|
|
if (prepare_error) {
|
|
pcm_close(pcm);
|
|
return prepare_error;
|
|
}
|
|
if (dir)
|
|
aw_pcm[2 * card] = pcm;
|
|
else
|
|
aw_pcm[2 * card + 1] = pcm;
|
|
} else {
|
|
if (dir) {
|
|
pcm = aw_pcm[2 * card];
|
|
aw_pcm[2 * card] = NULL;
|
|
}
|
|
else {
|
|
pcm = aw_pcm[2 * card + 1];
|
|
aw_pcm[2 * card + 1] = NULL;
|
|
}
|
|
if(pcm != NULL)
|
|
pcm_close(pcm);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void pcm_event_process(char *buf)
|
|
{
|
|
char *widget_str;
|
|
char *tmp_str;
|
|
int count = 0;
|
|
int opt_card, opt_open, opt_dir;
|
|
int opt_format;
|
|
int num;
|
|
struct pcm_config config;
|
|
|
|
printf("%s\n", buf);
|
|
|
|
tmp_str = strtok(buf, ":=/");
|
|
|
|
while (tmp_str != NULL)
|
|
{
|
|
count ++;
|
|
switch(count) {
|
|
case 2:
|
|
widget_str = tmp_str;
|
|
if (strncmp(widget_str, "I2S", 3) == 0) {
|
|
num = widget_str[3] - '0';
|
|
opt_card = pcm_plat[num];
|
|
if(opt_card < 0 )
|
|
{ return ; }
|
|
if (strncmp(widget_str+4, "IN", 2) == 0)
|
|
opt_dir = PCM_IN;
|
|
else
|
|
opt_dir = PCM_OUT;
|
|
} else {
|
|
return ;
|
|
}
|
|
break;
|
|
case 3:
|
|
opt_open = atoi(tmp_str);
|
|
break;
|
|
case 5:
|
|
config.channels = atoi(tmp_str);
|
|
break;
|
|
case 6:
|
|
config.rate = atoi(tmp_str);
|
|
break;
|
|
case 7:
|
|
/* alsa format define should be do some convert */
|
|
opt_format = atoi(tmp_str);
|
|
switch (opt_format) {
|
|
case 0 :
|
|
config.format = PCM_FORMAT_S8;
|
|
break;
|
|
case 2:
|
|
config.format = PCM_FORMAT_S16_LE;
|
|
break;
|
|
case 6:
|
|
config.format = PCM_FORMAT_S24_LE;
|
|
break;
|
|
case 10:
|
|
config.format = PCM_FORMAT_S32_LE;
|
|
break;
|
|
default:
|
|
printf("not support format type\n");
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
tmp_str = strtok(NULL, ":=/");
|
|
}
|
|
|
|
ALOGD("card =%d, dir=%d, open=%d, config=%d/%d/%d\n",
|
|
opt_card, opt_dir, opt_open, config.channels, config.rate, config.format);
|
|
|
|
config.period_size = 1024;
|
|
config.period_count = 4;
|
|
config.start_threshold = 0;
|
|
config.stop_threshold = 0;
|
|
config.silence_threshold = 0;
|
|
|
|
pcm_thread_process(opt_card, opt_dir, opt_open, &config);
|
|
return ;
|
|
}
|
|
|
|
/*void signal_handler(int signum)
|
|
{
|
|
running = 0;
|
|
}*/
|
|
|
|
void* pcm_event_thread(void* arg)
|
|
{
|
|
int_map();
|
|
init_pcm_plat();
|
|
int ret;
|
|
int sockfd;
|
|
fd_set readset;
|
|
struct timeval timeout;
|
|
int count = 0;
|
|
int checkret = 0;
|
|
ALOGD("pcm_event_thread.");
|
|
|
|
//signal(SIGTERM, signal_handler);
|
|
//signal(SIGINT, signal_handler);
|
|
//signal(SIGHUP, signal_handler);
|
|
|
|
sockfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_PCM_SUNXI);
|
|
if (sockfd < 0) {
|
|
perror("socket create failed\n");
|
|
return 0;
|
|
}
|
|
|
|
memset(&src_addr, 0, sizeof(src_addr));
|
|
src_addr.nl_family = AF_NETLINK;
|
|
src_addr.nl_pid = getpid(); /* self pid */
|
|
|
|
ret = bind(sockfd, (struct sockaddr *)&src_addr, sizeof(src_addr));
|
|
if (ret > 0) {
|
|
perror("socket bind failed\n");
|
|
return 0;
|
|
}
|
|
|
|
memset(&dest_addr, 0, sizeof(dest_addr));
|
|
dest_addr.nl_family = AF_NETLINK;
|
|
dest_addr.nl_pid = 0; /* for linux kernel */
|
|
dest_addr.nl_groups = 0; /* unicast */
|
|
|
|
nlhdr = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
|
|
memset(nlhdr, 0, NLMSG_SPACE(MAX_PAYLOAD));
|
|
nlhdr->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
|
|
nlhdr->nlmsg_pid = getpid();
|
|
nlhdr->nlmsg_flags = 0;
|
|
|
|
/* start the pcm_netlink unicast */
|
|
strcpy(NLMSG_DATA(nlhdr), "start");
|
|
|
|
iov.iov_base = (void *)nlhdr;
|
|
iov.iov_len = nlhdr->nlmsg_len;
|
|
msg.msg_name = (void *)&dest_addr;
|
|
msg.msg_namelen = sizeof(dest_addr);
|
|
msg.msg_iov = &iov;
|
|
msg.msg_iovlen = 1;
|
|
|
|
fprintf(stdout, "Start pcm xrun debug test\n");
|
|
sendmsg(sockfd, &msg, 0);
|
|
|
|
while(running) {
|
|
FD_ZERO(&readset);
|
|
FD_SET(sockfd, &readset);
|
|
timeout.tv_sec = 1;
|
|
timeout.tv_usec = 0;
|
|
|
|
ret = select(FD_SETSIZE, &readset, NULL, NULL, &timeout);
|
|
if (ret < 0) {
|
|
perror("select failed\n");
|
|
break;
|
|
} else if (ret = 0) {
|
|
/* timeout */
|
|
continue;
|
|
} else {
|
|
if (FD_ISSET(sockfd, &readset)) {
|
|
ret = recvmsg(sockfd, &msg, 0);
|
|
if (ret < 0) {
|
|
/* error recv msg */
|
|
return 0;
|
|
} else {
|
|
pcm_event_process(NLMSG_DATA(nlhdr));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
strcpy(NLMSG_DATA(nlhdr), "close");
|
|
fprintf(stdout, "Close sunxi netlink app thread\n");
|
|
sendmsg(sockfd, &msg, 0);
|
|
|
|
sleep(1);
|
|
close(sockfd);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static pthread_t event_thread;
|
|
|
|
// warning: no protect!!!
|
|
int pcm_event_thread_start() {
|
|
ALOGD("pcm_event_thread start.");
|
|
if (pthread_create(&event_thread, NULL, pcm_event_thread, NULL)){
|
|
ALOGE("%s() pthread_create usb_to_output_thread failed!!!", __func__);
|
|
return -1;
|
|
}
|
|
pthread_detach(event_thread);
|
|
return 0;
|
|
}
|
|
|
|
void pcm_event_thread_stop() {
|
|
ALOGD("pcm_event_thread stop.");
|
|
running = 0;
|
|
}
|
|
|
|
int main1() {
|
|
pcm_event_thread(NULL);
|
|
return 0;
|
|
}
|
|
|