1d5ac70f0Sopenharmony_ci/* 2d5ac70f0Sopenharmony_ci * PCM - Direct Stream Mixing 3d5ac70f0Sopenharmony_ci * Copyright (c) 2003 by Jaroslav Kysela <perex@perex.cz> 4d5ac70f0Sopenharmony_ci * 5d5ac70f0Sopenharmony_ci * 6d5ac70f0Sopenharmony_ci * This library is free software; you can redistribute it and/or modify 7d5ac70f0Sopenharmony_ci * it under the terms of the GNU Lesser General Public License as 8d5ac70f0Sopenharmony_ci * published by the Free Software Foundation; either version 2.1 of 9d5ac70f0Sopenharmony_ci * the License, or (at your option) any later version. 10d5ac70f0Sopenharmony_ci * 11d5ac70f0Sopenharmony_ci * This program is distributed in the hope that it will be useful, 12d5ac70f0Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 13d5ac70f0Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14d5ac70f0Sopenharmony_ci * GNU Lesser General Public License for more details. 15d5ac70f0Sopenharmony_ci * 16d5ac70f0Sopenharmony_ci * You should have received a copy of the GNU Lesser General Public 17d5ac70f0Sopenharmony_ci * License along with this library; if not, write to the Free Software 18d5ac70f0Sopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19d5ac70f0Sopenharmony_ci * 20d5ac70f0Sopenharmony_ci */ 21d5ac70f0Sopenharmony_ci 22d5ac70f0Sopenharmony_ci#include "pcm_local.h" 23d5ac70f0Sopenharmony_ci#include <stdio.h> 24d5ac70f0Sopenharmony_ci#include <stdlib.h> 25d5ac70f0Sopenharmony_ci#include <stddef.h> 26d5ac70f0Sopenharmony_ci#include <unistd.h> 27d5ac70f0Sopenharmony_ci#include <signal.h> 28d5ac70f0Sopenharmony_ci#include <string.h> 29d5ac70f0Sopenharmony_ci#include <fcntl.h> 30d5ac70f0Sopenharmony_ci#include <ctype.h> 31d5ac70f0Sopenharmony_ci#include <grp.h> 32d5ac70f0Sopenharmony_ci#include <sys/ioctl.h> 33d5ac70f0Sopenharmony_ci#include <sys/mman.h> 34d5ac70f0Sopenharmony_ci#include <poll.h> 35d5ac70f0Sopenharmony_ci#include <sys/shm.h> 36d5ac70f0Sopenharmony_ci#include <sys/sem.h> 37d5ac70f0Sopenharmony_ci#include <sys/wait.h> 38d5ac70f0Sopenharmony_ci#include <sys/socket.h> 39d5ac70f0Sopenharmony_ci#include <sys/stat.h> 40d5ac70f0Sopenharmony_ci#include <sys/un.h> 41d5ac70f0Sopenharmony_ci#include <sys/mman.h> 42d5ac70f0Sopenharmony_ci#include "pcm_direct.h" 43d5ac70f0Sopenharmony_ci 44d5ac70f0Sopenharmony_ci/* 45d5ac70f0Sopenharmony_ci * 46d5ac70f0Sopenharmony_ci */ 47d5ac70f0Sopenharmony_ci 48d5ac70f0Sopenharmony_ci#if !defined(__OpenBSD__) && !defined(__DragonFly__) && !defined(__ANDROID__) 49d5ac70f0Sopenharmony_ciunion semun { 50d5ac70f0Sopenharmony_ci int val; /* Value for SETVAL */ 51d5ac70f0Sopenharmony_ci struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ 52d5ac70f0Sopenharmony_ci unsigned short *array; /* Array for GETALL, SETALL */ 53d5ac70f0Sopenharmony_ci#if defined(__linux__) 54d5ac70f0Sopenharmony_ci struct seminfo *__buf; /* Buffer for IPC_INFO (Linux specific) */ 55d5ac70f0Sopenharmony_ci#endif 56d5ac70f0Sopenharmony_ci}; 57d5ac70f0Sopenharmony_ci#endif 58d5ac70f0Sopenharmony_ci 59d5ac70f0Sopenharmony_ci/* 60d5ac70f0Sopenharmony_ci * FIXME: 61d5ac70f0Sopenharmony_ci * add possibility to use futexes here 62d5ac70f0Sopenharmony_ci */ 63d5ac70f0Sopenharmony_ci 64d5ac70f0Sopenharmony_ciint snd_pcm_direct_semaphore_create_or_connect(snd_pcm_direct_t *dmix) 65d5ac70f0Sopenharmony_ci{ 66d5ac70f0Sopenharmony_ci union semun s; 67d5ac70f0Sopenharmony_ci struct semid_ds buf; 68d5ac70f0Sopenharmony_ci int i; 69d5ac70f0Sopenharmony_ci 70d5ac70f0Sopenharmony_ci dmix->semid = semget(dmix->ipc_key, DIRECT_IPC_SEMS, 71d5ac70f0Sopenharmony_ci IPC_CREAT | dmix->ipc_perm); 72d5ac70f0Sopenharmony_ci if (dmix->semid < 0) 73d5ac70f0Sopenharmony_ci return -errno; 74d5ac70f0Sopenharmony_ci if (dmix->ipc_gid < 0) 75d5ac70f0Sopenharmony_ci return 0; 76d5ac70f0Sopenharmony_ci for (i = 0; i < DIRECT_IPC_SEMS; i++) { 77d5ac70f0Sopenharmony_ci s.buf = &buf; 78d5ac70f0Sopenharmony_ci if (semctl(dmix->semid, i, IPC_STAT, s) < 0) { 79d5ac70f0Sopenharmony_ci int err = -errno; 80d5ac70f0Sopenharmony_ci snd_pcm_direct_semaphore_discard(dmix); 81d5ac70f0Sopenharmony_ci return err; 82d5ac70f0Sopenharmony_ci } 83d5ac70f0Sopenharmony_ci buf.sem_perm.gid = dmix->ipc_gid; 84d5ac70f0Sopenharmony_ci s.buf = &buf; 85d5ac70f0Sopenharmony_ci semctl(dmix->semid, i, IPC_SET, s); 86d5ac70f0Sopenharmony_ci } 87d5ac70f0Sopenharmony_ci return 0; 88d5ac70f0Sopenharmony_ci} 89d5ac70f0Sopenharmony_ci 90d5ac70f0Sopenharmony_cistatic unsigned int snd_pcm_direct_magic(snd_pcm_direct_t *dmix) 91d5ac70f0Sopenharmony_ci{ 92d5ac70f0Sopenharmony_ci if (!dmix->direct_memory_access) 93d5ac70f0Sopenharmony_ci return 0xa15ad300 + sizeof(snd_pcm_direct_share_t); 94d5ac70f0Sopenharmony_ci else 95d5ac70f0Sopenharmony_ci return 0xb15ad300 + sizeof(snd_pcm_direct_share_t); 96d5ac70f0Sopenharmony_ci} 97d5ac70f0Sopenharmony_ci 98d5ac70f0Sopenharmony_ci/* 99d5ac70f0Sopenharmony_ci * global shared memory area 100d5ac70f0Sopenharmony_ci */ 101d5ac70f0Sopenharmony_ci 102d5ac70f0Sopenharmony_ciint snd_pcm_direct_shm_create_or_connect(snd_pcm_direct_t *dmix) 103d5ac70f0Sopenharmony_ci{ 104d5ac70f0Sopenharmony_ci struct shmid_ds buf; 105d5ac70f0Sopenharmony_ci int tmpid, err, first_instance = 0; 106d5ac70f0Sopenharmony_ci 107d5ac70f0Sopenharmony_ciretryget: 108d5ac70f0Sopenharmony_ci dmix->shmid = shmget(dmix->ipc_key, sizeof(snd_pcm_direct_share_t), 109d5ac70f0Sopenharmony_ci dmix->ipc_perm); 110d5ac70f0Sopenharmony_ci if (dmix->shmid < 0 && errno == ENOENT) { 111d5ac70f0Sopenharmony_ci if ((dmix->shmid = shmget(dmix->ipc_key, sizeof(snd_pcm_direct_share_t), 112d5ac70f0Sopenharmony_ci IPC_CREAT | IPC_EXCL | dmix->ipc_perm)) != -1) 113d5ac70f0Sopenharmony_ci first_instance = 1; 114d5ac70f0Sopenharmony_ci else if (errno == EEXIST) 115d5ac70f0Sopenharmony_ci goto retryget; 116d5ac70f0Sopenharmony_ci } 117d5ac70f0Sopenharmony_ci err = -errno; 118d5ac70f0Sopenharmony_ci if (dmix->shmid < 0) { 119d5ac70f0Sopenharmony_ci if (errno == EINVAL) 120d5ac70f0Sopenharmony_ci if ((tmpid = shmget(dmix->ipc_key, 0, dmix->ipc_perm)) != -1) 121d5ac70f0Sopenharmony_ci if (!shmctl(tmpid, IPC_STAT, &buf)) 122d5ac70f0Sopenharmony_ci if (!buf.shm_nattch) 123d5ac70f0Sopenharmony_ci /* no users so destroy the segment */ 124d5ac70f0Sopenharmony_ci if (!shmctl(tmpid, IPC_RMID, NULL)) 125d5ac70f0Sopenharmony_ci goto retryget; 126d5ac70f0Sopenharmony_ci return err; 127d5ac70f0Sopenharmony_ci } 128d5ac70f0Sopenharmony_ci dmix->shmptr = shmat(dmix->shmid, 0, 0); 129d5ac70f0Sopenharmony_ci if (dmix->shmptr == (void *) -1) { 130d5ac70f0Sopenharmony_ci err = -errno; 131d5ac70f0Sopenharmony_ci snd_pcm_direct_shm_discard(dmix); 132d5ac70f0Sopenharmony_ci return err; 133d5ac70f0Sopenharmony_ci } 134d5ac70f0Sopenharmony_ci mlock(dmix->shmptr, sizeof(snd_pcm_direct_share_t)); 135d5ac70f0Sopenharmony_ci if (shmctl(dmix->shmid, IPC_STAT, &buf) < 0) { 136d5ac70f0Sopenharmony_ci err = -errno; 137d5ac70f0Sopenharmony_ci snd_pcm_direct_shm_discard(dmix); 138d5ac70f0Sopenharmony_ci return err; 139d5ac70f0Sopenharmony_ci } 140d5ac70f0Sopenharmony_ci if (first_instance) { /* we're the first user, clear the segment */ 141d5ac70f0Sopenharmony_ci memset(dmix->shmptr, 0, sizeof(snd_pcm_direct_share_t)); 142d5ac70f0Sopenharmony_ci if (dmix->ipc_gid >= 0) { 143d5ac70f0Sopenharmony_ci buf.shm_perm.gid = dmix->ipc_gid; 144d5ac70f0Sopenharmony_ci shmctl(dmix->shmid, IPC_SET, &buf); 145d5ac70f0Sopenharmony_ci } 146d5ac70f0Sopenharmony_ci dmix->shmptr->magic = snd_pcm_direct_magic(dmix); 147d5ac70f0Sopenharmony_ci return 1; 148d5ac70f0Sopenharmony_ci } else { 149d5ac70f0Sopenharmony_ci if (dmix->shmptr->magic != snd_pcm_direct_magic(dmix)) { 150d5ac70f0Sopenharmony_ci snd_pcm_direct_shm_discard(dmix); 151d5ac70f0Sopenharmony_ci return -EINVAL; 152d5ac70f0Sopenharmony_ci } 153d5ac70f0Sopenharmony_ci } 154d5ac70f0Sopenharmony_ci return 0; 155d5ac70f0Sopenharmony_ci} 156d5ac70f0Sopenharmony_ci 157d5ac70f0Sopenharmony_ci/* discard shared memory */ 158d5ac70f0Sopenharmony_ci/* 159d5ac70f0Sopenharmony_ci * Define snd_* functions to be used in server. 160d5ac70f0Sopenharmony_ci * Since objects referred in a plugin can be released dynamically, a forked 161d5ac70f0Sopenharmony_ci * server should have statically linked functions. 162d5ac70f0Sopenharmony_ci * (e.g. Novell bugzilla #105772) 163d5ac70f0Sopenharmony_ci */ 164d5ac70f0Sopenharmony_cistatic int _snd_pcm_direct_shm_discard(snd_pcm_direct_t *dmix) 165d5ac70f0Sopenharmony_ci{ 166d5ac70f0Sopenharmony_ci struct shmid_ds buf; 167d5ac70f0Sopenharmony_ci int ret = 0; 168d5ac70f0Sopenharmony_ci 169d5ac70f0Sopenharmony_ci if (dmix->shmid < 0) 170d5ac70f0Sopenharmony_ci return -EINVAL; 171d5ac70f0Sopenharmony_ci if (dmix->shmptr != (void *) -1 && shmdt(dmix->shmptr) < 0) 172d5ac70f0Sopenharmony_ci return -errno; 173d5ac70f0Sopenharmony_ci dmix->shmptr = (void *) -1; 174d5ac70f0Sopenharmony_ci if (shmctl(dmix->shmid, IPC_STAT, &buf) < 0) 175d5ac70f0Sopenharmony_ci return -errno; 176d5ac70f0Sopenharmony_ci if (buf.shm_nattch == 0) { /* we're the last user, destroy the segment */ 177d5ac70f0Sopenharmony_ci if (shmctl(dmix->shmid, IPC_RMID, NULL) < 0) 178d5ac70f0Sopenharmony_ci return -errno; 179d5ac70f0Sopenharmony_ci ret = 1; 180d5ac70f0Sopenharmony_ci } 181d5ac70f0Sopenharmony_ci dmix->shmid = -1; 182d5ac70f0Sopenharmony_ci return ret; 183d5ac70f0Sopenharmony_ci} 184d5ac70f0Sopenharmony_ci 185d5ac70f0Sopenharmony_ci/* ... and an exported version */ 186d5ac70f0Sopenharmony_ciint snd_pcm_direct_shm_discard(snd_pcm_direct_t *dmix) 187d5ac70f0Sopenharmony_ci{ 188d5ac70f0Sopenharmony_ci return _snd_pcm_direct_shm_discard(dmix); 189d5ac70f0Sopenharmony_ci} 190d5ac70f0Sopenharmony_ci 191d5ac70f0Sopenharmony_ci/* 192d5ac70f0Sopenharmony_ci * server side 193d5ac70f0Sopenharmony_ci */ 194d5ac70f0Sopenharmony_ci 195d5ac70f0Sopenharmony_cistatic int get_tmp_name(char *filename, size_t size) 196d5ac70f0Sopenharmony_ci{ 197d5ac70f0Sopenharmony_ci struct timeval tv; 198d5ac70f0Sopenharmony_ci 199d5ac70f0Sopenharmony_ci gettimeofday(&tv, NULL); 200d5ac70f0Sopenharmony_ci snprintf(filename, size, TMPDIR "/alsa-dmix-%i-%li-%li", (int)getpid(), (long)tv.tv_sec, (long)tv.tv_usec); 201d5ac70f0Sopenharmony_ci filename[size-1] = '\0'; 202d5ac70f0Sopenharmony_ci return 0; 203d5ac70f0Sopenharmony_ci} 204d5ac70f0Sopenharmony_ci 205d5ac70f0Sopenharmony_cistatic int make_local_socket(const char *filename, int server, mode_t ipc_perm, int ipc_gid) 206d5ac70f0Sopenharmony_ci{ 207d5ac70f0Sopenharmony_ci size_t l = strlen(filename); 208d5ac70f0Sopenharmony_ci size_t size = offsetof(struct sockaddr_un, sun_path) + l; 209d5ac70f0Sopenharmony_ci struct sockaddr_un *addr = alloca(size); 210d5ac70f0Sopenharmony_ci int sock; 211d5ac70f0Sopenharmony_ci 212d5ac70f0Sopenharmony_ci sock = socket(PF_LOCAL, SOCK_STREAM, 0); 213d5ac70f0Sopenharmony_ci if (sock < 0) { 214d5ac70f0Sopenharmony_ci int result = -errno; 215d5ac70f0Sopenharmony_ci SYSERR("socket failed"); 216d5ac70f0Sopenharmony_ci return result; 217d5ac70f0Sopenharmony_ci } 218d5ac70f0Sopenharmony_ci 219d5ac70f0Sopenharmony_ci if (server) 220d5ac70f0Sopenharmony_ci unlink(filename); 221d5ac70f0Sopenharmony_ci memset(addr, 0, size); /* make valgrind happy */ 222d5ac70f0Sopenharmony_ci addr->sun_family = AF_LOCAL; 223d5ac70f0Sopenharmony_ci memcpy(addr->sun_path, filename, l); 224d5ac70f0Sopenharmony_ci 225d5ac70f0Sopenharmony_ci if (server) { 226d5ac70f0Sopenharmony_ci if (bind(sock, (struct sockaddr *) addr, size) < 0) { 227d5ac70f0Sopenharmony_ci int result = -errno; 228d5ac70f0Sopenharmony_ci SYSERR("bind failed: %s", filename); 229d5ac70f0Sopenharmony_ci close(sock); 230d5ac70f0Sopenharmony_ci return result; 231d5ac70f0Sopenharmony_ci } else { 232d5ac70f0Sopenharmony_ci if (chmod(filename, ipc_perm) < 0) { 233d5ac70f0Sopenharmony_ci int result = -errno; 234d5ac70f0Sopenharmony_ci SYSERR("chmod failed: %s", filename); 235d5ac70f0Sopenharmony_ci close(sock); 236d5ac70f0Sopenharmony_ci unlink(filename); 237d5ac70f0Sopenharmony_ci return result; 238d5ac70f0Sopenharmony_ci } 239d5ac70f0Sopenharmony_ci if (chown(filename, -1, ipc_gid) < 0) { 240d5ac70f0Sopenharmony_ci#if 0 /* it's not fatal */ 241d5ac70f0Sopenharmony_ci int result = -errno; 242d5ac70f0Sopenharmony_ci SYSERR("chown failed: %s", filename); 243d5ac70f0Sopenharmony_ci close(sock); 244d5ac70f0Sopenharmony_ci unlink(filename); 245d5ac70f0Sopenharmony_ci return result; 246d5ac70f0Sopenharmony_ci#endif 247d5ac70f0Sopenharmony_ci } 248d5ac70f0Sopenharmony_ci } 249d5ac70f0Sopenharmony_ci } else { 250d5ac70f0Sopenharmony_ci if (connect(sock, (struct sockaddr *) addr, size) < 0) { 251d5ac70f0Sopenharmony_ci int result = -errno; 252d5ac70f0Sopenharmony_ci SYSERR("connect failed: %s", filename); 253d5ac70f0Sopenharmony_ci close(sock); 254d5ac70f0Sopenharmony_ci return result; 255d5ac70f0Sopenharmony_ci } 256d5ac70f0Sopenharmony_ci } 257d5ac70f0Sopenharmony_ci return sock; 258d5ac70f0Sopenharmony_ci} 259d5ac70f0Sopenharmony_ci 260d5ac70f0Sopenharmony_ci#if 0 261d5ac70f0Sopenharmony_ci#define SERVER_JOB_DEBUG 262d5ac70f0Sopenharmony_ci#define server_printf(fmt, args...) printf(fmt, ##args) 263d5ac70f0Sopenharmony_ci#else 264d5ac70f0Sopenharmony_ci#undef SERVER_JOB_DEBUG 265d5ac70f0Sopenharmony_ci#define server_printf(fmt, args...) /* nothing */ 266d5ac70f0Sopenharmony_ci#endif 267d5ac70f0Sopenharmony_ci 268d5ac70f0Sopenharmony_cistatic snd_pcm_direct_t *server_job_dmix; 269d5ac70f0Sopenharmony_ci 270d5ac70f0Sopenharmony_cistatic void server_cleanup(snd_pcm_direct_t *dmix) 271d5ac70f0Sopenharmony_ci{ 272d5ac70f0Sopenharmony_ci close(dmix->server_fd); 273d5ac70f0Sopenharmony_ci close(dmix->hw_fd); 274d5ac70f0Sopenharmony_ci if (dmix->server_free) 275d5ac70f0Sopenharmony_ci dmix->server_free(dmix); 276d5ac70f0Sopenharmony_ci unlink(dmix->shmptr->socket_name); 277d5ac70f0Sopenharmony_ci _snd_pcm_direct_shm_discard(dmix); 278d5ac70f0Sopenharmony_ci snd_pcm_direct_semaphore_discard(dmix); 279d5ac70f0Sopenharmony_ci} 280d5ac70f0Sopenharmony_ci 281d5ac70f0Sopenharmony_cistatic void server_job_signal(int sig ATTRIBUTE_UNUSED) 282d5ac70f0Sopenharmony_ci{ 283d5ac70f0Sopenharmony_ci snd_pcm_direct_semaphore_down(server_job_dmix, DIRECT_IPC_SEM_CLIENT); 284d5ac70f0Sopenharmony_ci server_cleanup(server_job_dmix); 285d5ac70f0Sopenharmony_ci server_printf("DIRECT SERVER EXIT - SIGNAL\n"); 286d5ac70f0Sopenharmony_ci _exit(EXIT_SUCCESS); 287d5ac70f0Sopenharmony_ci} 288d5ac70f0Sopenharmony_ci 289d5ac70f0Sopenharmony_ci/* This is a copy from ../socket.c, provided here only for a server job 290d5ac70f0Sopenharmony_ci * (see the comment above) 291d5ac70f0Sopenharmony_ci */ 292d5ac70f0Sopenharmony_cistatic int _snd_send_fd(int sock, void *data, size_t len, int fd) 293d5ac70f0Sopenharmony_ci{ 294d5ac70f0Sopenharmony_ci int ret; 295d5ac70f0Sopenharmony_ci size_t cmsg_len = CMSG_LEN(sizeof(int)); 296d5ac70f0Sopenharmony_ci struct cmsghdr *cmsg = alloca(cmsg_len); 297d5ac70f0Sopenharmony_ci int *fds = (int *) CMSG_DATA(cmsg); 298d5ac70f0Sopenharmony_ci struct msghdr msghdr; 299d5ac70f0Sopenharmony_ci struct iovec vec; 300d5ac70f0Sopenharmony_ci 301d5ac70f0Sopenharmony_ci vec.iov_base = (void *)&data; 302d5ac70f0Sopenharmony_ci vec.iov_len = len; 303d5ac70f0Sopenharmony_ci 304d5ac70f0Sopenharmony_ci cmsg->cmsg_len = cmsg_len; 305d5ac70f0Sopenharmony_ci cmsg->cmsg_level = SOL_SOCKET; 306d5ac70f0Sopenharmony_ci cmsg->cmsg_type = SCM_RIGHTS; 307d5ac70f0Sopenharmony_ci *fds = fd; 308d5ac70f0Sopenharmony_ci 309d5ac70f0Sopenharmony_ci msghdr.msg_name = NULL; 310d5ac70f0Sopenharmony_ci msghdr.msg_namelen = 0; 311d5ac70f0Sopenharmony_ci msghdr.msg_iov = &vec; 312d5ac70f0Sopenharmony_ci msghdr.msg_iovlen = 1; 313d5ac70f0Sopenharmony_ci msghdr.msg_control = cmsg; 314d5ac70f0Sopenharmony_ci msghdr.msg_controllen = cmsg_len; 315d5ac70f0Sopenharmony_ci msghdr.msg_flags = 0; 316d5ac70f0Sopenharmony_ci 317d5ac70f0Sopenharmony_ci ret = sendmsg(sock, &msghdr, 0 ); 318d5ac70f0Sopenharmony_ci if (ret < 0) 319d5ac70f0Sopenharmony_ci return -errno; 320d5ac70f0Sopenharmony_ci return ret; 321d5ac70f0Sopenharmony_ci} 322d5ac70f0Sopenharmony_ci 323d5ac70f0Sopenharmony_cistatic void server_job(snd_pcm_direct_t *dmix) 324d5ac70f0Sopenharmony_ci{ 325d5ac70f0Sopenharmony_ci int ret, sck, i; 326d5ac70f0Sopenharmony_ci int max = 128, current = 0; 327d5ac70f0Sopenharmony_ci struct pollfd pfds[max + 1]; 328d5ac70f0Sopenharmony_ci 329d5ac70f0Sopenharmony_ci server_job_dmix = dmix; 330d5ac70f0Sopenharmony_ci /* don't allow to be killed */ 331d5ac70f0Sopenharmony_ci signal(SIGHUP, server_job_signal); 332d5ac70f0Sopenharmony_ci signal(SIGQUIT, server_job_signal); 333d5ac70f0Sopenharmony_ci signal(SIGTERM, server_job_signal); 334d5ac70f0Sopenharmony_ci signal(SIGKILL, server_job_signal); 335d5ac70f0Sopenharmony_ci /* close all files to free resources */ 336d5ac70f0Sopenharmony_ci i = sysconf(_SC_OPEN_MAX); 337d5ac70f0Sopenharmony_ci#ifdef SERVER_JOB_DEBUG 338d5ac70f0Sopenharmony_ci while (--i >= 3) { 339d5ac70f0Sopenharmony_ci#else 340d5ac70f0Sopenharmony_ci while (--i >= 0) { 341d5ac70f0Sopenharmony_ci#endif 342d5ac70f0Sopenharmony_ci if (i != dmix->server_fd && i != dmix->hw_fd) 343d5ac70f0Sopenharmony_ci close(i); 344d5ac70f0Sopenharmony_ci } 345d5ac70f0Sopenharmony_ci 346d5ac70f0Sopenharmony_ci /* detach from parent */ 347d5ac70f0Sopenharmony_ci setsid(); 348d5ac70f0Sopenharmony_ci 349d5ac70f0Sopenharmony_ci pfds[0].fd = dmix->server_fd; 350d5ac70f0Sopenharmony_ci pfds[0].events = POLLIN | POLLERR | POLLHUP; 351d5ac70f0Sopenharmony_ci 352d5ac70f0Sopenharmony_ci server_printf("DIRECT SERVER STARTED\n"); 353d5ac70f0Sopenharmony_ci while (1) { 354d5ac70f0Sopenharmony_ci ret = poll(pfds, current + 1, 500); 355d5ac70f0Sopenharmony_ci server_printf("DIRECT SERVER: poll ret = %i, revents[0] = 0x%x, errno = %i\n", ret, pfds[0].revents, errno); 356d5ac70f0Sopenharmony_ci if (ret < 0) { 357d5ac70f0Sopenharmony_ci if (errno == EINTR) 358d5ac70f0Sopenharmony_ci continue; 359d5ac70f0Sopenharmony_ci /* some error */ 360d5ac70f0Sopenharmony_ci break; 361d5ac70f0Sopenharmony_ci } 362d5ac70f0Sopenharmony_ci if (ret == 0 || (pfds[0].revents & (POLLERR | POLLHUP))) { /* timeout or error? */ 363d5ac70f0Sopenharmony_ci struct shmid_ds buf; 364d5ac70f0Sopenharmony_ci snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT); 365d5ac70f0Sopenharmony_ci if (shmctl(dmix->shmid, IPC_STAT, &buf) < 0) { 366d5ac70f0Sopenharmony_ci _snd_pcm_direct_shm_discard(dmix); 367d5ac70f0Sopenharmony_ci snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT); 368d5ac70f0Sopenharmony_ci continue; 369d5ac70f0Sopenharmony_ci } 370d5ac70f0Sopenharmony_ci server_printf("DIRECT SERVER: nattch = %i\n", (int)buf.shm_nattch); 371d5ac70f0Sopenharmony_ci if (buf.shm_nattch == 1) /* server is the last user, exit */ 372d5ac70f0Sopenharmony_ci break; 373d5ac70f0Sopenharmony_ci snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT); 374d5ac70f0Sopenharmony_ci continue; 375d5ac70f0Sopenharmony_ci } 376d5ac70f0Sopenharmony_ci if (pfds[0].revents & POLLIN) { 377d5ac70f0Sopenharmony_ci ret--; 378d5ac70f0Sopenharmony_ci sck = accept(dmix->server_fd, 0, 0); 379d5ac70f0Sopenharmony_ci if (sck >= 0) { 380d5ac70f0Sopenharmony_ci server_printf("DIRECT SERVER: new connection %i\n", sck); 381d5ac70f0Sopenharmony_ci if (current == max) { 382d5ac70f0Sopenharmony_ci close(sck); 383d5ac70f0Sopenharmony_ci } else { 384d5ac70f0Sopenharmony_ci unsigned char buf = 'A'; 385d5ac70f0Sopenharmony_ci pfds[current+1].fd = sck; 386d5ac70f0Sopenharmony_ci pfds[current+1].events = POLLIN | POLLERR | POLLHUP; 387d5ac70f0Sopenharmony_ci _snd_send_fd(sck, &buf, 1, dmix->hw_fd); 388d5ac70f0Sopenharmony_ci server_printf("DIRECT SERVER: fd sent ok\n"); 389d5ac70f0Sopenharmony_ci current++; 390d5ac70f0Sopenharmony_ci } 391d5ac70f0Sopenharmony_ci } 392d5ac70f0Sopenharmony_ci } 393d5ac70f0Sopenharmony_ci for (i = 0; i < current && ret > 0; i++) { 394d5ac70f0Sopenharmony_ci struct pollfd *pfd = &pfds[i+1]; 395d5ac70f0Sopenharmony_ci unsigned char cmd; 396d5ac70f0Sopenharmony_ci server_printf("client %i revents = 0x%x\n", pfd->fd, pfd->revents); 397d5ac70f0Sopenharmony_ci if (pfd->revents & (POLLERR | POLLHUP)) { 398d5ac70f0Sopenharmony_ci ret--; 399d5ac70f0Sopenharmony_ci close(pfd->fd); 400d5ac70f0Sopenharmony_ci pfd->fd = -1; 401d5ac70f0Sopenharmony_ci continue; 402d5ac70f0Sopenharmony_ci } 403d5ac70f0Sopenharmony_ci if (!(pfd->revents & POLLIN)) 404d5ac70f0Sopenharmony_ci continue; 405d5ac70f0Sopenharmony_ci ret--; 406d5ac70f0Sopenharmony_ci if (read(pfd->fd, &cmd, 1) == 1) 407d5ac70f0Sopenharmony_ci cmd = 0 /*process command */; 408d5ac70f0Sopenharmony_ci } 409d5ac70f0Sopenharmony_ci for (i = 0; i < current; i++) { 410d5ac70f0Sopenharmony_ci if (pfds[i+1].fd < 0) { 411d5ac70f0Sopenharmony_ci if (i + 1 != max) 412d5ac70f0Sopenharmony_ci memcpy(&pfds[i+1], &pfds[i+2], sizeof(struct pollfd) * (max - i - 1)); 413d5ac70f0Sopenharmony_ci current--; 414d5ac70f0Sopenharmony_ci } 415d5ac70f0Sopenharmony_ci } 416d5ac70f0Sopenharmony_ci } 417d5ac70f0Sopenharmony_ci server_cleanup(dmix); 418d5ac70f0Sopenharmony_ci server_printf("DIRECT SERVER EXIT\n"); 419d5ac70f0Sopenharmony_ci#ifdef SERVER_JOB_DEBUG 420d5ac70f0Sopenharmony_ci close(0); close(1); close(2); 421d5ac70f0Sopenharmony_ci#endif 422d5ac70f0Sopenharmony_ci _exit(EXIT_SUCCESS); 423d5ac70f0Sopenharmony_ci} 424d5ac70f0Sopenharmony_ci 425d5ac70f0Sopenharmony_ciint snd_pcm_direct_server_create(snd_pcm_direct_t *dmix) 426d5ac70f0Sopenharmony_ci{ 427d5ac70f0Sopenharmony_ci int ret; 428d5ac70f0Sopenharmony_ci 429d5ac70f0Sopenharmony_ci dmix->server_fd = -1; 430d5ac70f0Sopenharmony_ci 431d5ac70f0Sopenharmony_ci ret = get_tmp_name(dmix->shmptr->socket_name, sizeof(dmix->shmptr->socket_name)); 432d5ac70f0Sopenharmony_ci if (ret < 0) 433d5ac70f0Sopenharmony_ci return ret; 434d5ac70f0Sopenharmony_ci 435d5ac70f0Sopenharmony_ci ret = make_local_socket(dmix->shmptr->socket_name, 1, dmix->ipc_perm, dmix->ipc_gid); 436d5ac70f0Sopenharmony_ci if (ret < 0) 437d5ac70f0Sopenharmony_ci return ret; 438d5ac70f0Sopenharmony_ci dmix->server_fd = ret; 439d5ac70f0Sopenharmony_ci 440d5ac70f0Sopenharmony_ci ret = listen(dmix->server_fd, 4); 441d5ac70f0Sopenharmony_ci if (ret < 0) { 442d5ac70f0Sopenharmony_ci close(dmix->server_fd); 443d5ac70f0Sopenharmony_ci return ret; 444d5ac70f0Sopenharmony_ci } 445d5ac70f0Sopenharmony_ci 446d5ac70f0Sopenharmony_ci ret = fork(); 447d5ac70f0Sopenharmony_ci if (ret < 0) { 448d5ac70f0Sopenharmony_ci close(dmix->server_fd); 449d5ac70f0Sopenharmony_ci return ret; 450d5ac70f0Sopenharmony_ci } else if (ret == 0) { 451d5ac70f0Sopenharmony_ci ret = fork(); 452d5ac70f0Sopenharmony_ci if (ret == 0) 453d5ac70f0Sopenharmony_ci server_job(dmix); 454d5ac70f0Sopenharmony_ci _exit(EXIT_SUCCESS); 455d5ac70f0Sopenharmony_ci } else { 456d5ac70f0Sopenharmony_ci waitpid(ret, NULL, 0); 457d5ac70f0Sopenharmony_ci } 458d5ac70f0Sopenharmony_ci dmix->server_pid = ret; 459d5ac70f0Sopenharmony_ci dmix->server = 1; 460d5ac70f0Sopenharmony_ci return 0; 461d5ac70f0Sopenharmony_ci} 462d5ac70f0Sopenharmony_ci 463d5ac70f0Sopenharmony_ciint snd_pcm_direct_server_discard(snd_pcm_direct_t *dmix) 464d5ac70f0Sopenharmony_ci{ 465d5ac70f0Sopenharmony_ci if (dmix->server) { 466d5ac70f0Sopenharmony_ci //kill(dmix->server_pid, SIGTERM); 467d5ac70f0Sopenharmony_ci //waitpid(dmix->server_pid, NULL, 0); 468d5ac70f0Sopenharmony_ci dmix->server_pid = (pid_t)-1; 469d5ac70f0Sopenharmony_ci } 470d5ac70f0Sopenharmony_ci if (dmix->server_fd > 0) { 471d5ac70f0Sopenharmony_ci close(dmix->server_fd); 472d5ac70f0Sopenharmony_ci dmix->server_fd = -1; 473d5ac70f0Sopenharmony_ci } 474d5ac70f0Sopenharmony_ci dmix->server = 0; 475d5ac70f0Sopenharmony_ci return 0; 476d5ac70f0Sopenharmony_ci} 477d5ac70f0Sopenharmony_ci 478d5ac70f0Sopenharmony_ci/* 479d5ac70f0Sopenharmony_ci * client side 480d5ac70f0Sopenharmony_ci */ 481d5ac70f0Sopenharmony_ci 482d5ac70f0Sopenharmony_ciint snd_pcm_direct_client_connect(snd_pcm_direct_t *dmix) 483d5ac70f0Sopenharmony_ci{ 484d5ac70f0Sopenharmony_ci int ret; 485d5ac70f0Sopenharmony_ci unsigned char buf; 486d5ac70f0Sopenharmony_ci 487d5ac70f0Sopenharmony_ci ret = make_local_socket(dmix->shmptr->socket_name, 0, -1, -1); 488d5ac70f0Sopenharmony_ci if (ret < 0) 489d5ac70f0Sopenharmony_ci return ret; 490d5ac70f0Sopenharmony_ci dmix->comm_fd = ret; 491d5ac70f0Sopenharmony_ci 492d5ac70f0Sopenharmony_ci ret = snd_receive_fd(dmix->comm_fd, &buf, 1, &dmix->hw_fd); 493d5ac70f0Sopenharmony_ci if (ret < 1 || buf != 'A') { 494d5ac70f0Sopenharmony_ci close(dmix->comm_fd); 495d5ac70f0Sopenharmony_ci dmix->comm_fd = -1; 496d5ac70f0Sopenharmony_ci return ret; 497d5ac70f0Sopenharmony_ci } 498d5ac70f0Sopenharmony_ci 499d5ac70f0Sopenharmony_ci dmix->client = 1; 500d5ac70f0Sopenharmony_ci return 0; 501d5ac70f0Sopenharmony_ci} 502d5ac70f0Sopenharmony_ci 503d5ac70f0Sopenharmony_ciint snd_pcm_direct_client_discard(snd_pcm_direct_t *dmix) 504d5ac70f0Sopenharmony_ci{ 505d5ac70f0Sopenharmony_ci if (dmix->client) { 506d5ac70f0Sopenharmony_ci close(dmix->comm_fd); 507d5ac70f0Sopenharmony_ci dmix->comm_fd = -1; 508d5ac70f0Sopenharmony_ci } 509d5ac70f0Sopenharmony_ci return 0; 510d5ac70f0Sopenharmony_ci} 511d5ac70f0Sopenharmony_ci 512d5ac70f0Sopenharmony_ci/* 513d5ac70f0Sopenharmony_ci * plugin helpers 514d5ac70f0Sopenharmony_ci */ 515d5ac70f0Sopenharmony_ci 516d5ac70f0Sopenharmony_ciint snd_pcm_direct_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED) 517d5ac70f0Sopenharmony_ci{ 518d5ac70f0Sopenharmony_ci /* value is cached for us in pcm->mode (SND_PCM_NONBLOCK flag) */ 519d5ac70f0Sopenharmony_ci return 0; 520d5ac70f0Sopenharmony_ci} 521d5ac70f0Sopenharmony_ci 522d5ac70f0Sopenharmony_ciint snd_pcm_direct_async(snd_pcm_t *pcm, int sig, pid_t pid) 523d5ac70f0Sopenharmony_ci{ 524d5ac70f0Sopenharmony_ci snd_pcm_direct_t *dmix = pcm->private_data; 525d5ac70f0Sopenharmony_ci return snd_timer_async(dmix->timer, sig, pid); 526d5ac70f0Sopenharmony_ci} 527d5ac70f0Sopenharmony_ci 528d5ac70f0Sopenharmony_ci/* empty the timer read queue */ 529d5ac70f0Sopenharmony_ciint snd_pcm_direct_clear_timer_queue(snd_pcm_direct_t *dmix) 530d5ac70f0Sopenharmony_ci{ 531d5ac70f0Sopenharmony_ci int changed = 0; 532d5ac70f0Sopenharmony_ci if (dmix->timer_need_poll) { 533d5ac70f0Sopenharmony_ci while (poll(&dmix->timer_fd, 1, 0) > 0) { 534d5ac70f0Sopenharmony_ci changed++; 535d5ac70f0Sopenharmony_ci /* we don't need the value */ 536d5ac70f0Sopenharmony_ci if (dmix->tread) { 537d5ac70f0Sopenharmony_ci snd_timer_tread_t rbuf[4]; 538d5ac70f0Sopenharmony_ci snd_timer_read(dmix->timer, rbuf, sizeof(rbuf)); 539d5ac70f0Sopenharmony_ci } else { 540d5ac70f0Sopenharmony_ci snd_timer_read_t rbuf; 541d5ac70f0Sopenharmony_ci snd_timer_read(dmix->timer, &rbuf, sizeof(rbuf)); 542d5ac70f0Sopenharmony_ci } 543d5ac70f0Sopenharmony_ci } 544d5ac70f0Sopenharmony_ci } else { 545d5ac70f0Sopenharmony_ci if (dmix->tread) { 546d5ac70f0Sopenharmony_ci snd_timer_tread_t rbuf[4]; 547d5ac70f0Sopenharmony_ci int len; 548d5ac70f0Sopenharmony_ci while ((len = snd_timer_read(dmix->timer, rbuf, 549d5ac70f0Sopenharmony_ci sizeof(rbuf))) > 0 550d5ac70f0Sopenharmony_ci && (++changed) && 551d5ac70f0Sopenharmony_ci len != sizeof(rbuf[0])) 552d5ac70f0Sopenharmony_ci ; 553d5ac70f0Sopenharmony_ci } else { 554d5ac70f0Sopenharmony_ci snd_timer_read_t rbuf; 555d5ac70f0Sopenharmony_ci while (snd_timer_read(dmix->timer, &rbuf, sizeof(rbuf)) > 0) 556d5ac70f0Sopenharmony_ci changed++; 557d5ac70f0Sopenharmony_ci } 558d5ac70f0Sopenharmony_ci } 559d5ac70f0Sopenharmony_ci return changed; 560d5ac70f0Sopenharmony_ci} 561d5ac70f0Sopenharmony_ci 562d5ac70f0Sopenharmony_ciint snd_pcm_direct_timer_stop(snd_pcm_direct_t *dmix) 563d5ac70f0Sopenharmony_ci{ 564d5ac70f0Sopenharmony_ci snd_timer_stop(dmix->timer); 565d5ac70f0Sopenharmony_ci return 0; 566d5ac70f0Sopenharmony_ci} 567d5ac70f0Sopenharmony_ci 568d5ac70f0Sopenharmony_ci#define RECOVERIES_FLAG_SUSPENDED (1U << 31) 569d5ac70f0Sopenharmony_ci#define RECOVERIES_MASK ((1U << 31) - 1) 570d5ac70f0Sopenharmony_ci 571d5ac70f0Sopenharmony_ci/* 572d5ac70f0Sopenharmony_ci * Recover slave on XRUN or SUSPENDED. 573d5ac70f0Sopenharmony_ci * Even if direct plugins disable xrun detection, there might be an xrun 574d5ac70f0Sopenharmony_ci * raised directly by some drivers. 575d5ac70f0Sopenharmony_ci * The first client recovers slave pcm. 576d5ac70f0Sopenharmony_ci * Each client needs to execute sw xrun handling afterwards 577d5ac70f0Sopenharmony_ci */ 578d5ac70f0Sopenharmony_ciint snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct) 579d5ac70f0Sopenharmony_ci{ 580d5ac70f0Sopenharmony_ci unsigned int recoveries; 581d5ac70f0Sopenharmony_ci int state; 582d5ac70f0Sopenharmony_ci int ret; 583d5ac70f0Sopenharmony_ci int semerr; 584d5ac70f0Sopenharmony_ci 585d5ac70f0Sopenharmony_ci semerr = snd_pcm_direct_semaphore_down(direct, 586d5ac70f0Sopenharmony_ci DIRECT_IPC_SEM_CLIENT); 587d5ac70f0Sopenharmony_ci if (semerr < 0) { 588d5ac70f0Sopenharmony_ci SNDERR("SEMDOWN FAILED with err %d", semerr); 589d5ac70f0Sopenharmony_ci return semerr; 590d5ac70f0Sopenharmony_ci } 591d5ac70f0Sopenharmony_ci 592d5ac70f0Sopenharmony_ci state = snd_pcm_state(direct->spcm); 593d5ac70f0Sopenharmony_ci if (state != SND_PCM_STATE_XRUN && state != SND_PCM_STATE_SUSPENDED) { 594d5ac70f0Sopenharmony_ci /* ignore... someone else already did recovery */ 595d5ac70f0Sopenharmony_ci semerr = snd_pcm_direct_semaphore_up(direct, 596d5ac70f0Sopenharmony_ci DIRECT_IPC_SEM_CLIENT); 597d5ac70f0Sopenharmony_ci if (semerr < 0) { 598d5ac70f0Sopenharmony_ci SNDERR("SEMUP FAILED with err %d", semerr); 599d5ac70f0Sopenharmony_ci return semerr; 600d5ac70f0Sopenharmony_ci } 601d5ac70f0Sopenharmony_ci return 0; 602d5ac70f0Sopenharmony_ci } 603d5ac70f0Sopenharmony_ci 604d5ac70f0Sopenharmony_ci recoveries = direct->shmptr->s.recoveries; 605d5ac70f0Sopenharmony_ci recoveries = (recoveries + 1) & RECOVERIES_MASK; 606d5ac70f0Sopenharmony_ci if (state == SND_PCM_STATE_SUSPENDED) 607d5ac70f0Sopenharmony_ci recoveries |= RECOVERIES_FLAG_SUSPENDED; 608d5ac70f0Sopenharmony_ci direct->shmptr->s.recoveries = recoveries; 609d5ac70f0Sopenharmony_ci 610d5ac70f0Sopenharmony_ci /* some buggy drivers require the device resumed before prepared; 611d5ac70f0Sopenharmony_ci * when a device has RESUME flag and is in SUSPENDED state, resume 612d5ac70f0Sopenharmony_ci * here but immediately drop to bring it to a sane active state. 613d5ac70f0Sopenharmony_ci */ 614d5ac70f0Sopenharmony_ci if (state == SND_PCM_STATE_SUSPENDED && 615d5ac70f0Sopenharmony_ci (direct->spcm->info & SND_PCM_INFO_RESUME)) { 616d5ac70f0Sopenharmony_ci snd_pcm_resume(direct->spcm); 617d5ac70f0Sopenharmony_ci snd_pcm_drop(direct->spcm); 618d5ac70f0Sopenharmony_ci snd_pcm_direct_timer_stop(direct); 619d5ac70f0Sopenharmony_ci snd_pcm_direct_clear_timer_queue(direct); 620d5ac70f0Sopenharmony_ci } 621d5ac70f0Sopenharmony_ci 622d5ac70f0Sopenharmony_ci ret = snd_pcm_prepare(direct->spcm); 623d5ac70f0Sopenharmony_ci if (ret < 0) { 624d5ac70f0Sopenharmony_ci SNDERR("recover: unable to prepare slave"); 625d5ac70f0Sopenharmony_ci semerr = snd_pcm_direct_semaphore_up(direct, 626d5ac70f0Sopenharmony_ci DIRECT_IPC_SEM_CLIENT); 627d5ac70f0Sopenharmony_ci if (semerr < 0) { 628d5ac70f0Sopenharmony_ci SNDERR("SEMUP FAILED with err %d", semerr); 629d5ac70f0Sopenharmony_ci return semerr; 630d5ac70f0Sopenharmony_ci } 631d5ac70f0Sopenharmony_ci return ret; 632d5ac70f0Sopenharmony_ci } 633d5ac70f0Sopenharmony_ci 634d5ac70f0Sopenharmony_ci if (direct->type == SND_PCM_TYPE_DSHARE) { 635d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *dst_areas; 636d5ac70f0Sopenharmony_ci dst_areas = snd_pcm_mmap_areas(direct->spcm); 637d5ac70f0Sopenharmony_ci snd_pcm_areas_silence(dst_areas, 0, direct->spcm->channels, 638d5ac70f0Sopenharmony_ci direct->spcm->buffer_size, 639d5ac70f0Sopenharmony_ci direct->spcm->format); 640d5ac70f0Sopenharmony_ci } 641d5ac70f0Sopenharmony_ci 642d5ac70f0Sopenharmony_ci ret = snd_pcm_start(direct->spcm); 643d5ac70f0Sopenharmony_ci if (ret < 0) { 644d5ac70f0Sopenharmony_ci SNDERR("recover: unable to start slave"); 645d5ac70f0Sopenharmony_ci semerr = snd_pcm_direct_semaphore_up(direct, 646d5ac70f0Sopenharmony_ci DIRECT_IPC_SEM_CLIENT); 647d5ac70f0Sopenharmony_ci if (semerr < 0) { 648d5ac70f0Sopenharmony_ci SNDERR("SEMUP FAILED with err %d", semerr); 649d5ac70f0Sopenharmony_ci return semerr; 650d5ac70f0Sopenharmony_ci } 651d5ac70f0Sopenharmony_ci return ret; 652d5ac70f0Sopenharmony_ci } 653d5ac70f0Sopenharmony_ci semerr = snd_pcm_direct_semaphore_up(direct, 654d5ac70f0Sopenharmony_ci DIRECT_IPC_SEM_CLIENT); 655d5ac70f0Sopenharmony_ci if (semerr < 0) { 656d5ac70f0Sopenharmony_ci SNDERR("SEMUP FAILED with err %d", semerr); 657d5ac70f0Sopenharmony_ci return semerr; 658d5ac70f0Sopenharmony_ci } 659d5ac70f0Sopenharmony_ci return 0; 660d5ac70f0Sopenharmony_ci} 661d5ac70f0Sopenharmony_ci 662d5ac70f0Sopenharmony_ci/* 663d5ac70f0Sopenharmony_ci * enter xrun or suspended state, if slave xrun occurred or suspended 664d5ac70f0Sopenharmony_ci * @return: 0 for no xrun/suspend or a negative error code for xrun/suspend 665d5ac70f0Sopenharmony_ci */ 666d5ac70f0Sopenharmony_ciint snd_pcm_direct_check_xrun(snd_pcm_direct_t *direct, snd_pcm_t *pcm) 667d5ac70f0Sopenharmony_ci{ 668d5ac70f0Sopenharmony_ci int err; 669d5ac70f0Sopenharmony_ci 670d5ac70f0Sopenharmony_ci switch (snd_pcm_state(direct->spcm)) { 671d5ac70f0Sopenharmony_ci case SND_PCM_STATE_DISCONNECTED: 672d5ac70f0Sopenharmony_ci direct->state = SNDRV_PCM_STATE_DISCONNECTED; 673d5ac70f0Sopenharmony_ci return -ENODEV; 674d5ac70f0Sopenharmony_ci case SND_PCM_STATE_XRUN: 675d5ac70f0Sopenharmony_ci case SND_PCM_STATE_SUSPENDED: 676d5ac70f0Sopenharmony_ci if ((err = snd_pcm_direct_slave_recover(direct)) < 0) 677d5ac70f0Sopenharmony_ci return err; 678d5ac70f0Sopenharmony_ci break; 679d5ac70f0Sopenharmony_ci default: 680d5ac70f0Sopenharmony_ci break; 681d5ac70f0Sopenharmony_ci } 682d5ac70f0Sopenharmony_ci 683d5ac70f0Sopenharmony_ci if (direct->state == SND_PCM_STATE_XRUN) 684d5ac70f0Sopenharmony_ci return -EPIPE; 685d5ac70f0Sopenharmony_ci else if (direct->state == SND_PCM_STATE_SUSPENDED) 686d5ac70f0Sopenharmony_ci return -ESTRPIPE; 687d5ac70f0Sopenharmony_ci if (direct->shmptr->s.recoveries != direct->recoveries) { 688d5ac70f0Sopenharmony_ci /* no matter how many xruns we missed - 689d5ac70f0Sopenharmony_ci * so don't increment but just update to actual counter 690d5ac70f0Sopenharmony_ci */ 691d5ac70f0Sopenharmony_ci direct->recoveries = direct->shmptr->s.recoveries; 692d5ac70f0Sopenharmony_ci pcm->fast_ops->drop(pcm->fast_op_arg); 693d5ac70f0Sopenharmony_ci /* trigger_tstamp update is missing in drop callbacks */ 694d5ac70f0Sopenharmony_ci gettimestamp(&direct->trigger_tstamp, pcm->tstamp_type); 695d5ac70f0Sopenharmony_ci /* no timer clear: 696d5ac70f0Sopenharmony_ci * if slave already entered xrun again the event is lost. 697d5ac70f0Sopenharmony_ci * snd_pcm_direct_clear_timer_queue(direct); 698d5ac70f0Sopenharmony_ci */ 699d5ac70f0Sopenharmony_ci if (direct->recoveries & RECOVERIES_FLAG_SUSPENDED) { 700d5ac70f0Sopenharmony_ci direct->state = SND_PCM_STATE_SUSPENDED; 701d5ac70f0Sopenharmony_ci return -ESTRPIPE; 702d5ac70f0Sopenharmony_ci } else { 703d5ac70f0Sopenharmony_ci direct->state = SND_PCM_STATE_XRUN; 704d5ac70f0Sopenharmony_ci return -EPIPE; 705d5ac70f0Sopenharmony_ci } 706d5ac70f0Sopenharmony_ci } 707d5ac70f0Sopenharmony_ci return 0; 708d5ac70f0Sopenharmony_ci} 709d5ac70f0Sopenharmony_ci 710d5ac70f0Sopenharmony_ci/* 711d5ac70f0Sopenharmony_ci * This is the only operation guaranteed to be called before entering poll(). 712d5ac70f0Sopenharmony_ci * Direct plugins use fd of snd_timer to poll on, these timers do NOT check 713d5ac70f0Sopenharmony_ci * state of substream in kernel by intention. 714d5ac70f0Sopenharmony_ci * Only the enter to xrun might be notified once (SND_TIMER_EVENT_MSTOP). 715d5ac70f0Sopenharmony_ci * If xrun event was not correctly handled or was ignored it will never be 716d5ac70f0Sopenharmony_ci * evaluated again afterwards. 717d5ac70f0Sopenharmony_ci * This will result in snd_pcm_wait() always returning timeout. 718d5ac70f0Sopenharmony_ci * In contrast poll() on pcm hardware checks ALSA state and will immediately 719d5ac70f0Sopenharmony_ci * return POLLERR on XRUN. 720d5ac70f0Sopenharmony_ci * 721d5ac70f0Sopenharmony_ci * To prevent timeout and applications endlessly spinning without xrun 722d5ac70f0Sopenharmony_ci * detected we add a state check here which may trigger the xrun sequence. 723d5ac70f0Sopenharmony_ci * 724d5ac70f0Sopenharmony_ci * return count of filled descriptors or negative error code 725d5ac70f0Sopenharmony_ci */ 726d5ac70f0Sopenharmony_ciint snd_pcm_direct_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, 727d5ac70f0Sopenharmony_ci unsigned int space) 728d5ac70f0Sopenharmony_ci{ 729d5ac70f0Sopenharmony_ci if (pcm->poll_fd < 0) { 730d5ac70f0Sopenharmony_ci SNDMSG("poll_fd < 0"); 731d5ac70f0Sopenharmony_ci return -EIO; 732d5ac70f0Sopenharmony_ci } 733d5ac70f0Sopenharmony_ci if (space >= 1 && pfds) { 734d5ac70f0Sopenharmony_ci pfds->fd = pcm->poll_fd; 735d5ac70f0Sopenharmony_ci pfds->events = pcm->poll_events | POLLERR | POLLNVAL; 736d5ac70f0Sopenharmony_ci } else { 737d5ac70f0Sopenharmony_ci return 0; 738d5ac70f0Sopenharmony_ci } 739d5ac70f0Sopenharmony_ci 740d5ac70f0Sopenharmony_ci /* this will also evaluate slave state and enter xrun if necessary */ 741d5ac70f0Sopenharmony_ci /* using __snd_pcm_state() since this function is called inside lock */ 742d5ac70f0Sopenharmony_ci switch (__snd_pcm_state(pcm)) { 743d5ac70f0Sopenharmony_ci case SND_PCM_STATE_XRUN: 744d5ac70f0Sopenharmony_ci return -EPIPE; 745d5ac70f0Sopenharmony_ci default: 746d5ac70f0Sopenharmony_ci break; 747d5ac70f0Sopenharmony_ci } 748d5ac70f0Sopenharmony_ci return 1; 749d5ac70f0Sopenharmony_ci} 750d5ac70f0Sopenharmony_ci 751d5ac70f0Sopenharmony_ciint snd_pcm_direct_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents) 752d5ac70f0Sopenharmony_ci{ 753d5ac70f0Sopenharmony_ci snd_pcm_direct_t *dmix = pcm->private_data; 754d5ac70f0Sopenharmony_ci unsigned short events; 755d5ac70f0Sopenharmony_ci int empty = 0; 756d5ac70f0Sopenharmony_ci 757d5ac70f0Sopenharmony_ci assert(pfds && nfds == 1 && revents); 758d5ac70f0Sopenharmony_ci 759d5ac70f0Sopenharmony_citimer_changed: 760d5ac70f0Sopenharmony_ci events = pfds[0].revents; 761d5ac70f0Sopenharmony_ci if (events & POLLIN) { 762d5ac70f0Sopenharmony_ci snd_pcm_uframes_t avail; 763d5ac70f0Sopenharmony_ci __snd_pcm_avail_update(pcm); 764d5ac70f0Sopenharmony_ci if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { 765d5ac70f0Sopenharmony_ci events |= POLLOUT; 766d5ac70f0Sopenharmony_ci events &= ~POLLIN; 767d5ac70f0Sopenharmony_ci avail = snd_pcm_mmap_playback_avail(pcm); 768d5ac70f0Sopenharmony_ci } else { 769d5ac70f0Sopenharmony_ci avail = snd_pcm_mmap_capture_avail(pcm); 770d5ac70f0Sopenharmony_ci } 771d5ac70f0Sopenharmony_ci empty = avail < pcm->avail_min; 772d5ac70f0Sopenharmony_ci } 773d5ac70f0Sopenharmony_ci 774d5ac70f0Sopenharmony_ci if (snd_pcm_direct_check_xrun(dmix, pcm) < 0 || 775d5ac70f0Sopenharmony_ci snd_pcm_state(dmix->spcm) == SND_PCM_STATE_SETUP) { 776d5ac70f0Sopenharmony_ci events |= POLLERR; 777d5ac70f0Sopenharmony_ci } else { 778d5ac70f0Sopenharmony_ci if (empty) { 779d5ac70f0Sopenharmony_ci /* here we have a race condition: 780d5ac70f0Sopenharmony_ci * if period event arrived after the avail_update call 781d5ac70f0Sopenharmony_ci * above we might clear this event with the following 782d5ac70f0Sopenharmony_ci * clear_timer_queue. 783d5ac70f0Sopenharmony_ci * There is no way to do this in atomic manner, so we 784d5ac70f0Sopenharmony_ci * need to recheck avail_update if we successfully 785d5ac70f0Sopenharmony_ci * cleared a poll event. 786d5ac70f0Sopenharmony_ci */ 787d5ac70f0Sopenharmony_ci if (snd_pcm_direct_clear_timer_queue(dmix)) 788d5ac70f0Sopenharmony_ci goto timer_changed; 789d5ac70f0Sopenharmony_ci events &= ~(POLLOUT|POLLIN); 790d5ac70f0Sopenharmony_ci /* additional check */ 791d5ac70f0Sopenharmony_ci switch (__snd_pcm_state(pcm)) { 792d5ac70f0Sopenharmony_ci case SND_PCM_STATE_XRUN: 793d5ac70f0Sopenharmony_ci case SND_PCM_STATE_SUSPENDED: 794d5ac70f0Sopenharmony_ci case SND_PCM_STATE_SETUP: 795d5ac70f0Sopenharmony_ci events |= POLLERR; 796d5ac70f0Sopenharmony_ci break; 797d5ac70f0Sopenharmony_ci default: 798d5ac70f0Sopenharmony_ci break; 799d5ac70f0Sopenharmony_ci } 800d5ac70f0Sopenharmony_ci } 801d5ac70f0Sopenharmony_ci } 802d5ac70f0Sopenharmony_ci *revents = events; 803d5ac70f0Sopenharmony_ci return 0; 804d5ac70f0Sopenharmony_ci} 805d5ac70f0Sopenharmony_ci 806d5ac70f0Sopenharmony_ciint snd_pcm_direct_info(snd_pcm_t *pcm, snd_pcm_info_t * info) 807d5ac70f0Sopenharmony_ci{ 808d5ac70f0Sopenharmony_ci snd_pcm_direct_t *dmix = pcm->private_data; 809d5ac70f0Sopenharmony_ci 810d5ac70f0Sopenharmony_ci if (dmix->spcm && !dmix->shmptr->use_server) 811d5ac70f0Sopenharmony_ci return snd_pcm_info(dmix->spcm, info); 812d5ac70f0Sopenharmony_ci 813d5ac70f0Sopenharmony_ci memset(info, 0, sizeof(*info)); 814d5ac70f0Sopenharmony_ci info->stream = pcm->stream; 815d5ac70f0Sopenharmony_ci info->card = -1; 816d5ac70f0Sopenharmony_ci /* FIXME: fill this with something more useful: we know the hardware name */ 817d5ac70f0Sopenharmony_ci if (pcm->name) { 818d5ac70f0Sopenharmony_ci snd_strlcpy((char *)info->id, pcm->name, sizeof(info->id)); 819d5ac70f0Sopenharmony_ci snd_strlcpy((char *)info->name, pcm->name, sizeof(info->name)); 820d5ac70f0Sopenharmony_ci snd_strlcpy((char *)info->subname, pcm->name, sizeof(info->subname)); 821d5ac70f0Sopenharmony_ci } 822d5ac70f0Sopenharmony_ci info->subdevices_count = 1; 823d5ac70f0Sopenharmony_ci return 0; 824d5ac70f0Sopenharmony_ci} 825d5ac70f0Sopenharmony_ci 826d5ac70f0Sopenharmony_cistatic inline snd_mask_t *hw_param_mask(snd_pcm_hw_params_t *params, 827d5ac70f0Sopenharmony_ci snd_pcm_hw_param_t var) 828d5ac70f0Sopenharmony_ci{ 829d5ac70f0Sopenharmony_ci return ¶ms->masks[var - SND_PCM_HW_PARAM_FIRST_MASK]; 830d5ac70f0Sopenharmony_ci} 831d5ac70f0Sopenharmony_ci 832d5ac70f0Sopenharmony_cistatic inline snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params, 833d5ac70f0Sopenharmony_ci snd_pcm_hw_param_t var) 834d5ac70f0Sopenharmony_ci{ 835d5ac70f0Sopenharmony_ci return ¶ms->intervals[var - SND_PCM_HW_PARAM_FIRST_INTERVAL]; 836d5ac70f0Sopenharmony_ci} 837d5ac70f0Sopenharmony_ci 838d5ac70f0Sopenharmony_cistatic int hw_param_interval_refine_one(snd_pcm_hw_params_t *params, 839d5ac70f0Sopenharmony_ci snd_pcm_hw_param_t var, 840d5ac70f0Sopenharmony_ci snd_interval_t *src) 841d5ac70f0Sopenharmony_ci{ 842d5ac70f0Sopenharmony_ci snd_interval_t *i; 843d5ac70f0Sopenharmony_ci 844d5ac70f0Sopenharmony_ci if (!(params->rmask & (1<<var))) /* nothing to do? */ 845d5ac70f0Sopenharmony_ci return 0; 846d5ac70f0Sopenharmony_ci i = hw_param_interval(params, var); 847d5ac70f0Sopenharmony_ci if (snd_interval_empty(i)) { 848d5ac70f0Sopenharmony_ci SNDERR("dshare interval %i empty?", (int)var); 849d5ac70f0Sopenharmony_ci return -EINVAL; 850d5ac70f0Sopenharmony_ci } 851d5ac70f0Sopenharmony_ci if (snd_interval_refine(i, src)) 852d5ac70f0Sopenharmony_ci params->cmask |= 1<<var; 853d5ac70f0Sopenharmony_ci return 0; 854d5ac70f0Sopenharmony_ci} 855d5ac70f0Sopenharmony_ci 856d5ac70f0Sopenharmony_cistatic int hw_param_interval_refine_minmax(snd_pcm_hw_params_t *params, 857d5ac70f0Sopenharmony_ci snd_pcm_hw_param_t var, 858d5ac70f0Sopenharmony_ci unsigned int imin, 859d5ac70f0Sopenharmony_ci unsigned int imax) 860d5ac70f0Sopenharmony_ci{ 861d5ac70f0Sopenharmony_ci snd_interval_t t; 862d5ac70f0Sopenharmony_ci 863d5ac70f0Sopenharmony_ci memset(&t, 0, sizeof(t)); 864d5ac70f0Sopenharmony_ci snd_interval_set_minmax(&t, imin, imax); 865d5ac70f0Sopenharmony_ci t.integer = 1; 866d5ac70f0Sopenharmony_ci return hw_param_interval_refine_one(params, var, &t); 867d5ac70f0Sopenharmony_ci} 868d5ac70f0Sopenharmony_ci 869d5ac70f0Sopenharmony_ci/* this code is used 'as-is' from the alsa kernel code */ 870d5ac70f0Sopenharmony_cistatic int snd_interval_step(struct snd_interval *i, unsigned int min, 871d5ac70f0Sopenharmony_ci unsigned int step) 872d5ac70f0Sopenharmony_ci{ 873d5ac70f0Sopenharmony_ci unsigned int n; 874d5ac70f0Sopenharmony_ci int changed = 0; 875d5ac70f0Sopenharmony_ci n = (i->min - min) % step; 876d5ac70f0Sopenharmony_ci if (n != 0 || i->openmin) { 877d5ac70f0Sopenharmony_ci i->min += step - n; 878d5ac70f0Sopenharmony_ci changed = 1; 879d5ac70f0Sopenharmony_ci } 880d5ac70f0Sopenharmony_ci n = (i->max - min) % step; 881d5ac70f0Sopenharmony_ci if (n != 0 || i->openmax) { 882d5ac70f0Sopenharmony_ci i->max -= n; 883d5ac70f0Sopenharmony_ci changed = 1; 884d5ac70f0Sopenharmony_ci } 885d5ac70f0Sopenharmony_ci if (snd_interval_checkempty(i)) { 886d5ac70f0Sopenharmony_ci i->empty = 1; 887d5ac70f0Sopenharmony_ci return -EINVAL; 888d5ac70f0Sopenharmony_ci } 889d5ac70f0Sopenharmony_ci return changed; 890d5ac70f0Sopenharmony_ci} 891d5ac70f0Sopenharmony_ci 892d5ac70f0Sopenharmony_ci#undef REFINE_DEBUG 893d5ac70f0Sopenharmony_ci 894d5ac70f0Sopenharmony_ciint snd_pcm_direct_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) 895d5ac70f0Sopenharmony_ci{ 896d5ac70f0Sopenharmony_ci snd_pcm_direct_t *dshare = pcm->private_data; 897d5ac70f0Sopenharmony_ci static const snd_mask_t access = { .bits = { 898d5ac70f0Sopenharmony_ci (1<<SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) | 899d5ac70f0Sopenharmony_ci (1<<SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) | 900d5ac70f0Sopenharmony_ci (1<<SNDRV_PCM_ACCESS_RW_INTERLEAVED) | 901d5ac70f0Sopenharmony_ci (1<<SNDRV_PCM_ACCESS_RW_NONINTERLEAVED), 902d5ac70f0Sopenharmony_ci 0, 0, 0 } }; 903d5ac70f0Sopenharmony_ci int err; 904d5ac70f0Sopenharmony_ci 905d5ac70f0Sopenharmony_ci#ifdef REFINE_DEBUG 906d5ac70f0Sopenharmony_ci snd_output_t *log; 907d5ac70f0Sopenharmony_ci snd_output_stdio_attach(&log, stderr, 0); 908d5ac70f0Sopenharmony_ci snd_output_puts(log, "DMIX REFINE (begin):\n"); 909d5ac70f0Sopenharmony_ci snd_pcm_hw_params_dump(params, log); 910d5ac70f0Sopenharmony_ci#endif 911d5ac70f0Sopenharmony_ci if (params->rmask & (1<<SND_PCM_HW_PARAM_ACCESS)) { 912d5ac70f0Sopenharmony_ci if (snd_mask_empty(hw_param_mask(params, SND_PCM_HW_PARAM_ACCESS))) { 913d5ac70f0Sopenharmony_ci SNDERR("dshare access mask empty?"); 914d5ac70f0Sopenharmony_ci return -EINVAL; 915d5ac70f0Sopenharmony_ci } 916d5ac70f0Sopenharmony_ci if (snd_mask_refine(hw_param_mask(params, SND_PCM_HW_PARAM_ACCESS), &access)) 917d5ac70f0Sopenharmony_ci params->cmask |= 1<<SND_PCM_HW_PARAM_ACCESS; 918d5ac70f0Sopenharmony_ci } 919d5ac70f0Sopenharmony_ci if (params->rmask & (1<<SND_PCM_HW_PARAM_FORMAT)) { 920d5ac70f0Sopenharmony_ci if (snd_mask_empty(hw_param_mask(params, SND_PCM_HW_PARAM_FORMAT))) { 921d5ac70f0Sopenharmony_ci SNDERR("dshare format mask empty?"); 922d5ac70f0Sopenharmony_ci return -EINVAL; 923d5ac70f0Sopenharmony_ci } 924d5ac70f0Sopenharmony_ci if (snd_mask_refine_set(hw_param_mask(params, SND_PCM_HW_PARAM_FORMAT), 925d5ac70f0Sopenharmony_ci dshare->shmptr->hw.format)) 926d5ac70f0Sopenharmony_ci params->cmask |= 1<<SND_PCM_HW_PARAM_FORMAT; 927d5ac70f0Sopenharmony_ci } 928d5ac70f0Sopenharmony_ci //snd_mask_none(hw_param_mask(params, SND_PCM_HW_PARAM_SUBFORMAT)); 929d5ac70f0Sopenharmony_ci if (params->rmask & (1<<SND_PCM_HW_PARAM_CHANNELS)) { 930d5ac70f0Sopenharmony_ci if (snd_interval_empty(hw_param_interval(params, SND_PCM_HW_PARAM_CHANNELS))) { 931d5ac70f0Sopenharmony_ci SNDERR("dshare channels mask empty?"); 932d5ac70f0Sopenharmony_ci return -EINVAL; 933d5ac70f0Sopenharmony_ci } 934d5ac70f0Sopenharmony_ci err = snd_interval_refine_set(hw_param_interval(params, SND_PCM_HW_PARAM_CHANNELS), dshare->channels); 935d5ac70f0Sopenharmony_ci if (err < 0) 936d5ac70f0Sopenharmony_ci return err; 937d5ac70f0Sopenharmony_ci } 938d5ac70f0Sopenharmony_ci err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_RATE, 939d5ac70f0Sopenharmony_ci &dshare->shmptr->hw.rate); 940d5ac70f0Sopenharmony_ci if (err < 0) 941d5ac70f0Sopenharmony_ci return err; 942d5ac70f0Sopenharmony_ci 943d5ac70f0Sopenharmony_ci if (dshare->max_periods < 0) { 944d5ac70f0Sopenharmony_ci err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_SIZE, 945d5ac70f0Sopenharmony_ci &dshare->shmptr->hw.period_size); 946d5ac70f0Sopenharmony_ci if (err < 0) 947d5ac70f0Sopenharmony_ci return err; 948d5ac70f0Sopenharmony_ci err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_TIME, 949d5ac70f0Sopenharmony_ci &dshare->shmptr->hw.period_time); 950d5ac70f0Sopenharmony_ci if (err < 0) 951d5ac70f0Sopenharmony_ci return err; 952d5ac70f0Sopenharmony_ci err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_BUFFER_SIZE, 953d5ac70f0Sopenharmony_ci &dshare->shmptr->hw.buffer_size); 954d5ac70f0Sopenharmony_ci if (err < 0) 955d5ac70f0Sopenharmony_ci return err; 956d5ac70f0Sopenharmony_ci err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_BUFFER_TIME, 957d5ac70f0Sopenharmony_ci &dshare->shmptr->hw.buffer_time); 958d5ac70f0Sopenharmony_ci if (err < 0) 959d5ac70f0Sopenharmony_ci return err; 960d5ac70f0Sopenharmony_ci } else if (params->rmask & ((1<<SND_PCM_HW_PARAM_PERIODS)| 961d5ac70f0Sopenharmony_ci (1<<SND_PCM_HW_PARAM_BUFFER_BYTES)| 962d5ac70f0Sopenharmony_ci (1<<SND_PCM_HW_PARAM_BUFFER_SIZE)| 963d5ac70f0Sopenharmony_ci (1<<SND_PCM_HW_PARAM_BUFFER_TIME)| 964d5ac70f0Sopenharmony_ci (1<<SND_PCM_HW_PARAM_PERIOD_TIME)| 965d5ac70f0Sopenharmony_ci (1<<SND_PCM_HW_PARAM_PERIOD_SIZE)| 966d5ac70f0Sopenharmony_ci (1<<SND_PCM_HW_PARAM_PERIOD_BYTES))) { 967d5ac70f0Sopenharmony_ci snd_interval_t period_size = dshare->shmptr->hw.period_size; 968d5ac70f0Sopenharmony_ci snd_interval_t period_time = dshare->shmptr->hw.period_time; 969d5ac70f0Sopenharmony_ci int changed; 970d5ac70f0Sopenharmony_ci unsigned int max_periods = dshare->max_periods; 971d5ac70f0Sopenharmony_ci if (max_periods < 2) 972d5ac70f0Sopenharmony_ci max_periods = dshare->slave_buffer_size / dshare->slave_period_size; 973d5ac70f0Sopenharmony_ci 974d5ac70f0Sopenharmony_ci /* make sure buffer size does not exceed slave buffer size */ 975d5ac70f0Sopenharmony_ci err = hw_param_interval_refine_minmax(params, SND_PCM_HW_PARAM_BUFFER_SIZE, 976d5ac70f0Sopenharmony_ci 2 * dshare->slave_period_size, dshare->slave_buffer_size); 977d5ac70f0Sopenharmony_ci if (err < 0) 978d5ac70f0Sopenharmony_ci return err; 979d5ac70f0Sopenharmony_ci if (dshare->var_periodsize) { 980d5ac70f0Sopenharmony_ci /* more tolerant settings... */ 981d5ac70f0Sopenharmony_ci if (dshare->shmptr->hw.buffer_size.max / 2 > period_size.max) { 982d5ac70f0Sopenharmony_ci period_size.max = dshare->shmptr->hw.buffer_size.max / 2; 983d5ac70f0Sopenharmony_ci period_size.openmax = dshare->shmptr->hw.buffer_size.openmax; 984d5ac70f0Sopenharmony_ci } 985d5ac70f0Sopenharmony_ci if (dshare->shmptr->hw.buffer_time.max / 2 > period_time.max) { 986d5ac70f0Sopenharmony_ci period_time.max = dshare->shmptr->hw.buffer_time.max / 2; 987d5ac70f0Sopenharmony_ci period_time.openmax = dshare->shmptr->hw.buffer_time.openmax; 988d5ac70f0Sopenharmony_ci } 989d5ac70f0Sopenharmony_ci } 990d5ac70f0Sopenharmony_ci 991d5ac70f0Sopenharmony_ci err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_SIZE, 992d5ac70f0Sopenharmony_ci &period_size); 993d5ac70f0Sopenharmony_ci if (err < 0) 994d5ac70f0Sopenharmony_ci return err; 995d5ac70f0Sopenharmony_ci err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_TIME, 996d5ac70f0Sopenharmony_ci &period_time); 997d5ac70f0Sopenharmony_ci if (err < 0) 998d5ac70f0Sopenharmony_ci return err; 999d5ac70f0Sopenharmony_ci do { 1000d5ac70f0Sopenharmony_ci changed = 0; 1001d5ac70f0Sopenharmony_ci err = hw_param_interval_refine_minmax(params, SND_PCM_HW_PARAM_PERIODS, 1002d5ac70f0Sopenharmony_ci 2, max_periods); 1003d5ac70f0Sopenharmony_ci if (err < 0) 1004d5ac70f0Sopenharmony_ci return err; 1005d5ac70f0Sopenharmony_ci changed |= err; 1006d5ac70f0Sopenharmony_ci err = snd_pcm_hw_refine_soft(pcm, params); 1007d5ac70f0Sopenharmony_ci if (err < 0) 1008d5ac70f0Sopenharmony_ci return err; 1009d5ac70f0Sopenharmony_ci changed |= err; 1010d5ac70f0Sopenharmony_ci err = snd_interval_step(hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE), 1011d5ac70f0Sopenharmony_ci 0, dshare->slave_period_size); 1012d5ac70f0Sopenharmony_ci if (err < 0) 1013d5ac70f0Sopenharmony_ci return err; 1014d5ac70f0Sopenharmony_ci changed |= err; 1015d5ac70f0Sopenharmony_ci if (err) 1016d5ac70f0Sopenharmony_ci params->rmask |= (1 << SND_PCM_HW_PARAM_PERIOD_SIZE); 1017d5ac70f0Sopenharmony_ci } while (changed); 1018d5ac70f0Sopenharmony_ci } 1019d5ac70f0Sopenharmony_ci dshare->timer_ticks = hw_param_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE)->max / dshare->slave_period_size; 1020d5ac70f0Sopenharmony_ci params->info = dshare->shmptr->s.info; 1021d5ac70f0Sopenharmony_ci#ifdef REFINE_DEBUG 1022d5ac70f0Sopenharmony_ci snd_output_puts(log, "DMIX REFINE (end):\n"); 1023d5ac70f0Sopenharmony_ci snd_pcm_hw_params_dump(params, log); 1024d5ac70f0Sopenharmony_ci snd_output_close(log); 1025d5ac70f0Sopenharmony_ci#endif 1026d5ac70f0Sopenharmony_ci return 0; 1027d5ac70f0Sopenharmony_ci} 1028d5ac70f0Sopenharmony_ci 1029d5ac70f0Sopenharmony_ciint snd_pcm_direct_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) 1030d5ac70f0Sopenharmony_ci{ 1031d5ac70f0Sopenharmony_ci snd_pcm_direct_t *dmix = pcm->private_data; 1032d5ac70f0Sopenharmony_ci 1033d5ac70f0Sopenharmony_ci params->info = dmix->shmptr->s.info; 1034d5ac70f0Sopenharmony_ci params->rate_num = dmix->shmptr->s.rate; 1035d5ac70f0Sopenharmony_ci params->rate_den = 1; 1036d5ac70f0Sopenharmony_ci params->fifo_size = 0; 1037d5ac70f0Sopenharmony_ci params->msbits = dmix->shmptr->s.msbits; 1038d5ac70f0Sopenharmony_ci return 0; 1039d5ac70f0Sopenharmony_ci} 1040d5ac70f0Sopenharmony_ci 1041d5ac70f0Sopenharmony_ciint snd_pcm_direct_hw_free(snd_pcm_t *pcm ATTRIBUTE_UNUSED) 1042d5ac70f0Sopenharmony_ci{ 1043d5ac70f0Sopenharmony_ci /* values are cached in the pcm structure */ 1044d5ac70f0Sopenharmony_ci return 0; 1045d5ac70f0Sopenharmony_ci} 1046d5ac70f0Sopenharmony_ci 1047d5ac70f0Sopenharmony_ciint snd_pcm_direct_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) 1048d5ac70f0Sopenharmony_ci{ 1049d5ac70f0Sopenharmony_ci if (params->tstamp_type != pcm->tstamp_type) 1050d5ac70f0Sopenharmony_ci return -EINVAL; 1051d5ac70f0Sopenharmony_ci 1052d5ac70f0Sopenharmony_ci /* values are cached in the pcm structure */ 1053d5ac70f0Sopenharmony_ci return 0; 1054d5ac70f0Sopenharmony_ci} 1055d5ac70f0Sopenharmony_ci 1056d5ac70f0Sopenharmony_ciint snd_pcm_direct_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info) 1057d5ac70f0Sopenharmony_ci{ 1058d5ac70f0Sopenharmony_ci return snd_pcm_channel_info_shm(pcm, info, -1); 1059d5ac70f0Sopenharmony_ci} 1060d5ac70f0Sopenharmony_ci 1061d5ac70f0Sopenharmony_ciint snd_pcm_direct_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED) 1062d5ac70f0Sopenharmony_ci{ 1063d5ac70f0Sopenharmony_ci return 0; 1064d5ac70f0Sopenharmony_ci} 1065d5ac70f0Sopenharmony_ci 1066d5ac70f0Sopenharmony_ciint snd_pcm_direct_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED) 1067d5ac70f0Sopenharmony_ci{ 1068d5ac70f0Sopenharmony_ci return 0; 1069d5ac70f0Sopenharmony_ci} 1070d5ac70f0Sopenharmony_ci 1071d5ac70f0Sopenharmony_cisnd_pcm_chmap_query_t **snd_pcm_direct_query_chmaps(snd_pcm_t *pcm) 1072d5ac70f0Sopenharmony_ci{ 1073d5ac70f0Sopenharmony_ci snd_pcm_direct_t *dmix = pcm->private_data; 1074d5ac70f0Sopenharmony_ci snd_pcm_chmap_query_t **smaps, **maps; 1075d5ac70f0Sopenharmony_ci unsigned int i, j; 1076d5ac70f0Sopenharmony_ci 1077d5ac70f0Sopenharmony_ci if (dmix->bindings == NULL) 1078d5ac70f0Sopenharmony_ci return snd_pcm_query_chmaps(dmix->spcm); 1079d5ac70f0Sopenharmony_ci 1080d5ac70f0Sopenharmony_ci maps = calloc(2, sizeof(*maps)); 1081d5ac70f0Sopenharmony_ci if (!maps) 1082d5ac70f0Sopenharmony_ci return NULL; 1083d5ac70f0Sopenharmony_ci maps[0] = calloc(dmix->channels + 2, sizeof(int *)); 1084d5ac70f0Sopenharmony_ci if (!maps[0]) { 1085d5ac70f0Sopenharmony_ci free(maps); 1086d5ac70f0Sopenharmony_ci return NULL; 1087d5ac70f0Sopenharmony_ci } 1088d5ac70f0Sopenharmony_ci smaps = snd_pcm_query_chmaps(dmix->spcm); 1089d5ac70f0Sopenharmony_ci if (smaps == NULL) { 1090d5ac70f0Sopenharmony_ci snd_pcm_free_chmaps(maps); 1091d5ac70f0Sopenharmony_ci return NULL; 1092d5ac70f0Sopenharmony_ci } 1093d5ac70f0Sopenharmony_ci maps[0]->type = SND_CHMAP_TYPE_FIXED; 1094d5ac70f0Sopenharmony_ci maps[0]->map.channels = dmix->channels; 1095d5ac70f0Sopenharmony_ci for (i = 0; i < dmix->channels; i++) { 1096d5ac70f0Sopenharmony_ci j = dmix->bindings[i]; 1097d5ac70f0Sopenharmony_ci if (j == UINT_MAX || smaps[0]->map.channels < j) 1098d5ac70f0Sopenharmony_ci continue; 1099d5ac70f0Sopenharmony_ci maps[0]->map.pos[i] = smaps[0]->map.pos[j]; 1100d5ac70f0Sopenharmony_ci } 1101d5ac70f0Sopenharmony_ci return maps; 1102d5ac70f0Sopenharmony_ci} 1103d5ac70f0Sopenharmony_ci 1104d5ac70f0Sopenharmony_cisnd_pcm_chmap_t *snd_pcm_direct_get_chmap(snd_pcm_t *pcm) 1105d5ac70f0Sopenharmony_ci{ 1106d5ac70f0Sopenharmony_ci snd_pcm_direct_t *dmix = pcm->private_data; 1107d5ac70f0Sopenharmony_ci return snd_pcm_get_chmap(dmix->spcm); 1108d5ac70f0Sopenharmony_ci} 1109d5ac70f0Sopenharmony_ci 1110d5ac70f0Sopenharmony_ciint snd_pcm_direct_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map) 1111d5ac70f0Sopenharmony_ci{ 1112d5ac70f0Sopenharmony_ci snd_pcm_direct_t *dmix = pcm->private_data; 1113d5ac70f0Sopenharmony_ci return snd_pcm_set_chmap(dmix->spcm, map); 1114d5ac70f0Sopenharmony_ci} 1115d5ac70f0Sopenharmony_ci 1116d5ac70f0Sopenharmony_ciint snd_pcm_direct_prepare(snd_pcm_t *pcm) 1117d5ac70f0Sopenharmony_ci{ 1118d5ac70f0Sopenharmony_ci snd_pcm_direct_t *dmix = pcm->private_data; 1119d5ac70f0Sopenharmony_ci int err; 1120d5ac70f0Sopenharmony_ci 1121d5ac70f0Sopenharmony_ci switch (snd_pcm_state(dmix->spcm)) { 1122d5ac70f0Sopenharmony_ci case SND_PCM_STATE_SETUP: 1123d5ac70f0Sopenharmony_ci case SND_PCM_STATE_XRUN: 1124d5ac70f0Sopenharmony_ci case SND_PCM_STATE_SUSPENDED: 1125d5ac70f0Sopenharmony_ci err = snd_pcm_prepare(dmix->spcm); 1126d5ac70f0Sopenharmony_ci if (err < 0) 1127d5ac70f0Sopenharmony_ci return err; 1128d5ac70f0Sopenharmony_ci snd_pcm_start(dmix->spcm); 1129d5ac70f0Sopenharmony_ci break; 1130d5ac70f0Sopenharmony_ci case SND_PCM_STATE_OPEN: 1131d5ac70f0Sopenharmony_ci case SND_PCM_STATE_DISCONNECTED: 1132d5ac70f0Sopenharmony_ci return -EBADFD; 1133d5ac70f0Sopenharmony_ci default: 1134d5ac70f0Sopenharmony_ci break; 1135d5ac70f0Sopenharmony_ci } 1136d5ac70f0Sopenharmony_ci snd_pcm_direct_check_interleave(dmix, pcm); 1137d5ac70f0Sopenharmony_ci dmix->state = SND_PCM_STATE_PREPARED; 1138d5ac70f0Sopenharmony_ci dmix->appl_ptr = dmix->last_appl_ptr = 0; 1139d5ac70f0Sopenharmony_ci dmix->hw_ptr = 0; 1140d5ac70f0Sopenharmony_ci return snd_pcm_direct_set_timer_params(dmix); 1141d5ac70f0Sopenharmony_ci} 1142d5ac70f0Sopenharmony_ci 1143d5ac70f0Sopenharmony_ciint snd_pcm_direct_resume(snd_pcm_t *pcm) 1144d5ac70f0Sopenharmony_ci{ 1145d5ac70f0Sopenharmony_ci snd_pcm_direct_t *dmix = pcm->private_data; 1146d5ac70f0Sopenharmony_ci int err; 1147d5ac70f0Sopenharmony_ci 1148d5ac70f0Sopenharmony_ci err = snd_pcm_direct_slave_recover(dmix); 1149d5ac70f0Sopenharmony_ci return err < 0 ? err : -ENOSYS; 1150d5ac70f0Sopenharmony_ci} 1151d5ac70f0Sopenharmony_ci 1152d5ac70f0Sopenharmony_ci#define COPY_SLAVE(field) (dmix->shmptr->s.field = spcm->field) 1153d5ac70f0Sopenharmony_ci 1154d5ac70f0Sopenharmony_ci/* copy the slave setting */ 1155d5ac70f0Sopenharmony_cistatic void save_slave_setting(snd_pcm_direct_t *dmix, snd_pcm_t *spcm) 1156d5ac70f0Sopenharmony_ci{ 1157d5ac70f0Sopenharmony_ci spcm->info &= ~SND_PCM_INFO_PAUSE; 1158d5ac70f0Sopenharmony_ci 1159d5ac70f0Sopenharmony_ci COPY_SLAVE(access); 1160d5ac70f0Sopenharmony_ci COPY_SLAVE(format); 1161d5ac70f0Sopenharmony_ci COPY_SLAVE(subformat); 1162d5ac70f0Sopenharmony_ci COPY_SLAVE(channels); 1163d5ac70f0Sopenharmony_ci COPY_SLAVE(rate); 1164d5ac70f0Sopenharmony_ci COPY_SLAVE(period_size); 1165d5ac70f0Sopenharmony_ci COPY_SLAVE(period_time); 1166d5ac70f0Sopenharmony_ci COPY_SLAVE(periods); 1167d5ac70f0Sopenharmony_ci COPY_SLAVE(tstamp_mode); 1168d5ac70f0Sopenharmony_ci COPY_SLAVE(tstamp_type); 1169d5ac70f0Sopenharmony_ci COPY_SLAVE(period_step); 1170d5ac70f0Sopenharmony_ci COPY_SLAVE(avail_min); 1171d5ac70f0Sopenharmony_ci COPY_SLAVE(start_threshold); 1172d5ac70f0Sopenharmony_ci COPY_SLAVE(stop_threshold); 1173d5ac70f0Sopenharmony_ci COPY_SLAVE(silence_threshold); 1174d5ac70f0Sopenharmony_ci COPY_SLAVE(silence_size); 1175d5ac70f0Sopenharmony_ci COPY_SLAVE(boundary); 1176d5ac70f0Sopenharmony_ci COPY_SLAVE(info); 1177d5ac70f0Sopenharmony_ci COPY_SLAVE(msbits); 1178d5ac70f0Sopenharmony_ci COPY_SLAVE(rate_num); 1179d5ac70f0Sopenharmony_ci COPY_SLAVE(rate_den); 1180d5ac70f0Sopenharmony_ci COPY_SLAVE(hw_flags); 1181d5ac70f0Sopenharmony_ci COPY_SLAVE(fifo_size); 1182d5ac70f0Sopenharmony_ci COPY_SLAVE(buffer_size); 1183d5ac70f0Sopenharmony_ci COPY_SLAVE(buffer_time); 1184d5ac70f0Sopenharmony_ci COPY_SLAVE(sample_bits); 1185d5ac70f0Sopenharmony_ci COPY_SLAVE(frame_bits); 1186d5ac70f0Sopenharmony_ci 1187d5ac70f0Sopenharmony_ci dmix->shmptr->s.info &= ~SND_PCM_INFO_RESUME; 1188d5ac70f0Sopenharmony_ci} 1189d5ac70f0Sopenharmony_ci 1190d5ac70f0Sopenharmony_ci#undef COPY_SLAVE 1191d5ac70f0Sopenharmony_ci 1192d5ac70f0Sopenharmony_ci/* 1193d5ac70f0Sopenharmony_ci * this function initializes hardware and starts playback operation with 1194d5ac70f0Sopenharmony_ci * no stop threshold (it operates all time without xrun checking) 1195d5ac70f0Sopenharmony_ci * also, the driver silences the unused ring buffer areas for us 1196d5ac70f0Sopenharmony_ci */ 1197d5ac70f0Sopenharmony_ciint snd_pcm_direct_initialize_slave(snd_pcm_direct_t *dmix, snd_pcm_t *spcm, struct slave_params *params) 1198d5ac70f0Sopenharmony_ci{ 1199d5ac70f0Sopenharmony_ci snd_pcm_hw_params_t hw_params = {0}; 1200d5ac70f0Sopenharmony_ci snd_pcm_sw_params_t sw_params = {0}; 1201d5ac70f0Sopenharmony_ci int ret, buffer_is_not_initialized; 1202d5ac70f0Sopenharmony_ci snd_pcm_uframes_t boundary; 1203d5ac70f0Sopenharmony_ci struct pollfd fd; 1204d5ac70f0Sopenharmony_ci int loops = 10; 1205d5ac70f0Sopenharmony_ci 1206d5ac70f0Sopenharmony_ci __again: 1207d5ac70f0Sopenharmony_ci if (loops-- <= 0) { 1208d5ac70f0Sopenharmony_ci SNDERR("unable to find a valid configuration for slave"); 1209d5ac70f0Sopenharmony_ci return -EINVAL; 1210d5ac70f0Sopenharmony_ci } 1211d5ac70f0Sopenharmony_ci ret = snd_pcm_hw_params_any(spcm, &hw_params); 1212d5ac70f0Sopenharmony_ci if (ret < 0) { 1213d5ac70f0Sopenharmony_ci SNDERR("snd_pcm_hw_params_any failed"); 1214d5ac70f0Sopenharmony_ci return ret; 1215d5ac70f0Sopenharmony_ci } 1216d5ac70f0Sopenharmony_ci ret = snd_pcm_hw_params_set_access(spcm, &hw_params, 1217d5ac70f0Sopenharmony_ci SND_PCM_ACCESS_MMAP_INTERLEAVED); 1218d5ac70f0Sopenharmony_ci if (ret < 0) { 1219d5ac70f0Sopenharmony_ci ret = snd_pcm_hw_params_set_access(spcm, &hw_params, 1220d5ac70f0Sopenharmony_ci SND_PCM_ACCESS_MMAP_NONINTERLEAVED); 1221d5ac70f0Sopenharmony_ci if (ret < 0) { 1222d5ac70f0Sopenharmony_ci SNDERR("slave plugin does not support mmap interleaved or mmap noninterleaved access"); 1223d5ac70f0Sopenharmony_ci return ret; 1224d5ac70f0Sopenharmony_ci } 1225d5ac70f0Sopenharmony_ci } 1226d5ac70f0Sopenharmony_ci if (params->format == SND_PCM_FORMAT_UNKNOWN) 1227d5ac70f0Sopenharmony_ci ret = -EINVAL; 1228d5ac70f0Sopenharmony_ci else 1229d5ac70f0Sopenharmony_ci ret = snd_pcm_hw_params_set_format(spcm, &hw_params, 1230d5ac70f0Sopenharmony_ci params->format); 1231d5ac70f0Sopenharmony_ci if (ret < 0) { 1232d5ac70f0Sopenharmony_ci static const snd_pcm_format_t dmix_formats[] = { 1233d5ac70f0Sopenharmony_ci SND_PCM_FORMAT_S32, 1234d5ac70f0Sopenharmony_ci SND_PCM_FORMAT_S32 ^ SND_PCM_FORMAT_S32_LE ^ 1235d5ac70f0Sopenharmony_ci SND_PCM_FORMAT_S32_BE, 1236d5ac70f0Sopenharmony_ci SND_PCM_FORMAT_S16, 1237d5ac70f0Sopenharmony_ci SND_PCM_FORMAT_S16 ^ SND_PCM_FORMAT_S16_LE ^ 1238d5ac70f0Sopenharmony_ci SND_PCM_FORMAT_S16_BE, 1239d5ac70f0Sopenharmony_ci SND_PCM_FORMAT_S24_LE, 1240d5ac70f0Sopenharmony_ci SND_PCM_FORMAT_S24_3LE, 1241d5ac70f0Sopenharmony_ci SND_PCM_FORMAT_U8, 1242d5ac70f0Sopenharmony_ci }; 1243d5ac70f0Sopenharmony_ci snd_pcm_format_t format; 1244d5ac70f0Sopenharmony_ci unsigned int i; 1245d5ac70f0Sopenharmony_ci 1246d5ac70f0Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dmix_formats); ++i) { 1247d5ac70f0Sopenharmony_ci format = dmix_formats[i]; 1248d5ac70f0Sopenharmony_ci ret = snd_pcm_hw_params_set_format(spcm, &hw_params, 1249d5ac70f0Sopenharmony_ci format); 1250d5ac70f0Sopenharmony_ci if (ret >= 0) 1251d5ac70f0Sopenharmony_ci break; 1252d5ac70f0Sopenharmony_ci } 1253d5ac70f0Sopenharmony_ci if (ret < 0 && dmix->type != SND_PCM_TYPE_DMIX) { 1254d5ac70f0Sopenharmony_ci /* TODO: try to choose a good format */ 1255d5ac70f0Sopenharmony_ci ret = INTERNAL(snd_pcm_hw_params_set_format_first)(spcm, 1256d5ac70f0Sopenharmony_ci &hw_params, &format); 1257d5ac70f0Sopenharmony_ci } 1258d5ac70f0Sopenharmony_ci if (ret < 0) { 1259d5ac70f0Sopenharmony_ci SNDERR("requested or auto-format is not available"); 1260d5ac70f0Sopenharmony_ci return ret; 1261d5ac70f0Sopenharmony_ci } 1262d5ac70f0Sopenharmony_ci params->format = format; 1263d5ac70f0Sopenharmony_ci } 1264d5ac70f0Sopenharmony_ci ret = INTERNAL(snd_pcm_hw_params_set_channels_near)(spcm, &hw_params, 1265d5ac70f0Sopenharmony_ci (unsigned int *)¶ms->channels); 1266d5ac70f0Sopenharmony_ci if (ret < 0) { 1267d5ac70f0Sopenharmony_ci SNDERR("requested count of channels is not available"); 1268d5ac70f0Sopenharmony_ci return ret; 1269d5ac70f0Sopenharmony_ci } 1270d5ac70f0Sopenharmony_ci ret = INTERNAL(snd_pcm_hw_params_set_rate_near)(spcm, &hw_params, 1271d5ac70f0Sopenharmony_ci (unsigned int *)¶ms->rate, 0); 1272d5ac70f0Sopenharmony_ci if (ret < 0) { 1273d5ac70f0Sopenharmony_ci SNDERR("requested rate is not available"); 1274d5ac70f0Sopenharmony_ci return ret; 1275d5ac70f0Sopenharmony_ci } 1276d5ac70f0Sopenharmony_ci 1277d5ac70f0Sopenharmony_ci buffer_is_not_initialized = 0; 1278d5ac70f0Sopenharmony_ci if (params->buffer_time > 0) { 1279d5ac70f0Sopenharmony_ci ret = INTERNAL(snd_pcm_hw_params_set_buffer_time_near)(spcm, 1280d5ac70f0Sopenharmony_ci &hw_params, (unsigned int *)¶ms->buffer_time, 0); 1281d5ac70f0Sopenharmony_ci if (ret < 0) { 1282d5ac70f0Sopenharmony_ci SNDERR("unable to set buffer time"); 1283d5ac70f0Sopenharmony_ci return ret; 1284d5ac70f0Sopenharmony_ci } 1285d5ac70f0Sopenharmony_ci } else if (params->buffer_size > 0) { 1286d5ac70f0Sopenharmony_ci ret = INTERNAL(snd_pcm_hw_params_set_buffer_size_near)(spcm, 1287d5ac70f0Sopenharmony_ci &hw_params, (snd_pcm_uframes_t *)¶ms->buffer_size); 1288d5ac70f0Sopenharmony_ci if (ret < 0) { 1289d5ac70f0Sopenharmony_ci SNDERR("unable to set buffer size"); 1290d5ac70f0Sopenharmony_ci return ret; 1291d5ac70f0Sopenharmony_ci } 1292d5ac70f0Sopenharmony_ci } else { 1293d5ac70f0Sopenharmony_ci buffer_is_not_initialized = 1; 1294d5ac70f0Sopenharmony_ci } 1295d5ac70f0Sopenharmony_ci 1296d5ac70f0Sopenharmony_ci if (params->period_time > 0) { 1297d5ac70f0Sopenharmony_ci ret = INTERNAL(snd_pcm_hw_params_set_period_time_near)(spcm, 1298d5ac70f0Sopenharmony_ci &hw_params, (unsigned int *)¶ms->period_time, 0); 1299d5ac70f0Sopenharmony_ci if (ret < 0) { 1300d5ac70f0Sopenharmony_ci SNDERR("unable to set period_time"); 1301d5ac70f0Sopenharmony_ci return ret; 1302d5ac70f0Sopenharmony_ci } 1303d5ac70f0Sopenharmony_ci } else if (params->period_size > 0) { 1304d5ac70f0Sopenharmony_ci ret = INTERNAL(snd_pcm_hw_params_set_period_size_near)(spcm, 1305d5ac70f0Sopenharmony_ci &hw_params, (snd_pcm_uframes_t *)¶ms->period_size, 1306d5ac70f0Sopenharmony_ci 0); 1307d5ac70f0Sopenharmony_ci if (ret < 0) { 1308d5ac70f0Sopenharmony_ci SNDERR("unable to set period_size"); 1309d5ac70f0Sopenharmony_ci return ret; 1310d5ac70f0Sopenharmony_ci } 1311d5ac70f0Sopenharmony_ci } 1312d5ac70f0Sopenharmony_ci 1313d5ac70f0Sopenharmony_ci if (buffer_is_not_initialized && params->periods > 0) { 1314d5ac70f0Sopenharmony_ci unsigned int periods = params->periods; 1315d5ac70f0Sopenharmony_ci ret = INTERNAL(snd_pcm_hw_params_set_periods_near)(spcm, 1316d5ac70f0Sopenharmony_ci &hw_params, ¶ms->periods, 0); 1317d5ac70f0Sopenharmony_ci if (ret < 0) { 1318d5ac70f0Sopenharmony_ci SNDERR("unable to set requested periods"); 1319d5ac70f0Sopenharmony_ci return ret; 1320d5ac70f0Sopenharmony_ci } 1321d5ac70f0Sopenharmony_ci if (params->periods == 1) { 1322d5ac70f0Sopenharmony_ci params->periods = periods; 1323d5ac70f0Sopenharmony_ci if (params->period_time > 0) { 1324d5ac70f0Sopenharmony_ci params->period_time /= 2; 1325d5ac70f0Sopenharmony_ci goto __again; 1326d5ac70f0Sopenharmony_ci } else if (params->period_size > 0) { 1327d5ac70f0Sopenharmony_ci params->period_size /= 2; 1328d5ac70f0Sopenharmony_ci goto __again; 1329d5ac70f0Sopenharmony_ci } 1330d5ac70f0Sopenharmony_ci SNDERR("unable to use stream with periods == 1"); 1331d5ac70f0Sopenharmony_ci return ret; 1332d5ac70f0Sopenharmony_ci } 1333d5ac70f0Sopenharmony_ci } 1334d5ac70f0Sopenharmony_ci 1335d5ac70f0Sopenharmony_ci ret = snd_pcm_hw_params(spcm, &hw_params); 1336d5ac70f0Sopenharmony_ci if (ret < 0) { 1337d5ac70f0Sopenharmony_ci SNDERR("unable to install hw params"); 1338d5ac70f0Sopenharmony_ci return ret; 1339d5ac70f0Sopenharmony_ci } 1340d5ac70f0Sopenharmony_ci 1341d5ac70f0Sopenharmony_ci /* store some hw_params values to shared info */ 1342d5ac70f0Sopenharmony_ci dmix->shmptr->hw.format = 1343d5ac70f0Sopenharmony_ci snd_mask_value(hw_param_mask(&hw_params, 1344d5ac70f0Sopenharmony_ci SND_PCM_HW_PARAM_FORMAT)); 1345d5ac70f0Sopenharmony_ci dmix->shmptr->hw.rate = 1346d5ac70f0Sopenharmony_ci *hw_param_interval(&hw_params, SND_PCM_HW_PARAM_RATE); 1347d5ac70f0Sopenharmony_ci dmix->shmptr->hw.buffer_size = 1348d5ac70f0Sopenharmony_ci *hw_param_interval(&hw_params, SND_PCM_HW_PARAM_BUFFER_SIZE); 1349d5ac70f0Sopenharmony_ci dmix->shmptr->hw.buffer_time = 1350d5ac70f0Sopenharmony_ci *hw_param_interval(&hw_params, SND_PCM_HW_PARAM_BUFFER_TIME); 1351d5ac70f0Sopenharmony_ci dmix->shmptr->hw.period_size = 1352d5ac70f0Sopenharmony_ci *hw_param_interval(&hw_params, SND_PCM_HW_PARAM_PERIOD_SIZE); 1353d5ac70f0Sopenharmony_ci dmix->shmptr->hw.period_time = 1354d5ac70f0Sopenharmony_ci *hw_param_interval(&hw_params, SND_PCM_HW_PARAM_PERIOD_TIME); 1355d5ac70f0Sopenharmony_ci dmix->shmptr->hw.periods = 1356d5ac70f0Sopenharmony_ci *hw_param_interval(&hw_params, SND_PCM_HW_PARAM_PERIODS); 1357d5ac70f0Sopenharmony_ci 1358d5ac70f0Sopenharmony_ci 1359d5ac70f0Sopenharmony_ci ret = snd_pcm_sw_params_current(spcm, &sw_params); 1360d5ac70f0Sopenharmony_ci if (ret < 0) { 1361d5ac70f0Sopenharmony_ci SNDERR("unable to get current sw_params"); 1362d5ac70f0Sopenharmony_ci return ret; 1363d5ac70f0Sopenharmony_ci } 1364d5ac70f0Sopenharmony_ci 1365d5ac70f0Sopenharmony_ci ret = snd_pcm_sw_params_get_boundary(&sw_params, &boundary); 1366d5ac70f0Sopenharmony_ci if (ret < 0) { 1367d5ac70f0Sopenharmony_ci SNDERR("unable to get boundary"); 1368d5ac70f0Sopenharmony_ci return ret; 1369d5ac70f0Sopenharmony_ci } 1370d5ac70f0Sopenharmony_ci ret = snd_pcm_sw_params_set_stop_threshold(spcm, &sw_params, boundary); 1371d5ac70f0Sopenharmony_ci if (ret < 0) { 1372d5ac70f0Sopenharmony_ci SNDERR("unable to set stop threshold"); 1373d5ac70f0Sopenharmony_ci return ret; 1374d5ac70f0Sopenharmony_ci } 1375d5ac70f0Sopenharmony_ci 1376d5ac70f0Sopenharmony_ci /* set timestamp mode to MMAP 1377d5ac70f0Sopenharmony_ci * the slave timestamp is copied appropriately in dsnoop/dmix/dshare 1378d5ac70f0Sopenharmony_ci * based on the tstamp_mode of each client 1379d5ac70f0Sopenharmony_ci */ 1380d5ac70f0Sopenharmony_ci ret = snd_pcm_sw_params_set_tstamp_mode(spcm, &sw_params, 1381d5ac70f0Sopenharmony_ci SND_PCM_TSTAMP_ENABLE); 1382d5ac70f0Sopenharmony_ci if (ret < 0) { 1383d5ac70f0Sopenharmony_ci SNDERR("unable to tstamp mode MMAP"); 1384d5ac70f0Sopenharmony_ci return ret; 1385d5ac70f0Sopenharmony_ci } 1386d5ac70f0Sopenharmony_ci 1387d5ac70f0Sopenharmony_ci if (dmix->tstamp_type != -1) { 1388d5ac70f0Sopenharmony_ci ret = snd_pcm_sw_params_set_tstamp_type(spcm, &sw_params, 1389d5ac70f0Sopenharmony_ci dmix->tstamp_type); 1390d5ac70f0Sopenharmony_ci if (ret < 0) { 1391d5ac70f0Sopenharmony_ci SNDERR("unable to set tstamp type"); 1392d5ac70f0Sopenharmony_ci return ret; 1393d5ac70f0Sopenharmony_ci } 1394d5ac70f0Sopenharmony_ci } 1395d5ac70f0Sopenharmony_ci 1396d5ac70f0Sopenharmony_ci if (dmix->type != SND_PCM_TYPE_DMIX && 1397d5ac70f0Sopenharmony_ci dmix->type != SND_PCM_TYPE_DSHARE) 1398d5ac70f0Sopenharmony_ci goto __skip_silencing; 1399d5ac70f0Sopenharmony_ci 1400d5ac70f0Sopenharmony_ci ret = snd_pcm_sw_params_set_silence_threshold(spcm, &sw_params, 0); 1401d5ac70f0Sopenharmony_ci if (ret < 0) { 1402d5ac70f0Sopenharmony_ci SNDERR("unable to set silence threshold"); 1403d5ac70f0Sopenharmony_ci return ret; 1404d5ac70f0Sopenharmony_ci } 1405d5ac70f0Sopenharmony_ci ret = snd_pcm_sw_params_set_silence_size(spcm, &sw_params, boundary); 1406d5ac70f0Sopenharmony_ci if (ret < 0) { 1407d5ac70f0Sopenharmony_ci SNDERR("unable to set silence threshold (please upgrade to 0.9.0rc8+ driver)"); 1408d5ac70f0Sopenharmony_ci return ret; 1409d5ac70f0Sopenharmony_ci } 1410d5ac70f0Sopenharmony_ci 1411d5ac70f0Sopenharmony_ci __skip_silencing: 1412d5ac70f0Sopenharmony_ci 1413d5ac70f0Sopenharmony_ci ret = snd_pcm_sw_params(spcm, &sw_params); 1414d5ac70f0Sopenharmony_ci if (ret < 0) { 1415d5ac70f0Sopenharmony_ci SNDERR("unable to install sw params (please upgrade to 0.9.0rc8+ driver)"); 1416d5ac70f0Sopenharmony_ci return ret; 1417d5ac70f0Sopenharmony_ci } 1418d5ac70f0Sopenharmony_ci 1419d5ac70f0Sopenharmony_ci if (dmix->type == SND_PCM_TYPE_DSHARE) { 1420d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *dst_areas; 1421d5ac70f0Sopenharmony_ci dst_areas = snd_pcm_mmap_areas(spcm); 1422d5ac70f0Sopenharmony_ci snd_pcm_areas_silence(dst_areas, 0, spcm->channels, 1423d5ac70f0Sopenharmony_ci spcm->buffer_size, spcm->format); 1424d5ac70f0Sopenharmony_ci } 1425d5ac70f0Sopenharmony_ci 1426d5ac70f0Sopenharmony_ci ret = snd_pcm_start(spcm); 1427d5ac70f0Sopenharmony_ci if (ret < 0) { 1428d5ac70f0Sopenharmony_ci SNDERR("unable to start PCM stream"); 1429d5ac70f0Sopenharmony_ci return ret; 1430d5ac70f0Sopenharmony_ci } 1431d5ac70f0Sopenharmony_ci 1432d5ac70f0Sopenharmony_ci if (snd_pcm_poll_descriptors_count(spcm) != 1) { 1433d5ac70f0Sopenharmony_ci SNDERR("unable to use hardware pcm with fd more than one!!!"); 1434d5ac70f0Sopenharmony_ci return ret; 1435d5ac70f0Sopenharmony_ci } 1436d5ac70f0Sopenharmony_ci snd_pcm_poll_descriptors(spcm, &fd, 1); 1437d5ac70f0Sopenharmony_ci dmix->hw_fd = fd.fd; 1438d5ac70f0Sopenharmony_ci 1439d5ac70f0Sopenharmony_ci save_slave_setting(dmix, spcm); 1440d5ac70f0Sopenharmony_ci 1441d5ac70f0Sopenharmony_ci /* Currently, we assume that each dmix client has the same 1442d5ac70f0Sopenharmony_ci * hw_params setting. 1443d5ac70f0Sopenharmony_ci * If the arbitrary hw_parmas is supported in future, 1444d5ac70f0Sopenharmony_ci * boundary has to be taken from the slave config but 1445d5ac70f0Sopenharmony_ci * recalculated for the native boundary size (for 32bit 1446d5ac70f0Sopenharmony_ci * emulation on 64bit arch). 1447d5ac70f0Sopenharmony_ci */ 1448d5ac70f0Sopenharmony_ci dmix->slave_buffer_size = spcm->buffer_size; 1449d5ac70f0Sopenharmony_ci dmix->slave_period_size = spcm->period_size; 1450d5ac70f0Sopenharmony_ci dmix->slave_boundary = spcm->boundary; 1451d5ac70f0Sopenharmony_ci 1452d5ac70f0Sopenharmony_ci spcm->donot_close = 1; 1453d5ac70f0Sopenharmony_ci 1454d5ac70f0Sopenharmony_ci { 1455d5ac70f0Sopenharmony_ci int ver = 0; 1456d5ac70f0Sopenharmony_ci ioctl(spcm->poll_fd, SNDRV_PCM_IOCTL_PVERSION, &ver); 1457d5ac70f0Sopenharmony_ci if (ver < SNDRV_PROTOCOL_VERSION(2, 0, 8)) 1458d5ac70f0Sopenharmony_ci dmix->shmptr->use_server = 1; 1459d5ac70f0Sopenharmony_ci } 1460d5ac70f0Sopenharmony_ci 1461d5ac70f0Sopenharmony_ci return 0; 1462d5ac70f0Sopenharmony_ci} 1463d5ac70f0Sopenharmony_ci 1464d5ac70f0Sopenharmony_ci/* 1465d5ac70f0Sopenharmony_ci * the trick is used here; we cannot use effectively the hardware handle because 1466d5ac70f0Sopenharmony_ci * we cannot drive multiple accesses to appl_ptr; so we use slave timer of given 1467d5ac70f0Sopenharmony_ci * PCM hardware handle; it's not this easy and cheap? 1468d5ac70f0Sopenharmony_ci */ 1469d5ac70f0Sopenharmony_ciint snd_pcm_direct_initialize_poll_fd(snd_pcm_direct_t *dmix) 1470d5ac70f0Sopenharmony_ci{ 1471d5ac70f0Sopenharmony_ci int ret; 1472d5ac70f0Sopenharmony_ci snd_pcm_info_t info = {0}; 1473d5ac70f0Sopenharmony_ci char name[128]; 1474d5ac70f0Sopenharmony_ci int capture = dmix->type == SND_PCM_TYPE_DSNOOP ? 1 : 0; 1475d5ac70f0Sopenharmony_ci 1476d5ac70f0Sopenharmony_ci dmix->tread = 1; 1477d5ac70f0Sopenharmony_ci dmix->timer_need_poll = 0; 1478d5ac70f0Sopenharmony_ci dmix->timer_ticks = 1; 1479d5ac70f0Sopenharmony_ci ret = snd_pcm_info(dmix->spcm, &info); 1480d5ac70f0Sopenharmony_ci if (ret < 0) { 1481d5ac70f0Sopenharmony_ci SNDERR("unable to info for slave pcm"); 1482d5ac70f0Sopenharmony_ci return ret; 1483d5ac70f0Sopenharmony_ci } 1484d5ac70f0Sopenharmony_ci sprintf(name, "hw:CLASS=%i,SCLASS=0,CARD=%i,DEV=%i,SUBDEV=%i", 1485d5ac70f0Sopenharmony_ci (int)SND_TIMER_CLASS_PCM, 1486d5ac70f0Sopenharmony_ci snd_pcm_info_get_card(&info), 1487d5ac70f0Sopenharmony_ci snd_pcm_info_get_device(&info), 1488d5ac70f0Sopenharmony_ci snd_pcm_info_get_subdevice(&info) * 2 + capture); 1489d5ac70f0Sopenharmony_ci ret = snd_timer_open(&dmix->timer, name, 1490d5ac70f0Sopenharmony_ci SND_TIMER_OPEN_NONBLOCK | SND_TIMER_OPEN_TREAD); 1491d5ac70f0Sopenharmony_ci if (ret < 0) { 1492d5ac70f0Sopenharmony_ci dmix->tread = 0; 1493d5ac70f0Sopenharmony_ci ret = snd_timer_open(&dmix->timer, name, 1494d5ac70f0Sopenharmony_ci SND_TIMER_OPEN_NONBLOCK); 1495d5ac70f0Sopenharmony_ci if (ret < 0) { 1496d5ac70f0Sopenharmony_ci SNDERR("unable to open timer '%s'", name); 1497d5ac70f0Sopenharmony_ci return ret; 1498d5ac70f0Sopenharmony_ci } 1499d5ac70f0Sopenharmony_ci } 1500d5ac70f0Sopenharmony_ci 1501d5ac70f0Sopenharmony_ci if (snd_timer_poll_descriptors_count(dmix->timer) != 1) { 1502d5ac70f0Sopenharmony_ci SNDERR("unable to use timer '%s' with more than one fd!", name); 1503d5ac70f0Sopenharmony_ci return ret; 1504d5ac70f0Sopenharmony_ci } 1505d5ac70f0Sopenharmony_ci snd_timer_poll_descriptors(dmix->timer, &dmix->timer_fd, 1); 1506d5ac70f0Sopenharmony_ci dmix->poll_fd = dmix->timer_fd.fd; 1507d5ac70f0Sopenharmony_ci 1508d5ac70f0Sopenharmony_ci dmix->timer_events = (1<<SND_TIMER_EVENT_MSUSPEND) | 1509d5ac70f0Sopenharmony_ci (1<<SND_TIMER_EVENT_MRESUME) | 1510d5ac70f0Sopenharmony_ci (1<<SND_TIMER_EVENT_MSTOP) | 1511d5ac70f0Sopenharmony_ci (1<<SND_TIMER_EVENT_STOP); 1512d5ac70f0Sopenharmony_ci 1513d5ac70f0Sopenharmony_ci /* 1514d5ac70f0Sopenharmony_ci * Some hacks for older kernel drivers 1515d5ac70f0Sopenharmony_ci */ 1516d5ac70f0Sopenharmony_ci { 1517d5ac70f0Sopenharmony_ci int ver = 0; 1518d5ac70f0Sopenharmony_ci ioctl(dmix->poll_fd, SNDRV_TIMER_IOCTL_PVERSION, &ver); 1519d5ac70f0Sopenharmony_ci /* In older versions, check via poll before read() is needed 1520d5ac70f0Sopenharmony_ci * because of the confliction between TIMER_START and 1521d5ac70f0Sopenharmony_ci * FIONBIO ioctls. 1522d5ac70f0Sopenharmony_ci */ 1523d5ac70f0Sopenharmony_ci if (ver < SNDRV_PROTOCOL_VERSION(2, 0, 4)) 1524d5ac70f0Sopenharmony_ci dmix->timer_need_poll = 1; 1525d5ac70f0Sopenharmony_ci /* 1526d5ac70f0Sopenharmony_ci * In older versions, timer uses pause events instead 1527d5ac70f0Sopenharmony_ci * suspend/resume events. 1528d5ac70f0Sopenharmony_ci */ 1529d5ac70f0Sopenharmony_ci if (ver < SNDRV_PROTOCOL_VERSION(2, 0, 5)) { 1530d5ac70f0Sopenharmony_ci dmix->timer_events &= ~((1<<SND_TIMER_EVENT_MSUSPEND) | 1531d5ac70f0Sopenharmony_ci (1<<SND_TIMER_EVENT_MRESUME)); 1532d5ac70f0Sopenharmony_ci dmix->timer_events |= (1<<SND_TIMER_EVENT_MPAUSE) | 1533d5ac70f0Sopenharmony_ci (1<<SND_TIMER_EVENT_MCONTINUE); 1534d5ac70f0Sopenharmony_ci } 1535d5ac70f0Sopenharmony_ci /* In older versions, use SND_TIMER_EVENT_START too. 1536d5ac70f0Sopenharmony_ci */ 1537d5ac70f0Sopenharmony_ci if (ver < SNDRV_PROTOCOL_VERSION(2, 0, 6)) 1538d5ac70f0Sopenharmony_ci dmix->timer_events |= 1<<SND_TIMER_EVENT_START; 1539d5ac70f0Sopenharmony_ci } 1540d5ac70f0Sopenharmony_ci return 0; 1541d5ac70f0Sopenharmony_ci} 1542d5ac70f0Sopenharmony_ci 1543d5ac70f0Sopenharmony_cistatic snd_pcm_uframes_t recalc_boundary_size(unsigned long long bsize, snd_pcm_uframes_t buffer_size) 1544d5ac70f0Sopenharmony_ci{ 1545d5ac70f0Sopenharmony_ci if (bsize > LONG_MAX) { 1546d5ac70f0Sopenharmony_ci bsize = buffer_size; 1547d5ac70f0Sopenharmony_ci while (bsize * 2 <= LONG_MAX - buffer_size) 1548d5ac70f0Sopenharmony_ci bsize *= 2; 1549d5ac70f0Sopenharmony_ci } 1550d5ac70f0Sopenharmony_ci return (snd_pcm_uframes_t)bsize; 1551d5ac70f0Sopenharmony_ci} 1552d5ac70f0Sopenharmony_ci 1553d5ac70f0Sopenharmony_ci#define COPY_SLAVE(field) (spcm->field = dmix->shmptr->s.field) 1554d5ac70f0Sopenharmony_ci 1555d5ac70f0Sopenharmony_ci/* copy the slave setting */ 1556d5ac70f0Sopenharmony_cistatic void copy_slave_setting(snd_pcm_direct_t *dmix, snd_pcm_t *spcm) 1557d5ac70f0Sopenharmony_ci{ 1558d5ac70f0Sopenharmony_ci COPY_SLAVE(access); 1559d5ac70f0Sopenharmony_ci COPY_SLAVE(format); 1560d5ac70f0Sopenharmony_ci COPY_SLAVE(subformat); 1561d5ac70f0Sopenharmony_ci COPY_SLAVE(channels); 1562d5ac70f0Sopenharmony_ci COPY_SLAVE(rate); 1563d5ac70f0Sopenharmony_ci COPY_SLAVE(period_size); 1564d5ac70f0Sopenharmony_ci COPY_SLAVE(period_time); 1565d5ac70f0Sopenharmony_ci COPY_SLAVE(periods); 1566d5ac70f0Sopenharmony_ci COPY_SLAVE(tstamp_mode); 1567d5ac70f0Sopenharmony_ci COPY_SLAVE(tstamp_type); 1568d5ac70f0Sopenharmony_ci COPY_SLAVE(period_step); 1569d5ac70f0Sopenharmony_ci COPY_SLAVE(avail_min); 1570d5ac70f0Sopenharmony_ci COPY_SLAVE(start_threshold); 1571d5ac70f0Sopenharmony_ci COPY_SLAVE(stop_threshold); 1572d5ac70f0Sopenharmony_ci COPY_SLAVE(silence_threshold); 1573d5ac70f0Sopenharmony_ci COPY_SLAVE(silence_size); 1574d5ac70f0Sopenharmony_ci COPY_SLAVE(boundary); 1575d5ac70f0Sopenharmony_ci COPY_SLAVE(info); 1576d5ac70f0Sopenharmony_ci COPY_SLAVE(msbits); 1577d5ac70f0Sopenharmony_ci COPY_SLAVE(rate_num); 1578d5ac70f0Sopenharmony_ci COPY_SLAVE(rate_den); 1579d5ac70f0Sopenharmony_ci COPY_SLAVE(hw_flags); 1580d5ac70f0Sopenharmony_ci COPY_SLAVE(fifo_size); 1581d5ac70f0Sopenharmony_ci COPY_SLAVE(buffer_size); 1582d5ac70f0Sopenharmony_ci COPY_SLAVE(buffer_time); 1583d5ac70f0Sopenharmony_ci COPY_SLAVE(sample_bits); 1584d5ac70f0Sopenharmony_ci COPY_SLAVE(frame_bits); 1585d5ac70f0Sopenharmony_ci 1586d5ac70f0Sopenharmony_ci spcm->info &= ~SND_PCM_INFO_PAUSE; 1587d5ac70f0Sopenharmony_ci spcm->boundary = recalc_boundary_size(dmix->shmptr->s.boundary, spcm->buffer_size); 1588d5ac70f0Sopenharmony_ci} 1589d5ac70f0Sopenharmony_ci 1590d5ac70f0Sopenharmony_ci#undef COPY_SLAVE 1591d5ac70f0Sopenharmony_ci 1592d5ac70f0Sopenharmony_ci 1593d5ac70f0Sopenharmony_ci/* 1594d5ac70f0Sopenharmony_ci * open a slave PCM as secondary client (dup'ed fd) 1595d5ac70f0Sopenharmony_ci */ 1596d5ac70f0Sopenharmony_ciint snd_pcm_direct_open_secondary_client(snd_pcm_t **spcmp, snd_pcm_direct_t *dmix, const char *client_name) 1597d5ac70f0Sopenharmony_ci{ 1598d5ac70f0Sopenharmony_ci int ret; 1599d5ac70f0Sopenharmony_ci snd_pcm_t *spcm; 1600d5ac70f0Sopenharmony_ci 1601d5ac70f0Sopenharmony_ci ret = snd_pcm_hw_open_fd(spcmp, client_name, dmix->hw_fd, 0); 1602d5ac70f0Sopenharmony_ci if (ret < 0) { 1603d5ac70f0Sopenharmony_ci SNDERR("unable to open hardware"); 1604d5ac70f0Sopenharmony_ci return ret; 1605d5ac70f0Sopenharmony_ci } 1606d5ac70f0Sopenharmony_ci 1607d5ac70f0Sopenharmony_ci spcm = *spcmp; 1608d5ac70f0Sopenharmony_ci spcm->donot_close = 1; 1609d5ac70f0Sopenharmony_ci spcm->setup = 1; 1610d5ac70f0Sopenharmony_ci 1611d5ac70f0Sopenharmony_ci copy_slave_setting(dmix, spcm); 1612d5ac70f0Sopenharmony_ci 1613d5ac70f0Sopenharmony_ci /* Use the slave setting as SPCM, so far */ 1614d5ac70f0Sopenharmony_ci dmix->slave_buffer_size = spcm->buffer_size; 1615d5ac70f0Sopenharmony_ci dmix->slave_period_size = dmix->shmptr->s.period_size; 1616d5ac70f0Sopenharmony_ci dmix->slave_boundary = spcm->boundary; 1617d5ac70f0Sopenharmony_ci dmix->recoveries = dmix->shmptr->s.recoveries; 1618d5ac70f0Sopenharmony_ci 1619d5ac70f0Sopenharmony_ci ret = snd_pcm_mmap(spcm); 1620d5ac70f0Sopenharmony_ci if (ret < 0) { 1621d5ac70f0Sopenharmony_ci SNDERR("unable to mmap channels"); 1622d5ac70f0Sopenharmony_ci return ret; 1623d5ac70f0Sopenharmony_ci } 1624d5ac70f0Sopenharmony_ci return 0; 1625d5ac70f0Sopenharmony_ci} 1626d5ac70f0Sopenharmony_ci 1627d5ac70f0Sopenharmony_ci/* 1628d5ac70f0Sopenharmony_ci * open a slave PCM as secondary client (dup'ed fd) 1629d5ac70f0Sopenharmony_ci */ 1630d5ac70f0Sopenharmony_ciint snd_pcm_direct_initialize_secondary_slave(snd_pcm_direct_t *dmix, 1631d5ac70f0Sopenharmony_ci snd_pcm_t *spcm, 1632d5ac70f0Sopenharmony_ci struct slave_params *params ATTRIBUTE_UNUSED) 1633d5ac70f0Sopenharmony_ci{ 1634d5ac70f0Sopenharmony_ci int ret; 1635d5ac70f0Sopenharmony_ci 1636d5ac70f0Sopenharmony_ci spcm->donot_close = 1; 1637d5ac70f0Sopenharmony_ci spcm->setup = 1; 1638d5ac70f0Sopenharmony_ci 1639d5ac70f0Sopenharmony_ci copy_slave_setting(dmix, spcm); 1640d5ac70f0Sopenharmony_ci 1641d5ac70f0Sopenharmony_ci /* Use the slave setting as SPCM, so far */ 1642d5ac70f0Sopenharmony_ci dmix->slave_buffer_size = spcm->buffer_size; 1643d5ac70f0Sopenharmony_ci dmix->slave_period_size = dmix->shmptr->s.period_size; 1644d5ac70f0Sopenharmony_ci dmix->slave_boundary = spcm->boundary; 1645d5ac70f0Sopenharmony_ci 1646d5ac70f0Sopenharmony_ci ret = snd_pcm_mmap(spcm); 1647d5ac70f0Sopenharmony_ci if (ret < 0) { 1648d5ac70f0Sopenharmony_ci SNDERR("unable to mmap channels"); 1649d5ac70f0Sopenharmony_ci return ret; 1650d5ac70f0Sopenharmony_ci } 1651d5ac70f0Sopenharmony_ci return 0; 1652d5ac70f0Sopenharmony_ci} 1653d5ac70f0Sopenharmony_ci 1654d5ac70f0Sopenharmony_ciint snd_pcm_direct_set_timer_params(snd_pcm_direct_t *dmix) 1655d5ac70f0Sopenharmony_ci{ 1656d5ac70f0Sopenharmony_ci snd_timer_params_t params = {0}; 1657d5ac70f0Sopenharmony_ci unsigned int filter; 1658d5ac70f0Sopenharmony_ci int ret; 1659d5ac70f0Sopenharmony_ci 1660d5ac70f0Sopenharmony_ci snd_timer_params_set_auto_start(¶ms, 1); 1661d5ac70f0Sopenharmony_ci if (dmix->type != SND_PCM_TYPE_DSNOOP) 1662d5ac70f0Sopenharmony_ci snd_timer_params_set_early_event(¶ms, 1); 1663d5ac70f0Sopenharmony_ci snd_timer_params_set_ticks(¶ms, dmix->timer_ticks); 1664d5ac70f0Sopenharmony_ci if (dmix->tread) { 1665d5ac70f0Sopenharmony_ci filter = (1<<SND_TIMER_EVENT_TICK) | 1666d5ac70f0Sopenharmony_ci dmix->timer_events; 1667d5ac70f0Sopenharmony_ci INTERNAL(snd_timer_params_set_filter)(¶ms, filter); 1668d5ac70f0Sopenharmony_ci } 1669d5ac70f0Sopenharmony_ci ret = snd_timer_params(dmix->timer, ¶ms); 1670d5ac70f0Sopenharmony_ci if (ret < 0) { 1671d5ac70f0Sopenharmony_ci SNDERR("unable to set timer parameters"); 1672d5ac70f0Sopenharmony_ci return ret; 1673d5ac70f0Sopenharmony_ci } 1674d5ac70f0Sopenharmony_ci return 0; 1675d5ac70f0Sopenharmony_ci} 1676d5ac70f0Sopenharmony_ci 1677d5ac70f0Sopenharmony_ci/* 1678d5ac70f0Sopenharmony_ci * ring buffer operation 1679d5ac70f0Sopenharmony_ci */ 1680d5ac70f0Sopenharmony_ciint snd_pcm_direct_check_interleave(snd_pcm_direct_t *dmix, snd_pcm_t *pcm) 1681d5ac70f0Sopenharmony_ci{ 1682d5ac70f0Sopenharmony_ci unsigned int chn, channels; 1683d5ac70f0Sopenharmony_ci int bits; 1684d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *dst_areas; 1685d5ac70f0Sopenharmony_ci const snd_pcm_channel_area_t *src_areas; 1686d5ac70f0Sopenharmony_ci 1687d5ac70f0Sopenharmony_ci bits = snd_pcm_format_physical_width(pcm->format); 1688d5ac70f0Sopenharmony_ci if ((bits % 8) != 0) 1689d5ac70f0Sopenharmony_ci goto __nointerleaved; 1690d5ac70f0Sopenharmony_ci channels = dmix->channels; 1691d5ac70f0Sopenharmony_ci if (channels != dmix->spcm->channels) 1692d5ac70f0Sopenharmony_ci goto __nointerleaved; 1693d5ac70f0Sopenharmony_ci dst_areas = snd_pcm_mmap_areas(dmix->spcm); 1694d5ac70f0Sopenharmony_ci src_areas = snd_pcm_mmap_areas(pcm); 1695d5ac70f0Sopenharmony_ci for (chn = 1; chn < channels; chn++) { 1696d5ac70f0Sopenharmony_ci if (dst_areas[chn-1].addr != dst_areas[chn].addr) 1697d5ac70f0Sopenharmony_ci goto __nointerleaved; 1698d5ac70f0Sopenharmony_ci if (src_areas[chn-1].addr != src_areas[chn].addr) 1699d5ac70f0Sopenharmony_ci goto __nointerleaved; 1700d5ac70f0Sopenharmony_ci } 1701d5ac70f0Sopenharmony_ci for (chn = 0; chn < channels; chn++) { 1702d5ac70f0Sopenharmony_ci if (dmix->bindings && dmix->bindings[chn] != chn) 1703d5ac70f0Sopenharmony_ci goto __nointerleaved; 1704d5ac70f0Sopenharmony_ci if (dst_areas[chn].first != chn * bits || 1705d5ac70f0Sopenharmony_ci dst_areas[chn].step != channels * bits) 1706d5ac70f0Sopenharmony_ci goto __nointerleaved; 1707d5ac70f0Sopenharmony_ci if (src_areas[chn].first != chn * bits || 1708d5ac70f0Sopenharmony_ci src_areas[chn].step != channels * bits) 1709d5ac70f0Sopenharmony_ci goto __nointerleaved; 1710d5ac70f0Sopenharmony_ci } 1711d5ac70f0Sopenharmony_ci return dmix->interleaved = 1; 1712d5ac70f0Sopenharmony_ci__nointerleaved: 1713d5ac70f0Sopenharmony_ci return dmix->interleaved = 0; 1714d5ac70f0Sopenharmony_ci} 1715d5ac70f0Sopenharmony_ci 1716d5ac70f0Sopenharmony_ci/* 1717d5ac70f0Sopenharmony_ci * parse the channel map 1718d5ac70f0Sopenharmony_ci * id == client channel 1719d5ac70f0Sopenharmony_ci * value == slave's channel 1720d5ac70f0Sopenharmony_ci */ 1721d5ac70f0Sopenharmony_ciint snd_pcm_direct_parse_bindings(snd_pcm_direct_t *dmix, 1722d5ac70f0Sopenharmony_ci struct slave_params *params, 1723d5ac70f0Sopenharmony_ci snd_config_t *cfg) 1724d5ac70f0Sopenharmony_ci{ 1725d5ac70f0Sopenharmony_ci snd_config_iterator_t i, next; 1726d5ac70f0Sopenharmony_ci unsigned int chn, chn1, count = 0; 1727d5ac70f0Sopenharmony_ci unsigned int *bindings; 1728d5ac70f0Sopenharmony_ci int err; 1729d5ac70f0Sopenharmony_ci 1730d5ac70f0Sopenharmony_ci dmix->channels = UINT_MAX; 1731d5ac70f0Sopenharmony_ci if (cfg == NULL) 1732d5ac70f0Sopenharmony_ci return 0; 1733d5ac70f0Sopenharmony_ci if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { 1734d5ac70f0Sopenharmony_ci SNDERR("invalid type for bindings"); 1735d5ac70f0Sopenharmony_ci return -EINVAL; 1736d5ac70f0Sopenharmony_ci } 1737d5ac70f0Sopenharmony_ci snd_config_for_each(i, next, cfg) { 1738d5ac70f0Sopenharmony_ci snd_config_t *n = snd_config_iterator_entry(i); 1739d5ac70f0Sopenharmony_ci const char *id; 1740d5ac70f0Sopenharmony_ci long cchannel; 1741d5ac70f0Sopenharmony_ci if (snd_config_get_id(n, &id) < 0) 1742d5ac70f0Sopenharmony_ci continue; 1743d5ac70f0Sopenharmony_ci err = safe_strtol(id, &cchannel); 1744d5ac70f0Sopenharmony_ci if (err < 0 || cchannel < 0) { 1745d5ac70f0Sopenharmony_ci SNDERR("invalid client channel in binding: %s", id); 1746d5ac70f0Sopenharmony_ci return -EINVAL; 1747d5ac70f0Sopenharmony_ci } 1748d5ac70f0Sopenharmony_ci if ((unsigned)cchannel >= count) 1749d5ac70f0Sopenharmony_ci count = cchannel + 1; 1750d5ac70f0Sopenharmony_ci } 1751d5ac70f0Sopenharmony_ci if (count == 0) 1752d5ac70f0Sopenharmony_ci return 0; 1753d5ac70f0Sopenharmony_ci if (count > 1024) { 1754d5ac70f0Sopenharmony_ci SNDERR("client channel out of range"); 1755d5ac70f0Sopenharmony_ci return -EINVAL; 1756d5ac70f0Sopenharmony_ci } 1757d5ac70f0Sopenharmony_ci bindings = malloc(count * sizeof(unsigned int)); 1758d5ac70f0Sopenharmony_ci if (bindings == NULL) 1759d5ac70f0Sopenharmony_ci return -ENOMEM; 1760d5ac70f0Sopenharmony_ci for (chn = 0; chn < count; chn++) 1761d5ac70f0Sopenharmony_ci bindings[chn] = UINT_MAX; /* don't route */ 1762d5ac70f0Sopenharmony_ci snd_config_for_each(i, next, cfg) { 1763d5ac70f0Sopenharmony_ci snd_config_t *n = snd_config_iterator_entry(i); 1764d5ac70f0Sopenharmony_ci const char *id; 1765d5ac70f0Sopenharmony_ci long cchannel, schannel; 1766d5ac70f0Sopenharmony_ci if (snd_config_get_id(n, &id) < 0) 1767d5ac70f0Sopenharmony_ci continue; 1768d5ac70f0Sopenharmony_ci safe_strtol(id, &cchannel); 1769d5ac70f0Sopenharmony_ci if (snd_config_get_integer(n, &schannel) < 0) { 1770d5ac70f0Sopenharmony_ci SNDERR("unable to get slave channel (should be integer type) in binding: %s", id); 1771d5ac70f0Sopenharmony_ci free(bindings); 1772d5ac70f0Sopenharmony_ci return -EINVAL; 1773d5ac70f0Sopenharmony_ci } 1774d5ac70f0Sopenharmony_ci if (schannel < 0 || schannel >= params->channels) { 1775d5ac70f0Sopenharmony_ci SNDERR("invalid slave channel number %ld in binding to %ld", 1776d5ac70f0Sopenharmony_ci schannel, cchannel); 1777d5ac70f0Sopenharmony_ci free(bindings); 1778d5ac70f0Sopenharmony_ci return -EINVAL; 1779d5ac70f0Sopenharmony_ci } 1780d5ac70f0Sopenharmony_ci bindings[cchannel] = schannel; 1781d5ac70f0Sopenharmony_ci } 1782d5ac70f0Sopenharmony_ci if (dmix->type == SND_PCM_TYPE_DSNOOP || 1783d5ac70f0Sopenharmony_ci ! dmix->bindings) 1784d5ac70f0Sopenharmony_ci goto __skip_same_dst; 1785d5ac70f0Sopenharmony_ci for (chn = 0; chn < count; chn++) { 1786d5ac70f0Sopenharmony_ci for (chn1 = 0; chn1 < count; chn1++) { 1787d5ac70f0Sopenharmony_ci if (chn == chn1) 1788d5ac70f0Sopenharmony_ci continue; 1789d5ac70f0Sopenharmony_ci if (bindings[chn] == dmix->bindings[chn1]) { 1790d5ac70f0Sopenharmony_ci SNDERR("unable to route channels %d,%d to same destination %d", chn, chn1, bindings[chn]); 1791d5ac70f0Sopenharmony_ci free(bindings); 1792d5ac70f0Sopenharmony_ci return -EINVAL; 1793d5ac70f0Sopenharmony_ci } 1794d5ac70f0Sopenharmony_ci } 1795d5ac70f0Sopenharmony_ci } 1796d5ac70f0Sopenharmony_ci __skip_same_dst: 1797d5ac70f0Sopenharmony_ci dmix->bindings = bindings; 1798d5ac70f0Sopenharmony_ci dmix->channels = count; 1799d5ac70f0Sopenharmony_ci return 0; 1800d5ac70f0Sopenharmony_ci} 1801d5ac70f0Sopenharmony_ci 1802d5ac70f0Sopenharmony_ci/* 1803d5ac70f0Sopenharmony_ci * parse slave config and calculate the ipc_key offset 1804d5ac70f0Sopenharmony_ci */ 1805d5ac70f0Sopenharmony_ci 1806d5ac70f0Sopenharmony_cistatic int _snd_pcm_direct_get_slave_ipc_offset(snd_config_t *root, 1807d5ac70f0Sopenharmony_ci snd_config_t *sconf, 1808d5ac70f0Sopenharmony_ci int direction, 1809d5ac70f0Sopenharmony_ci int hop) 1810d5ac70f0Sopenharmony_ci{ 1811d5ac70f0Sopenharmony_ci snd_config_iterator_t i, next; 1812d5ac70f0Sopenharmony_ci snd_config_t *pcm_conf, *pcm_conf2; 1813d5ac70f0Sopenharmony_ci int err; 1814d5ac70f0Sopenharmony_ci long card = 0, device = 0, subdevice = 0; 1815d5ac70f0Sopenharmony_ci const char *str; 1816d5ac70f0Sopenharmony_ci 1817d5ac70f0Sopenharmony_ci if (snd_config_get_string(sconf, &str) >= 0) { 1818d5ac70f0Sopenharmony_ci if (hop > SND_CONF_MAX_HOPS) { 1819d5ac70f0Sopenharmony_ci SNDERR("Too many definition levels (looped?)"); 1820d5ac70f0Sopenharmony_ci return -EINVAL; 1821d5ac70f0Sopenharmony_ci } 1822d5ac70f0Sopenharmony_ci err = snd_config_search_definition(root, "pcm", str, &pcm_conf); 1823d5ac70f0Sopenharmony_ci if (err < 0) { 1824d5ac70f0Sopenharmony_ci SNDERR("Unknown slave PCM %s", str); 1825d5ac70f0Sopenharmony_ci return err; 1826d5ac70f0Sopenharmony_ci } 1827d5ac70f0Sopenharmony_ci err = _snd_pcm_direct_get_slave_ipc_offset(root, pcm_conf, 1828d5ac70f0Sopenharmony_ci direction, 1829d5ac70f0Sopenharmony_ci hop + 1); 1830d5ac70f0Sopenharmony_ci snd_config_delete(pcm_conf); 1831d5ac70f0Sopenharmony_ci return err; 1832d5ac70f0Sopenharmony_ci } 1833d5ac70f0Sopenharmony_ci 1834d5ac70f0Sopenharmony_ci#if 0 /* for debug purposes */ 1835d5ac70f0Sopenharmony_ci { 1836d5ac70f0Sopenharmony_ci snd_output_t *out; 1837d5ac70f0Sopenharmony_ci snd_output_stdio_attach(&out, stderr, 0); 1838d5ac70f0Sopenharmony_ci snd_config_save(sconf, out); 1839d5ac70f0Sopenharmony_ci snd_output_close(out); 1840d5ac70f0Sopenharmony_ci } 1841d5ac70f0Sopenharmony_ci#endif 1842d5ac70f0Sopenharmony_ci 1843d5ac70f0Sopenharmony_ci if (snd_config_search(sconf, "slave", &pcm_conf) >= 0) { 1844d5ac70f0Sopenharmony_ci if (snd_config_search(pcm_conf, "pcm", &pcm_conf) >= 0) { 1845d5ac70f0Sopenharmony_ci return _snd_pcm_direct_get_slave_ipc_offset(root, 1846d5ac70f0Sopenharmony_ci pcm_conf, 1847d5ac70f0Sopenharmony_ci direction, 1848d5ac70f0Sopenharmony_ci hop + 1); 1849d5ac70f0Sopenharmony_ci } else { 1850d5ac70f0Sopenharmony_ci if (snd_config_get_string(pcm_conf, &str) >= 0 && 1851d5ac70f0Sopenharmony_ci snd_config_search_definition(root, "pcm_slave", 1852d5ac70f0Sopenharmony_ci str, &pcm_conf) >= 0) { 1853d5ac70f0Sopenharmony_ci if (snd_config_search(pcm_conf, "pcm", 1854d5ac70f0Sopenharmony_ci &pcm_conf2) >= 0) { 1855d5ac70f0Sopenharmony_ci err = 1856d5ac70f0Sopenharmony_ci _snd_pcm_direct_get_slave_ipc_offset( 1857d5ac70f0Sopenharmony_ci root, pcm_conf2, direction, hop + 1); 1858d5ac70f0Sopenharmony_ci snd_config_delete(pcm_conf); 1859d5ac70f0Sopenharmony_ci return err; 1860d5ac70f0Sopenharmony_ci } 1861d5ac70f0Sopenharmony_ci snd_config_delete(pcm_conf); 1862d5ac70f0Sopenharmony_ci } 1863d5ac70f0Sopenharmony_ci } 1864d5ac70f0Sopenharmony_ci } 1865d5ac70f0Sopenharmony_ci 1866d5ac70f0Sopenharmony_ci snd_config_for_each(i, next, sconf) { 1867d5ac70f0Sopenharmony_ci snd_config_t *n = snd_config_iterator_entry(i); 1868d5ac70f0Sopenharmony_ci const char *id, *str; 1869d5ac70f0Sopenharmony_ci if (snd_config_get_id(n, &id) < 0) 1870d5ac70f0Sopenharmony_ci continue; 1871d5ac70f0Sopenharmony_ci if (strcmp(id, "type") == 0) { 1872d5ac70f0Sopenharmony_ci err = snd_config_get_string(n, &str); 1873d5ac70f0Sopenharmony_ci if (err < 0) { 1874d5ac70f0Sopenharmony_ci SNDERR("Invalid value for PCM type definition"); 1875d5ac70f0Sopenharmony_ci return -EINVAL; 1876d5ac70f0Sopenharmony_ci } 1877d5ac70f0Sopenharmony_ci if (strcmp(str, "hw")) { 1878d5ac70f0Sopenharmony_ci SNDERR("Invalid type '%s' for slave PCM", str); 1879d5ac70f0Sopenharmony_ci return -EINVAL; 1880d5ac70f0Sopenharmony_ci } 1881d5ac70f0Sopenharmony_ci continue; 1882d5ac70f0Sopenharmony_ci } 1883d5ac70f0Sopenharmony_ci if (strcmp(id, "card") == 0) { 1884d5ac70f0Sopenharmony_ci err = snd_config_get_card(n); 1885d5ac70f0Sopenharmony_ci if (err < 0) 1886d5ac70f0Sopenharmony_ci return err; 1887d5ac70f0Sopenharmony_ci card = err; 1888d5ac70f0Sopenharmony_ci continue; 1889d5ac70f0Sopenharmony_ci } 1890d5ac70f0Sopenharmony_ci if (strcmp(id, "device") == 0) { 1891d5ac70f0Sopenharmony_ci err = snd_config_get_integer(n, &device); 1892d5ac70f0Sopenharmony_ci if (err < 0) { 1893d5ac70f0Sopenharmony_ci SNDERR("Invalid type for %s", id); 1894d5ac70f0Sopenharmony_ci return err; 1895d5ac70f0Sopenharmony_ci } 1896d5ac70f0Sopenharmony_ci continue; 1897d5ac70f0Sopenharmony_ci } 1898d5ac70f0Sopenharmony_ci if (strcmp(id, "subdevice") == 0) { 1899d5ac70f0Sopenharmony_ci err = snd_config_get_integer(n, &subdevice); 1900d5ac70f0Sopenharmony_ci if (err < 0) { 1901d5ac70f0Sopenharmony_ci SNDERR("Invalid type for %s", id); 1902d5ac70f0Sopenharmony_ci return err; 1903d5ac70f0Sopenharmony_ci } 1904d5ac70f0Sopenharmony_ci continue; 1905d5ac70f0Sopenharmony_ci } 1906d5ac70f0Sopenharmony_ci } 1907d5ac70f0Sopenharmony_ci if (device < 0) 1908d5ac70f0Sopenharmony_ci device = 0; 1909d5ac70f0Sopenharmony_ci if (subdevice < 0) 1910d5ac70f0Sopenharmony_ci subdevice = 0; 1911d5ac70f0Sopenharmony_ci return (direction << 1) + (device << 2) + (subdevice << 8) + (card << 12); 1912d5ac70f0Sopenharmony_ci} 1913d5ac70f0Sopenharmony_ci 1914d5ac70f0Sopenharmony_cistatic int snd_pcm_direct_get_slave_ipc_offset(snd_config_t *root, 1915d5ac70f0Sopenharmony_ci snd_config_t *sconf, 1916d5ac70f0Sopenharmony_ci int direction) 1917d5ac70f0Sopenharmony_ci{ 1918d5ac70f0Sopenharmony_ci return _snd_pcm_direct_get_slave_ipc_offset(root, sconf, direction, 0); 1919d5ac70f0Sopenharmony_ci} 1920d5ac70f0Sopenharmony_ci 1921d5ac70f0Sopenharmony_ciint snd_pcm_direct_parse_open_conf(snd_config_t *root, snd_config_t *conf, 1922d5ac70f0Sopenharmony_ci int stream, struct snd_pcm_direct_open_conf *rec) 1923d5ac70f0Sopenharmony_ci{ 1924d5ac70f0Sopenharmony_ci snd_config_iterator_t i, next; 1925d5ac70f0Sopenharmony_ci int ipc_key_add_uid = 0; 1926d5ac70f0Sopenharmony_ci snd_config_t *n; 1927d5ac70f0Sopenharmony_ci int err; 1928d5ac70f0Sopenharmony_ci 1929d5ac70f0Sopenharmony_ci rec->slave = NULL; 1930d5ac70f0Sopenharmony_ci rec->bindings = NULL; 1931d5ac70f0Sopenharmony_ci rec->ipc_key = 0; 1932d5ac70f0Sopenharmony_ci rec->ipc_perm = 0600; 1933d5ac70f0Sopenharmony_ci rec->ipc_gid = -1; 1934d5ac70f0Sopenharmony_ci rec->slowptr = 1; 1935d5ac70f0Sopenharmony_ci rec->max_periods = 0; 1936d5ac70f0Sopenharmony_ci rec->var_periodsize = 0; 1937d5ac70f0Sopenharmony_ci#ifdef LOCKLESS_DMIX_DEFAULT 1938d5ac70f0Sopenharmony_ci rec->direct_memory_access = 1; 1939d5ac70f0Sopenharmony_ci#else 1940d5ac70f0Sopenharmony_ci rec->direct_memory_access = 0; 1941d5ac70f0Sopenharmony_ci#endif 1942d5ac70f0Sopenharmony_ci rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_AUTO; 1943d5ac70f0Sopenharmony_ci rec->tstamp_type = -1; 1944d5ac70f0Sopenharmony_ci 1945d5ac70f0Sopenharmony_ci /* read defaults */ 1946d5ac70f0Sopenharmony_ci if (snd_config_search(root, "defaults.pcm.dmix_max_periods", &n) >= 0) { 1947d5ac70f0Sopenharmony_ci long val; 1948d5ac70f0Sopenharmony_ci err = snd_config_get_integer(n, &val); 1949d5ac70f0Sopenharmony_ci if (err >= 0) 1950d5ac70f0Sopenharmony_ci rec->max_periods = val; 1951d5ac70f0Sopenharmony_ci } 1952d5ac70f0Sopenharmony_ci 1953d5ac70f0Sopenharmony_ci snd_config_for_each(i, next, conf) { 1954d5ac70f0Sopenharmony_ci const char *id; 1955d5ac70f0Sopenharmony_ci n = snd_config_iterator_entry(i); 1956d5ac70f0Sopenharmony_ci if (snd_config_get_id(n, &id) < 0) 1957d5ac70f0Sopenharmony_ci continue; 1958d5ac70f0Sopenharmony_ci if (snd_pcm_conf_generic_id(id)) 1959d5ac70f0Sopenharmony_ci continue; 1960d5ac70f0Sopenharmony_ci if (strcmp(id, "ipc_key") == 0) { 1961d5ac70f0Sopenharmony_ci long key; 1962d5ac70f0Sopenharmony_ci err = snd_config_get_integer(n, &key); 1963d5ac70f0Sopenharmony_ci if (err < 0) { 1964d5ac70f0Sopenharmony_ci SNDERR("The field ipc_key must be an integer type"); 1965d5ac70f0Sopenharmony_ci 1966d5ac70f0Sopenharmony_ci return err; 1967d5ac70f0Sopenharmony_ci } 1968d5ac70f0Sopenharmony_ci rec->ipc_key = key; 1969d5ac70f0Sopenharmony_ci continue; 1970d5ac70f0Sopenharmony_ci } 1971d5ac70f0Sopenharmony_ci if (strcmp(id, "ipc_perm") == 0) { 1972d5ac70f0Sopenharmony_ci long perm; 1973d5ac70f0Sopenharmony_ci err = snd_config_get_integer(n, &perm); 1974d5ac70f0Sopenharmony_ci if (err < 0) { 1975d5ac70f0Sopenharmony_ci SNDERR("Invalid type for %s", id); 1976d5ac70f0Sopenharmony_ci return err; 1977d5ac70f0Sopenharmony_ci } 1978d5ac70f0Sopenharmony_ci if ((perm & ~0777) != 0) { 1979d5ac70f0Sopenharmony_ci SNDERR("The field ipc_perm must be a valid file permission"); 1980d5ac70f0Sopenharmony_ci return -EINVAL; 1981d5ac70f0Sopenharmony_ci } 1982d5ac70f0Sopenharmony_ci rec->ipc_perm = perm; 1983d5ac70f0Sopenharmony_ci continue; 1984d5ac70f0Sopenharmony_ci } 1985d5ac70f0Sopenharmony_ci if (strcmp(id, "hw_ptr_alignment") == 0) { 1986d5ac70f0Sopenharmony_ci const char *str; 1987d5ac70f0Sopenharmony_ci err = snd_config_get_string(n, &str); 1988d5ac70f0Sopenharmony_ci if (err < 0) { 1989d5ac70f0Sopenharmony_ci SNDERR("Invalid type for %s", id); 1990d5ac70f0Sopenharmony_ci return -EINVAL; 1991d5ac70f0Sopenharmony_ci } 1992d5ac70f0Sopenharmony_ci if (strcmp(str, "no") == 0 || strcmp(str, "off") == 0) 1993d5ac70f0Sopenharmony_ci rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_NO; 1994d5ac70f0Sopenharmony_ci else if (strcmp(str, "roundup") == 0) 1995d5ac70f0Sopenharmony_ci rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_ROUNDUP; 1996d5ac70f0Sopenharmony_ci else if (strcmp(str, "rounddown") == 0) 1997d5ac70f0Sopenharmony_ci rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_ROUNDDOWN; 1998d5ac70f0Sopenharmony_ci else if (strcmp(str, "auto") == 0) 1999d5ac70f0Sopenharmony_ci rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_AUTO; 2000d5ac70f0Sopenharmony_ci else { 2001d5ac70f0Sopenharmony_ci SNDERR("The field hw_ptr_alignment is invalid : %s", str); 2002d5ac70f0Sopenharmony_ci return -EINVAL; 2003d5ac70f0Sopenharmony_ci } 2004d5ac70f0Sopenharmony_ci 2005d5ac70f0Sopenharmony_ci continue; 2006d5ac70f0Sopenharmony_ci } 2007d5ac70f0Sopenharmony_ci if (strcmp(id, "tstamp_type") == 0) { 2008d5ac70f0Sopenharmony_ci const char *str; 2009d5ac70f0Sopenharmony_ci err = snd_config_get_string(n, &str); 2010d5ac70f0Sopenharmony_ci if (err < 0) { 2011d5ac70f0Sopenharmony_ci SNDERR("Invalid type for %s", id); 2012d5ac70f0Sopenharmony_ci return -EINVAL; 2013d5ac70f0Sopenharmony_ci } 2014d5ac70f0Sopenharmony_ci if (strcmp(str, "default") == 0) 2015d5ac70f0Sopenharmony_ci rec->tstamp_type = -1; 2016d5ac70f0Sopenharmony_ci else if (strcmp(str, "gettimeofday") == 0) 2017d5ac70f0Sopenharmony_ci rec->tstamp_type = SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY; 2018d5ac70f0Sopenharmony_ci else if (strcmp(str, "monotonic") == 0) 2019d5ac70f0Sopenharmony_ci rec->tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC; 2020d5ac70f0Sopenharmony_ci else if (strcmp(str, "monotonic_raw") == 0) 2021d5ac70f0Sopenharmony_ci rec->tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW; 2022d5ac70f0Sopenharmony_ci else { 2023d5ac70f0Sopenharmony_ci SNDERR("The field tstamp_type is invalid : %s", str); 2024d5ac70f0Sopenharmony_ci return -EINVAL; 2025d5ac70f0Sopenharmony_ci } 2026d5ac70f0Sopenharmony_ci continue; 2027d5ac70f0Sopenharmony_ci } 2028d5ac70f0Sopenharmony_ci if (strcmp(id, "ipc_gid") == 0) { 2029d5ac70f0Sopenharmony_ci char *group; 2030d5ac70f0Sopenharmony_ci char *endp; 2031d5ac70f0Sopenharmony_ci err = snd_config_get_ascii(n, &group); 2032d5ac70f0Sopenharmony_ci if (err < 0) { 2033d5ac70f0Sopenharmony_ci SNDERR("The field ipc_gid must be a valid group"); 2034d5ac70f0Sopenharmony_ci return err; 2035d5ac70f0Sopenharmony_ci } 2036d5ac70f0Sopenharmony_ci if (! *group) { 2037d5ac70f0Sopenharmony_ci rec->ipc_gid = -1; 2038d5ac70f0Sopenharmony_ci free(group); 2039d5ac70f0Sopenharmony_ci continue; 2040d5ac70f0Sopenharmony_ci } 2041d5ac70f0Sopenharmony_ci if (isdigit(*group) == 0) { 2042d5ac70f0Sopenharmony_ci long clen = sysconf(_SC_GETGR_R_SIZE_MAX); 2043d5ac70f0Sopenharmony_ci size_t len = (clen == -1) ? 1024 : (size_t)clen; 2044d5ac70f0Sopenharmony_ci struct group grp, *pgrp; 2045d5ac70f0Sopenharmony_ci char *buffer = (char *)malloc(len); 2046d5ac70f0Sopenharmony_ci if (buffer == NULL) 2047d5ac70f0Sopenharmony_ci return -ENOMEM; 2048d5ac70f0Sopenharmony_ci int st = getgrnam_r(group, &grp, buffer, len, &pgrp); 2049d5ac70f0Sopenharmony_ci if (st != 0 || !pgrp) { 2050d5ac70f0Sopenharmony_ci SNDERR("The field ipc_gid must be a valid group (create group %s)", group); 2051d5ac70f0Sopenharmony_ci free(buffer); 2052d5ac70f0Sopenharmony_ci return -EINVAL; 2053d5ac70f0Sopenharmony_ci } 2054d5ac70f0Sopenharmony_ci rec->ipc_gid = pgrp->gr_gid; 2055d5ac70f0Sopenharmony_ci free(buffer); 2056d5ac70f0Sopenharmony_ci } else { 2057d5ac70f0Sopenharmony_ci rec->ipc_gid = strtol(group, &endp, 10); 2058d5ac70f0Sopenharmony_ci } 2059d5ac70f0Sopenharmony_ci free(group); 2060d5ac70f0Sopenharmony_ci continue; 2061d5ac70f0Sopenharmony_ci } 2062d5ac70f0Sopenharmony_ci if (strcmp(id, "ipc_key_add_uid") == 0) { 2063d5ac70f0Sopenharmony_ci if ((err = snd_config_get_bool(n)) < 0) { 2064d5ac70f0Sopenharmony_ci SNDERR("The field ipc_key_add_uid must be a boolean type"); 2065d5ac70f0Sopenharmony_ci return err; 2066d5ac70f0Sopenharmony_ci } 2067d5ac70f0Sopenharmony_ci ipc_key_add_uid = err; 2068d5ac70f0Sopenharmony_ci continue; 2069d5ac70f0Sopenharmony_ci } 2070d5ac70f0Sopenharmony_ci if (strcmp(id, "slave") == 0) { 2071d5ac70f0Sopenharmony_ci rec->slave = n; 2072d5ac70f0Sopenharmony_ci continue; 2073d5ac70f0Sopenharmony_ci } 2074d5ac70f0Sopenharmony_ci if (strcmp(id, "bindings") == 0) { 2075d5ac70f0Sopenharmony_ci rec->bindings = n; 2076d5ac70f0Sopenharmony_ci continue; 2077d5ac70f0Sopenharmony_ci } 2078d5ac70f0Sopenharmony_ci if (strcmp(id, "slowptr") == 0) { 2079d5ac70f0Sopenharmony_ci err = snd_config_get_bool(n); 2080d5ac70f0Sopenharmony_ci if (err < 0) 2081d5ac70f0Sopenharmony_ci return err; 2082d5ac70f0Sopenharmony_ci rec->slowptr = err; 2083d5ac70f0Sopenharmony_ci continue; 2084d5ac70f0Sopenharmony_ci } 2085d5ac70f0Sopenharmony_ci if (strcmp(id, "max_periods") == 0) { 2086d5ac70f0Sopenharmony_ci long val; 2087d5ac70f0Sopenharmony_ci err = snd_config_get_integer(n, &val); 2088d5ac70f0Sopenharmony_ci if (err < 0) 2089d5ac70f0Sopenharmony_ci return err; 2090d5ac70f0Sopenharmony_ci rec->max_periods = val; 2091d5ac70f0Sopenharmony_ci continue; 2092d5ac70f0Sopenharmony_ci } 2093d5ac70f0Sopenharmony_ci if (strcmp(id, "var_periodsize") == 0) { 2094d5ac70f0Sopenharmony_ci err = snd_config_get_bool(n); 2095d5ac70f0Sopenharmony_ci if (err < 0) 2096d5ac70f0Sopenharmony_ci return err; 2097d5ac70f0Sopenharmony_ci rec->var_periodsize = err; 2098d5ac70f0Sopenharmony_ci continue; 2099d5ac70f0Sopenharmony_ci } 2100d5ac70f0Sopenharmony_ci if (strcmp(id, "direct_memory_access") == 0) { 2101d5ac70f0Sopenharmony_ci err = snd_config_get_bool(n); 2102d5ac70f0Sopenharmony_ci if (err < 0) 2103d5ac70f0Sopenharmony_ci return err; 2104d5ac70f0Sopenharmony_ci rec->direct_memory_access = err; 2105d5ac70f0Sopenharmony_ci continue; 2106d5ac70f0Sopenharmony_ci } 2107d5ac70f0Sopenharmony_ci SNDERR("Unknown field %s", id); 2108d5ac70f0Sopenharmony_ci return -EINVAL; 2109d5ac70f0Sopenharmony_ci } 2110d5ac70f0Sopenharmony_ci if (!rec->slave) { 2111d5ac70f0Sopenharmony_ci SNDERR("slave is not defined"); 2112d5ac70f0Sopenharmony_ci return -EINVAL; 2113d5ac70f0Sopenharmony_ci } 2114d5ac70f0Sopenharmony_ci if (!rec->ipc_key) { 2115d5ac70f0Sopenharmony_ci SNDERR("Unique IPC key is not defined"); 2116d5ac70f0Sopenharmony_ci return -EINVAL; 2117d5ac70f0Sopenharmony_ci } 2118d5ac70f0Sopenharmony_ci if (ipc_key_add_uid) 2119d5ac70f0Sopenharmony_ci rec->ipc_key += getuid(); 2120d5ac70f0Sopenharmony_ci err = snd_pcm_direct_get_slave_ipc_offset(root, conf, stream); 2121d5ac70f0Sopenharmony_ci if (err < 0) 2122d5ac70f0Sopenharmony_ci return err; 2123d5ac70f0Sopenharmony_ci rec->ipc_key += err; 2124d5ac70f0Sopenharmony_ci 2125d5ac70f0Sopenharmony_ci return 0; 2126d5ac70f0Sopenharmony_ci} 2127d5ac70f0Sopenharmony_ci 2128d5ac70f0Sopenharmony_civoid snd_pcm_direct_reset_slave_ptr(snd_pcm_t *pcm, snd_pcm_direct_t *dmix, 2129d5ac70f0Sopenharmony_ci snd_pcm_uframes_t hw_ptr) 2130d5ac70f0Sopenharmony_ci{ 2131d5ac70f0Sopenharmony_ci dmix->slave_appl_ptr = dmix->slave_hw_ptr = hw_ptr; 2132d5ac70f0Sopenharmony_ci if (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_ROUNDUP || 2133d5ac70f0Sopenharmony_ci (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_AUTO && 2134d5ac70f0Sopenharmony_ci pcm->buffer_size <= pcm->period_size * 2)) 2135d5ac70f0Sopenharmony_ci dmix->slave_appl_ptr = 2136d5ac70f0Sopenharmony_ci ((dmix->slave_appl_ptr + dmix->slave_period_size - 1) / 2137d5ac70f0Sopenharmony_ci dmix->slave_period_size) * dmix->slave_period_size; 2138d5ac70f0Sopenharmony_ci else if (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_ROUNDDOWN || 2139d5ac70f0Sopenharmony_ci (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_AUTO && 2140d5ac70f0Sopenharmony_ci ((dmix->slave_period_size * SEC_TO_MS) / pcm->rate) < LOW_LATENCY_PERIOD_TIME)) 2141d5ac70f0Sopenharmony_ci dmix->slave_appl_ptr = dmix->slave_hw_ptr = 2142d5ac70f0Sopenharmony_ci ((dmix->slave_hw_ptr / dmix->slave_period_size) * 2143d5ac70f0Sopenharmony_ci dmix->slave_period_size); 2144d5ac70f0Sopenharmony_ci} 2145d5ac70f0Sopenharmony_ci 2146d5ac70f0Sopenharmony_ciint _snd_pcm_direct_new(snd_pcm_t **pcmp, snd_pcm_direct_t **_dmix, int type, 2147d5ac70f0Sopenharmony_ci const char *name, struct snd_pcm_direct_open_conf *opts, 2148d5ac70f0Sopenharmony_ci struct slave_params *params, snd_pcm_stream_t stream, int mode) 2149d5ac70f0Sopenharmony_ci{ 2150d5ac70f0Sopenharmony_ci snd_pcm_direct_t *dmix; 2151d5ac70f0Sopenharmony_ci int fail_sem_loop = 10; 2152d5ac70f0Sopenharmony_ci int ret; 2153d5ac70f0Sopenharmony_ci 2154d5ac70f0Sopenharmony_ci dmix = calloc(1, sizeof(snd_pcm_direct_t)); 2155d5ac70f0Sopenharmony_ci if (!dmix) 2156d5ac70f0Sopenharmony_ci return -ENOMEM; 2157d5ac70f0Sopenharmony_ci 2158d5ac70f0Sopenharmony_ci ret = snd_pcm_direct_parse_bindings(dmix, params, opts->bindings); 2159d5ac70f0Sopenharmony_ci if (ret < 0) { 2160d5ac70f0Sopenharmony_ci free(dmix); 2161d5ac70f0Sopenharmony_ci return ret; 2162d5ac70f0Sopenharmony_ci } 2163d5ac70f0Sopenharmony_ci 2164d5ac70f0Sopenharmony_ci dmix->ipc_key = opts->ipc_key; 2165d5ac70f0Sopenharmony_ci dmix->ipc_perm = opts->ipc_perm; 2166d5ac70f0Sopenharmony_ci dmix->ipc_gid = opts->ipc_gid; 2167d5ac70f0Sopenharmony_ci dmix->tstamp_type = opts->tstamp_type; 2168d5ac70f0Sopenharmony_ci dmix->semid = -1; 2169d5ac70f0Sopenharmony_ci dmix->shmid = -1; 2170d5ac70f0Sopenharmony_ci dmix->shmptr = (void *) -1; 2171d5ac70f0Sopenharmony_ci dmix->type = type; 2172d5ac70f0Sopenharmony_ci 2173d5ac70f0Sopenharmony_ci ret = snd_pcm_new(pcmp, type, name, stream, mode); 2174d5ac70f0Sopenharmony_ci if (ret < 0) 2175d5ac70f0Sopenharmony_ci goto _err_nosem; 2176d5ac70f0Sopenharmony_ci 2177d5ac70f0Sopenharmony_ci while (1) { 2178d5ac70f0Sopenharmony_ci ret = snd_pcm_direct_semaphore_create_or_connect(dmix); 2179d5ac70f0Sopenharmony_ci if (ret < 0) { 2180d5ac70f0Sopenharmony_ci SNDERR("unable to create IPC semaphore"); 2181d5ac70f0Sopenharmony_ci goto _err_nosem_free; 2182d5ac70f0Sopenharmony_ci } 2183d5ac70f0Sopenharmony_ci ret = snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT); 2184d5ac70f0Sopenharmony_ci if (ret < 0) { 2185d5ac70f0Sopenharmony_ci snd_pcm_direct_semaphore_discard(dmix); 2186d5ac70f0Sopenharmony_ci if (--fail_sem_loop <= 0) 2187d5ac70f0Sopenharmony_ci goto _err_nosem_free; 2188d5ac70f0Sopenharmony_ci continue; 2189d5ac70f0Sopenharmony_ci } 2190d5ac70f0Sopenharmony_ci break; 2191d5ac70f0Sopenharmony_ci } 2192d5ac70f0Sopenharmony_ci 2193d5ac70f0Sopenharmony_ci ret = snd_pcm_direct_shm_create_or_connect(dmix); 2194d5ac70f0Sopenharmony_ci if (ret < 0) { 2195d5ac70f0Sopenharmony_ci SNDERR("unable to create IPC shm instance"); 2196d5ac70f0Sopenharmony_ci snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT); 2197d5ac70f0Sopenharmony_ci goto _err_nosem_free; 2198d5ac70f0Sopenharmony_ci } else { 2199d5ac70f0Sopenharmony_ci *_dmix = dmix; 2200d5ac70f0Sopenharmony_ci } 2201d5ac70f0Sopenharmony_ci 2202d5ac70f0Sopenharmony_ci return ret; 2203d5ac70f0Sopenharmony_ci_err_nosem_free: 2204d5ac70f0Sopenharmony_ci snd_pcm_free(*pcmp); 2205d5ac70f0Sopenharmony_ci *pcmp = NULL; 2206d5ac70f0Sopenharmony_ci_err_nosem: 2207d5ac70f0Sopenharmony_ci free(dmix->bindings); 2208d5ac70f0Sopenharmony_ci free(dmix); 2209d5ac70f0Sopenharmony_ci return ret; 2210d5ac70f0Sopenharmony_ci} 2211