| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * \file pcm/pcm_dmix.c | 
					
						
							|  |  |  |  * \ingroup PCM_Plugins | 
					
						
							|  |  |  |  * \brief PCM Direct Stream Mixing (dmix) Plugin Interface | 
					
						
							|  |  |  |  * \author Jaroslav Kysela <perex@suse.cz> | 
					
						
							|  |  |  |  * \date 2002 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  *  PCM - Direct Stream Mixing | 
					
						
							|  |  |  |  *  Copyright (c) 2000 by Jaroslav Kysela <perex@suse.cz> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   This library is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  *   it under the terms of the GNU Lesser General Public License as | 
					
						
							|  |  |  |  *   published by the Free Software Foundation; either version 2.1 of | 
					
						
							|  |  |  |  *   the License, or (at your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  *   but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  *   GNU Lesser General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   You should have received a copy of the GNU Lesser General Public | 
					
						
							|  |  |  |  *   License along with this library; if not, write to the Free Software | 
					
						
							|  |  |  |  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <stddef.h>
 | 
					
						
							|  |  |  | #include <unistd.h>
 | 
					
						
							|  |  |  | #include <signal.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <fcntl.h>
 | 
					
						
							|  |  |  | #include <sys/ioctl.h>
 | 
					
						
							|  |  |  | #include <sys/mman.h>
 | 
					
						
							|  |  |  | #include <sys/shm.h>
 | 
					
						
							|  |  |  | #include <sys/sem.h>
 | 
					
						
							|  |  |  | #include <sys/wait.h>
 | 
					
						
							|  |  |  | #include <sys/socket.h>
 | 
					
						
							|  |  |  | #include <sys/un.h>
 | 
					
						
							|  |  |  | #include "pcm_local.h"
 | 
					
						
							|  |  |  | #include "../control/control_local.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifndef PIC
 | 
					
						
							|  |  |  | /* entry for static linking */ | 
					
						
							|  |  |  | const char *_snd_module_pcm_dmix = ""; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  |   | 
					
						
							|  |  |  |   | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * FIXME: | 
					
						
							|  |  |  |  *  add possibility to use futexes here | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #define DMIX_IPC_SEMS		1
 | 
					
						
							|  |  |  | #define DMIX_IPC_SEM_CLIENT	0
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-06 19:15:53 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  |   | 
					
						
							|  |  |  | int snd_timer_async(snd_timer_t *timer, int sig, pid_t pid); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct slave_params { | 
					
						
							|  |  |  | 	snd_pcm_format_t format; | 
					
						
							|  |  |  | 	unsigned int rate; | 
					
						
							|  |  |  | 	unsigned int channels; | 
					
						
							|  |  |  | 	unsigned int period_time; | 
					
						
							|  |  |  | 	unsigned int buffer_time; | 
					
						
							|  |  |  | 	snd_pcm_uframes_t period_size; | 
					
						
							|  |  |  | 	snd_pcm_uframes_t buffer_size; | 
					
						
							|  |  |  | 	unsigned int periods; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct { | 
					
						
							|  |  |  | 	int owners;		/* count of all owners */ | 
					
						
							|  |  |  | 	char socket_name[256];	/* name of communication socket */ | 
					
						
							|  |  |  | 	snd_pcm_type_t type;	/* PCM type (currently only hw) */ | 
					
						
							|  |  |  | 	snd_pcm_hw_params_t hw_params; | 
					
						
							|  |  |  | 	snd_pcm_sw_params_t sw_params; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:46:42 +00:00
										 |  |  | 	struct { | 
					
						
							|  |  |  | 		snd_pcm_uframes_t buffer_size; | 
					
						
							|  |  |  | 		snd_pcm_uframes_t boundary; | 
					
						
							| 
									
										
										
										
											2003-02-12 09:02:49 +00:00
										 |  |  | 		snd_pcm_uframes_t channels; | 
					
						
							|  |  |  | 		unsigned int sample_bits; | 
					
						
							|  |  |  | 		unsigned int rate; | 
					
						
							|  |  |  | 		snd_pcm_format_t format; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:46:42 +00:00
										 |  |  | 	} s; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | } snd_pcm_dmix_share_t; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct { | 
					
						
							|  |  |  | 	key_t ipc_key;	/* IPC key for semaphore and memory */ | 
					
						
							|  |  |  | 	int semid;	/* IPC global semaphore identification */ | 
					
						
							|  |  |  | 	int shmid;	/* IPC global shared memory identification */ | 
					
						
							|  |  |  | 	snd_pcm_dmix_share_t *shmptr;	/* pointer to shared memory area */ | 
					
						
							|  |  |  | 	snd_pcm_t *spcm; /* slave PCM handle */ | 
					
						
							|  |  |  | 	snd_pcm_uframes_t appl_ptr; | 
					
						
							|  |  |  | 	snd_pcm_uframes_t hw_ptr; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	snd_pcm_uframes_t avail_max; | 
					
						
							|  |  |  | 	snd_pcm_uframes_t slave_appl_ptr; | 
					
						
							|  |  |  | 	snd_pcm_uframes_t slave_hw_ptr; | 
					
						
							|  |  |  | 	snd_pcm_state_t state; | 
					
						
							|  |  |  | 	snd_timestamp_t trigger_tstamp; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	int server, client; | 
					
						
							|  |  |  | 	int comm_fd;	/* communication file descriptor (socket) */ | 
					
						
							|  |  |  | 	int hw_fd;	/* hardware file descriptor */ | 
					
						
							|  |  |  | 	int poll_fd; | 
					
						
							|  |  |  | 	int server_fd; | 
					
						
							|  |  |  | 	pid_t server_pid; | 
					
						
							|  |  |  | 	snd_timer_t *timer; /* timer used as poll_fd */ | 
					
						
							|  |  |  | } snd_pcm_dmix_t; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  *  global semaphore and shared memory area | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if 0
 | 
					
						
							|  |  |  | struct sembuf { | 
					
						
							|  |  |  | 	unsigned short sem_num; | 
					
						
							|  |  |  | 	short sem_op; | 
					
						
							|  |  |  | 	short sem_flg; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int semaphore_create_or_connect(snd_pcm_dmix_t *dmix) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	dmix->semid = semget(dmix->ipc_key, DMIX_IPC_SEMS, IPC_CREAT | 0666); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	if (dmix->semid < 0) | 
					
						
							|  |  |  | 		return -errno; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int semaphore_discard(snd_pcm_dmix_t *dmix) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (dmix->semid < 0) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	if (semctl(dmix->semid, 0, IPC_RMID, NULL) < 0) | 
					
						
							|  |  |  | 		return -errno; | 
					
						
							|  |  |  | 	dmix->semid = -1; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | static int semaphore_down(snd_pcm_dmix_t *dmix, int sem_num) | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	struct sembuf op[2] = { { 0, 0, SEM_UNDO }, { 0, 1, SEM_UNDO | IPC_NOWAIT } }; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	assert(dmix->semid >= 0); | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	op[0].sem_num = sem_num; | 
					
						
							|  |  |  | 	op[1].sem_num = sem_num; | 
					
						
							|  |  |  | 	if (semop(dmix->semid, op, 2) < 0) | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 		return -errno; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | static int semaphore_up(snd_pcm_dmix_t *dmix, int sem_num) | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	struct sembuf op = { 0, -1, SEM_UNDO | IPC_NOWAIT }; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	assert(dmix->semid >= 0); | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	op.sem_num = sem_num; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	if (semop(dmix->semid, &op, 1) < 0) | 
					
						
							|  |  |  | 		return -errno; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  *  global shared memory area  | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int shm_create_or_connect(snd_pcm_dmix_t *dmix) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	static int shm_discard(snd_pcm_dmix_t *dmix); | 
					
						
							|  |  |  | 	struct shmid_ds buf; | 
					
						
							|  |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 	dmix->shmid = shmget(dmix->ipc_key, sizeof(snd_pcm_dmix_share_t), IPC_CREAT | 0666); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	if (dmix->shmid < 0) | 
					
						
							|  |  |  | 		return -errno; | 
					
						
							|  |  |  | 	dmix->shmptr = shmat(dmix->shmid, 0, 0); | 
					
						
							|  |  |  | 	if (dmix->shmptr == (void *) -1) { | 
					
						
							|  |  |  | 		shm_discard(dmix); | 
					
						
							|  |  |  | 		return -errno; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (shmctl(dmix->shmid, IPC_STAT, &buf) < 0) { | 
					
						
							|  |  |  | 		shm_discard(dmix); | 
					
						
							|  |  |  | 		return -errno; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (buf.shm_nattch == 1) {	/* we're the first user, clear the segment */ | 
					
						
							|  |  |  | 		memset(dmix->shmptr, 0, sizeof(snd_pcm_dmix_share_t)); | 
					
						
							|  |  |  | 		ret = 1; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int shm_discard(snd_pcm_dmix_t *dmix) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct shmid_ds buf; | 
					
						
							|  |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dmix->shmid < 0) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	if (dmix->shmptr != (void *) -1 && shmdt(dmix->shmptr) < 0) | 
					
						
							|  |  |  | 		return -errno; | 
					
						
							|  |  |  | 	dmix->shmptr = (void *) -1; | 
					
						
							|  |  |  | 	if (shmctl(dmix->shmid, IPC_STAT, &buf) < 0) { | 
					
						
							|  |  |  | 		shm_discard(dmix); | 
					
						
							|  |  |  | 		return -errno; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (buf.shm_nattch == 0) {	/* we're the last user, destroy the segment */ | 
					
						
							|  |  |  | 		if (shmctl(dmix->shmid, IPC_RMID, NULL) < 0) | 
					
						
							|  |  |  | 			return -errno; | 
					
						
							|  |  |  | 		ret = 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	dmix->shmid = -1; | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  *  server side | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int get_tmp_name(char *filename, size_t size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct timeval tv; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	gettimeofday(&tv, NULL); | 
					
						
							|  |  |  | 	snprintf(filename, size, "/tmp/alsa-dmix-%i-%li-%li", getpid(), tv.tv_sec, tv.tv_usec); | 
					
						
							|  |  |  | 	filename[size-1] = '\0'; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int make_local_socket(const char *filename, int server) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	size_t l = strlen(filename); | 
					
						
							|  |  |  | 	size_t size = offsetof(struct sockaddr_un, sun_path) + l; | 
					
						
							|  |  |  | 	struct sockaddr_un *addr = alloca(size); | 
					
						
							|  |  |  | 	int sock; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sock = socket(PF_LOCAL, SOCK_STREAM, 0); | 
					
						
							|  |  |  | 	if (sock < 0) { | 
					
						
							|  |  |  | 		int result = -errno; | 
					
						
							|  |  |  | 		SYSERR("socket failed"); | 
					
						
							|  |  |  | 		return result; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (server) | 
					
						
							|  |  |  | 		unlink(filename); | 
					
						
							|  |  |  | 	addr->sun_family = AF_LOCAL; | 
					
						
							|  |  |  | 	memcpy(addr->sun_path, filename, l); | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	if (server) { | 
					
						
							|  |  |  | 		if (bind(sock, (struct sockaddr *) addr, size) < 0) { | 
					
						
							|  |  |  | 			int result = -errno; | 
					
						
							|  |  |  | 			SYSERR("bind failed"); | 
					
						
							|  |  |  | 			return result; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		if (connect(sock, (struct sockaddr *) addr, size) < 0) { | 
					
						
							|  |  |  | 			SYSERR("connect failed"); | 
					
						
							|  |  |  | 			return -errno; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return sock; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int send_fd(int sock, void *data, size_t len, int fd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 	size_t cmsg_len = CMSG_LEN(sizeof(int)); | 
					
						
							|  |  |  | 	struct cmsghdr *cmsg = alloca(cmsg_len); | 
					
						
							|  |  |  | 	int *fds = (int *) CMSG_DATA(cmsg); | 
					
						
							|  |  |  | 	struct msghdr msghdr; | 
					
						
							|  |  |  | 	struct iovec vec; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vec.iov_base = (void *)&data; | 
					
						
							|  |  |  | 	vec.iov_len = len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cmsg->cmsg_len = cmsg_len; | 
					
						
							|  |  |  | 	cmsg->cmsg_level = SOL_SOCKET; | 
					
						
							|  |  |  | 	cmsg->cmsg_type = SCM_RIGHTS; | 
					
						
							|  |  |  | 	*fds = fd; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	msghdr.msg_name = NULL; | 
					
						
							|  |  |  | 	msghdr.msg_namelen = 0; | 
					
						
							|  |  |  | 	msghdr.msg_iov = &vec; | 
					
						
							|  |  |  |  	msghdr.msg_iovlen = 1; | 
					
						
							|  |  |  | 	msghdr.msg_control = cmsg; | 
					
						
							|  |  |  | 	msghdr.msg_controllen = cmsg_len; | 
					
						
							|  |  |  | 	msghdr.msg_flags = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = sendmsg(sock, &msghdr, 0 ); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		SYSERR("sendmsg failed"); | 
					
						
							|  |  |  | 		return -errno; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | #if 0
 | 
					
						
							|  |  |  | #define server_printf(fmt, args...) printf(fmt, ##args)
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #define server_printf(fmt, args...) /* nothing */
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | static void server_job(snd_pcm_dmix_t *dmix) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret, sck, i; | 
					
						
							|  |  |  | 	int max = 128, current = 0; | 
					
						
							|  |  |  | 	struct pollfd pfds[max + 1]; | 
					
						
							| 
									
										
										
										
											2003-02-12 09:02:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	/* close all files to free resources */ | 
					
						
							|  |  |  | 	i = sysconf(_SC_OPEN_MAX); | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	while (--i >= 3) { | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 		if (i != dmix->server_fd && i != dmix->hw_fd) | 
					
						
							|  |  |  | 			close(i); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	/* detach from parent */ | 
					
						
							|  |  |  | 	setsid(); | 
					
						
							| 
									
										
										
										
											2003-02-12 09:02:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	pfds[0].fd = dmix->server_fd; | 
					
						
							|  |  |  | 	pfds[0].events = POLLIN | POLLERR | POLLHUP; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	server_printf("DMIX SERVER STARTED\n"); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	while (1) { | 
					
						
							|  |  |  | 		ret = poll(pfds, current + 1, 500); | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 		server_printf("DMIX SERVER: poll ret = %i, revents[0] = 0x%x\n", ret, pfds[0].revents); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 		if (ret < 0)	/* some error */ | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 		if (ret == 0 || pfds[0].revents & (POLLERR | POLLHUP)) {	/* timeout or error? */ | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 			struct shmid_ds buf; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 			semaphore_down(dmix, DMIX_IPC_SEM_CLIENT); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 			if (shmctl(dmix->shmid, IPC_STAT, &buf) < 0) { | 
					
						
							|  |  |  | 				shm_discard(dmix); | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 			server_printf("DMIX SERVER: nattch = %i\n", (int)buf.shm_nattch); | 
					
						
							|  |  |  | 			if (buf.shm_nattch == 1)	/* server is the last user, exit */ | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 				break; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 			semaphore_up(dmix, DMIX_IPC_SEM_CLIENT); | 
					
						
							|  |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if (pfds[0].revents & POLLIN) { | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 			ret--; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 			sck = accept(dmix->server_fd, 0, 0); | 
					
						
							|  |  |  | 			if (sck >= 0) { | 
					
						
							| 
									
										
										
										
											2003-02-12 09:02:49 +00:00
										 |  |  | 				server_printf("DMIX SERVER: new connection %i\n", sck); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 				if (current == max) { | 
					
						
							|  |  |  | 					close(sck); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					unsigned char buf = 'A'; | 
					
						
							| 
									
										
										
										
											2003-02-12 09:02:49 +00:00
										 |  |  | 					pfds[current+1].fd = sck; | 
					
						
							|  |  |  | 					pfds[current+1].events = POLLIN | POLLERR | POLLHUP; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 					send_fd(sck, &buf, 1, dmix->hw_fd); | 
					
						
							| 
									
										
										
										
											2003-02-12 09:02:49 +00:00
										 |  |  | 					server_printf("DMIX SERVER: fd sent ok\n"); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 					current++; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2003-02-12 09:02:49 +00:00
										 |  |  | 		for (i = 0; i < current && ret > 0; i++) { | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 			struct pollfd *pfd = &pfds[i+1]; | 
					
						
							|  |  |  | 			unsigned char cmd; | 
					
						
							| 
									
										
										
										
											2003-02-12 09:02:49 +00:00
										 |  |  | 			server_printf("client %i revents = 0x%x\n", pfd->fd, pfd->revents); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 			if (pfd->revents & (POLLERR | POLLHUP)) { | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 				ret--; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 				close(pfd->fd); | 
					
						
							|  |  |  | 				pfd->fd = -1; | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if (!(pfd->revents & POLLIN)) | 
					
						
							|  |  |  | 				continue; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 			ret--; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 			if (read(pfd->fd, &cmd, 1) == 1) | 
					
						
							|  |  |  | 				cmd = 0 /*process command */; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2003-02-12 09:02:49 +00:00
										 |  |  | 		for (i = 0; i < current; i++) { | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 			if (pfds[i+1].fd < 0) { | 
					
						
							|  |  |  | 				if (i + 1 != max) | 
					
						
							|  |  |  | 					memcpy(&pfds[i+1], &pfds[i+2], sizeof(struct pollfd) * (max - i - 1)); | 
					
						
							|  |  |  | 				current--; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	close(dmix->server_fd); | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	close(dmix->hw_fd); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	shm_discard(dmix); | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	semaphore_discard(dmix); | 
					
						
							|  |  |  | 	server_printf("DMIX SERVER EXIT\n"); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	exit(EXIT_SUCCESS); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int server_create(snd_pcm_dmix_t *dmix) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dmix->server_fd = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = get_tmp_name(dmix->shmptr->socket_name, sizeof(dmix->shmptr->socket_name)); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	ret = make_local_socket(dmix->shmptr->socket_name, 1); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	dmix->server_fd = ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = listen(dmix->server_fd, 4); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		close(dmix->server_fd); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	ret = fork(); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		close(dmix->server_fd); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} else if (ret == 0) { | 
					
						
							|  |  |  | 		server_job(dmix); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	dmix->server_pid = ret; | 
					
						
							|  |  |  | 	dmix->server = 1; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int server_discard(snd_pcm_dmix_t *dmix) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (dmix->server) { | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 		//kill(dmix->server_pid, SIGTERM);
 | 
					
						
							|  |  |  | 		//waitpid(dmix->server_pid, NULL, 0);
 | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 		dmix->server_pid = (pid_t)-1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (dmix->server_fd > 0) { | 
					
						
							|  |  |  | 		close(dmix->server_fd); | 
					
						
							|  |  |  | 		dmix->server_fd = -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	dmix->server = 0; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  *  client side | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int snd_receive_fd(int sock, void *data, size_t len, int *fd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int client_connect(snd_pcm_dmix_t *dmix) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 	unsigned char buf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = make_local_socket(dmix->shmptr->socket_name, 0); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	dmix->comm_fd = ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = snd_receive_fd(dmix->comm_fd, &buf, 1, &dmix->hw_fd); | 
					
						
							|  |  |  | 	if (ret < 0 || buf != 'A') { | 
					
						
							|  |  |  | 		close(dmix->comm_fd); | 
					
						
							|  |  |  | 		dmix->comm_fd = -1; | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dmix->client = 1; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int client_discard(snd_pcm_dmix_t *dmix) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (dmix->client) { | 
					
						
							|  |  |  | 		close(dmix->comm_fd); | 
					
						
							|  |  |  | 		dmix->comm_fd = -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-06 19:15:53 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  ring buffer operation | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  the main function of this plugin: mixing | 
					
						
							|  |  |  |  *  FIXME: optimize it for different architectures | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void mix_areas(const snd_pcm_channel_area_t *src_areas, | 
					
						
							|  |  |  | 		      const snd_pcm_channel_area_t *dst_areas, | 
					
						
							|  |  |  | 		      unsigned int channels, | 
					
						
							|  |  |  | 		      snd_pcm_uframes_t src_ofs, | 
					
						
							|  |  |  | 		      snd_pcm_uframes_t dst_ofs, | 
					
						
							|  |  |  | 		      snd_pcm_uframes_t size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	register signed int sample; | 
					
						
							|  |  |  | 	signed short *src, *dst; | 
					
						
							|  |  |  | 	unsigned int src_step, dst_step; | 
					
						
							|  |  |  | 	unsigned int chn, count; | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	for (chn = 0; chn < channels; chn++) { | 
					
						
							|  |  |  | 		src_step = src_areas[chn].step / 8; | 
					
						
							|  |  |  | 		dst_step = dst_areas[chn].step / 8; | 
					
						
							|  |  |  | 		src = (signed short *)(((char *)src_areas[chn].addr + src_areas[chn].first / 8) + (src_ofs * src_step)); | 
					
						
							|  |  |  | 		dst = (signed short *)(((char *)dst_areas[chn].addr + dst_areas[chn].first / 8) + (dst_ofs * dst_step)); | 
					
						
							|  |  |  | 		count = 0; | 
					
						
							|  |  |  | 		while (count++ < size) { | 
					
						
							|  |  |  | 			/* FIXME: it's pretty ugly, because the other process might modify our value between
 | 
					
						
							|  |  |  | 			          read / write session - especially on MP machines */ | 
					
						
							|  |  |  | 			sample = *dst + *src; | 
					
						
							|  |  |  | 			if (sample > 0x7fff) | 
					
						
							|  |  |  | 				sample = 0x7fff; | 
					
						
							|  |  |  | 			if (sample < -0x8000) | 
					
						
							|  |  |  | 				sample = -0x8000; | 
					
						
							|  |  |  | 			*dst = sample; | 
					
						
							|  |  |  | 			((char *)src) += src_step; | 
					
						
							|  |  |  | 			((char *)dst) += dst_step; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-06 19:15:53 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  synchronize shm ring buffer with hardware | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | static void snd_pcm_dmix_sync_area(snd_pcm_t *pcm, snd_pcm_uframes_t size) | 
					
						
							| 
									
										
										
										
											2003-02-06 19:15:53 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 	snd_pcm_uframes_t appl_ptr, slave_appl_ptr, transfer; | 
					
						
							|  |  |  | 	const snd_pcm_channel_area_t *src_areas, *dst_areas; | 
					
						
							| 
									
										
										
										
											2003-02-06 19:15:53 +00:00
										 |  |  | 	 | 
					
						
							|  |  |  | 	/* get the start of update area */ | 
					
						
							|  |  |  | 	appl_ptr = dmix->appl_ptr - size; | 
					
						
							|  |  |  | 	if (appl_ptr > pcm->boundary) | 
					
						
							|  |  |  | 		appl_ptr += pcm->boundary; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 	appl_ptr %= pcm->buffer_size; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	/* add sample areas here */ | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 	src_areas = snd_pcm_mmap_areas(pcm); | 
					
						
							|  |  |  | 	dst_areas = snd_pcm_mmap_areas(dmix->spcm); | 
					
						
							| 
									
										
										
										
											2003-02-11 21:46:42 +00:00
										 |  |  | 	slave_appl_ptr = dmix->slave_appl_ptr % dmix->shmptr->s.buffer_size; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 	dmix->slave_appl_ptr += size; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:46:42 +00:00
										 |  |  | 	dmix->slave_appl_ptr %= dmix->shmptr->s.boundary; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 	while (size > 0) { | 
					
						
							|  |  |  | 		transfer = appl_ptr + size > pcm->buffer_size ? pcm->buffer_size - appl_ptr : size; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:46:42 +00:00
										 |  |  | 		transfer = slave_appl_ptr + transfer > dmix->shmptr->s.buffer_size ? dmix->shmptr->s.buffer_size - slave_appl_ptr : transfer; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 		size -= transfer; | 
					
						
							|  |  |  | 		mix_areas(src_areas, dst_areas, pcm->channels, appl_ptr, slave_appl_ptr, transfer); | 
					
						
							|  |  |  | 		slave_appl_ptr += transfer; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:46:42 +00:00
										 |  |  | 		slave_appl_ptr %= dmix->shmptr->s.buffer_size; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 		appl_ptr += transfer; | 
					
						
							|  |  |  | 		appl_ptr %= pcm->buffer_size; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  *  synchronize hardware pointer (hw_ptr) with ours | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int snd_pcm_dmix_sync_ptr(snd_pcm_t *pcm) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							|  |  |  | 	snd_pcm_uframes_t slave_hw_ptr, old_slave_hw_ptr, avail; | 
					
						
							|  |  |  | 	snd_pcm_sframes_t diff; | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	old_slave_hw_ptr = dmix->slave_hw_ptr; | 
					
						
							|  |  |  | 	slave_hw_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr; | 
					
						
							|  |  |  | 	diff = slave_hw_ptr - old_slave_hw_ptr; | 
					
						
							|  |  |  | 	if (diff == 0)		/* fast path */ | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	if (diff < 0) { | 
					
						
							| 
									
										
										
										
											2003-02-11 21:46:42 +00:00
										 |  |  | 		slave_hw_ptr += dmix->shmptr->s.boundary; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 		diff = slave_hw_ptr - old_slave_hw_ptr; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	dmix->hw_ptr += diff; | 
					
						
							|  |  |  | 	dmix->hw_ptr %= pcm->boundary; | 
					
						
							|  |  |  | 	// printf("sync ptr diff = %li\n", diff);
 | 
					
						
							|  |  |  | 	if (pcm->stop_threshold >= pcm->boundary)	/* don't care */ | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	if ((avail = snd_pcm_mmap_playback_avail(pcm)) >= pcm->stop_threshold) { | 
					
						
							| 
									
										
										
										
											2003-02-12 09:02:49 +00:00
										 |  |  | 		gettimeofday(&dmix->trigger_tstamp, 0); | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 		dmix->state = SND_PCM_STATE_XRUN; | 
					
						
							|  |  |  | 		dmix->avail_max = avail; | 
					
						
							|  |  |  | 		return -EPIPE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (avail > dmix->avail_max) | 
					
						
							|  |  |  | 		dmix->avail_max = avail; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2003-02-06 19:15:53 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  plugin implementation | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | static int snd_pcm_dmix_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED) | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2003-02-06 19:15:53 +00:00
										 |  |  | 	/* value is cached for us in pcm->mode (SND_PCM_NONBLOCK flag) */ | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int snd_pcm_dmix_async(snd_pcm_t *pcm, int sig, pid_t pid) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							| 
									
										
										
										
											2003-02-06 19:15:53 +00:00
										 |  |  | 	return snd_timer_async(dmix->timer, sig, pid); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | static int snd_pcm_dmix_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents) | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	unsigned short events; | 
					
						
							|  |  |  | 	static snd_timer_read_t rbuf[5];	/* can be overwriten by multiple plugins, we don't need the value */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	assert(pfds && nfds == 1 && revents); | 
					
						
							|  |  |  | 	events = pfds[0].revents; | 
					
						
							|  |  |  | 	if (events & POLLIN) { | 
					
						
							|  |  |  | 		events |= POLLOUT; | 
					
						
							|  |  |  | 		events &= ~POLLIN; | 
					
						
							|  |  |  | 		/* empty the timer read queue */ | 
					
						
							|  |  |  | 		while (snd_timer_read(dmix->timer, &rbuf, sizeof(rbuf)) == sizeof(rbuf)) ; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	*revents = events; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int snd_pcm_dmix_info(snd_pcm_t *pcm, snd_pcm_info_t * info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	// snd_pcm_dmix_t *dmix = pcm->private_data;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-06 19:15:53 +00:00
										 |  |  | 	memset(info, 0, sizeof(*info)); | 
					
						
							|  |  |  | 	info->stream = pcm->stream; | 
					
						
							|  |  |  | 	info->card = -1; | 
					
						
							|  |  |  | 	/* FIXME: fill this with something more useful: we know the hardware name */ | 
					
						
							|  |  |  | 	strncpy(info->id, pcm->name, sizeof(info->id)); | 
					
						
							|  |  |  | 	strncpy(info->name, pcm->name, sizeof(info->name)); | 
					
						
							|  |  |  | 	strncpy(info->subname, pcm->name, sizeof(info->subname)); | 
					
						
							|  |  |  | 	info->subdevices_count = 1; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-05 21:26:14 +00:00
										 |  |  | static inline snd_mask_t *hw_param_mask(snd_pcm_hw_params_t *params, | 
					
						
							|  |  |  | 					snd_pcm_hw_param_t var) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return ¶ms->masks[var - SND_PCM_HW_PARAM_FIRST_MASK]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params, | 
					
						
							|  |  |  | 						snd_pcm_hw_param_t var) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return ¶ms->intervals[var - SND_PCM_HW_PARAM_FIRST_INTERVAL]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-08 16:35:24 +00:00
										 |  |  | static int hw_param_interval_refine_one(snd_pcm_hw_params_t *params, | 
					
						
							|  |  |  | 					snd_pcm_hw_param_t var, | 
					
						
							|  |  |  | 					snd_pcm_hw_params_t *src) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_interval_t *i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(params->rmask & (1<<var)))	/* nothing to do? */ | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	i = hw_param_interval(params, var); | 
					
						
							|  |  |  | 	if (snd_interval_empty(i)) { | 
					
						
							|  |  |  | 		SNDERR("dmix interval %i empty?\n", (int)var); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (snd_interval_refine(i, hw_param_interval(src, var))) | 
					
						
							|  |  |  | 		params->cmask |= 1<<var; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #undef REFINE_DEBUG
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | static int snd_pcm_dmix_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							| 
									
										
										
										
											2003-02-05 21:26:14 +00:00
										 |  |  | 	snd_pcm_hw_params_t *hw_params = &dmix->shmptr->hw_params; | 
					
						
							|  |  |  | 	static snd_mask_t access = { .bits = {  | 
					
						
							|  |  |  | 					(1<<SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) | | 
					
						
							|  |  |  | 					(1<<SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) | | 
					
						
							|  |  |  | 					(1<<SNDRV_PCM_ACCESS_RW_INTERLEAVED) | | 
					
						
							|  |  |  | 					(1<<SNDRV_PCM_ACCESS_RW_NONINTERLEAVED), | 
					
						
							|  |  |  | 					0, 0, 0 } }; | 
					
						
							| 
									
										
										
										
											2003-02-08 16:35:24 +00:00
										 |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef REFINE_DEBUG
 | 
					
						
							|  |  |  | 	snd_output_t *log; | 
					
						
							|  |  |  | 	snd_output_stdio_attach(&log, stderr, 0); | 
					
						
							|  |  |  | 	snd_output_puts(log, "DMIX REFINE (begin):\n"); | 
					
						
							|  |  |  | 	snd_pcm_hw_params_dump(params, log); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2003-02-05 21:26:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-08 16:35:24 +00:00
										 |  |  | 	if (params->rmask & (1<<SND_PCM_HW_PARAM_ACCESS)) { | 
					
						
							|  |  |  | 		if (snd_mask_empty(hw_param_mask(params, SND_PCM_HW_PARAM_ACCESS))) { | 
					
						
							|  |  |  | 			SNDERR("dmix access mask empty?\n"); | 
					
						
							|  |  |  | 			return -EINVAL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snd_mask_refine(hw_param_mask(params, SND_PCM_HW_PARAM_ACCESS), &access)) | 
					
						
							|  |  |  | 			params->cmask |= 1<<SND_PCM_HW_PARAM_ACCESS; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (params->rmask & (1<<SND_PCM_HW_PARAM_FORMAT)) { | 
					
						
							|  |  |  | 		if (snd_mask_empty(hw_param_mask(params, SND_PCM_HW_PARAM_FORMAT))) { | 
					
						
							|  |  |  | 			SNDERR("dmix format mask empty?\n"); | 
					
						
							|  |  |  | 			return -EINVAL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (snd_mask_refine_set(hw_param_mask(params, SND_PCM_HW_PARAM_FORMAT), | 
					
						
							|  |  |  | 				        snd_mask_value(hw_param_mask(hw_params, SND_PCM_HW_PARAM_FORMAT)))) | 
					
						
							|  |  |  | 			params->cmask |= 1<<SND_PCM_HW_PARAM_FORMAT; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-05 21:26:14 +00:00
										 |  |  | 	//snd_mask_none(hw_param_mask(params, SND_PCM_HW_PARAM_SUBFORMAT));
 | 
					
						
							| 
									
										
										
										
											2003-02-08 16:35:24 +00:00
										 |  |  | 	err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_CHANNELS, hw_params); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_RATE, hw_params); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_BUFFER_SIZE, hw_params); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_BUFFER_TIME, hw_params); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_SIZE, hw_params); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_TIME, hw_params); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIODS, hw_params); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | #ifdef REFINE_DEBUG
 | 
					
						
							|  |  |  | 	snd_output_puts(log, "DMIX REFINE (end):\n"); | 
					
						
							|  |  |  | 	snd_pcm_hw_params_dump(params, log); | 
					
						
							|  |  |  | 	snd_output_close(log); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | static int snd_pcm_dmix_hw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t * params ATTRIBUTE_UNUSED) | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	/* values are cached in the pcm structure */ | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | static int snd_pcm_dmix_hw_free(snd_pcm_t *pcm ATTRIBUTE_UNUSED) | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	/* values are cached in the pcm structure */ | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | static int snd_pcm_dmix_sw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sw_params_t * params ATTRIBUTE_UNUSED) | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	/* values are cached in the pcm structure */ | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int snd_pcm_dmix_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2003-02-06 19:15:53 +00:00
										 |  |  |         return snd_pcm_channel_info_shm(pcm, info, -1); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int snd_pcm_dmix_status(snd_pcm_t *pcm, snd_pcm_status_t * status) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	memset(status, 0, sizeof(*status)); | 
					
						
							|  |  |  | 	status->state = dmix->state; | 
					
						
							|  |  |  | 	status->trigger_tstamp = dmix->trigger_tstamp; | 
					
						
							|  |  |  | 	gettimeofday(&status->tstamp, 0); | 
					
						
							|  |  |  | 	status->avail = snd_pcm_mmap_playback_avail(pcm); | 
					
						
							|  |  |  | 	status->avail_max = status->avail > dmix->avail_max ? status->avail : dmix->avail_max; | 
					
						
							|  |  |  | 	dmix->avail_max = 0; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static snd_pcm_state_t snd_pcm_dmix_state(snd_pcm_t *pcm) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	return dmix->state; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int snd_pcm_dmix_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	int err; | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	assert(pcm && delayp); | 
					
						
							|  |  |  | 	switch(dmix->state) { | 
					
						
							|  |  |  | 	case SNDRV_PCM_STATE_DRAINING: | 
					
						
							|  |  |  | 	case SNDRV_PCM_STATE_RUNNING: | 
					
						
							|  |  |  | 		err = snd_pcm_dmix_sync_ptr(pcm); | 
					
						
							|  |  |  | 		if (err < 0) | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 	case SNDRV_PCM_STATE_PREPARED: | 
					
						
							|  |  |  | 	case SNDRV_PCM_STATE_SUSPENDED: | 
					
						
							|  |  |  | 		*delayp = snd_pcm_mmap_playback_hw_avail(pcm); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	case SNDRV_PCM_STATE_XRUN: | 
					
						
							|  |  |  | 		return -EPIPE; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return -EBADFD; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int snd_pcm_dmix_hwsync(snd_pcm_t *pcm) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	switch(dmix->state) { | 
					
						
							|  |  |  | 	case SNDRV_PCM_STATE_DRAINING: | 
					
						
							|  |  |  | 	case SNDRV_PCM_STATE_RUNNING: | 
					
						
							|  |  |  | 		return snd_pcm_dmix_sync_ptr(pcm); | 
					
						
							|  |  |  | 	case SNDRV_PCM_STATE_PREPARED: | 
					
						
							|  |  |  | 	case SNDRV_PCM_STATE_SUSPENDED: | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	case SNDRV_PCM_STATE_XRUN: | 
					
						
							|  |  |  | 		return -EPIPE; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return -EBADFD; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int snd_pcm_dmix_prepare(snd_pcm_t *pcm) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-12 14:54:41 +00:00
										 |  |  | 	// assert(pcm->boundary == dmix->shmptr->s.boundary);	/* for sure */
 | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	dmix->state = SND_PCM_STATE_PREPARED; | 
					
						
							|  |  |  | 	dmix->appl_ptr = 0; | 
					
						
							|  |  |  | 	dmix->hw_ptr = 0; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int snd_pcm_dmix_reset(snd_pcm_t *pcm) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	dmix->hw_ptr %= pcm->period_size; | 
					
						
							|  |  |  | 	dmix->appl_ptr = dmix->hw_ptr; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 	dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int snd_pcm_dmix_start(snd_pcm_t *pcm) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 	snd_pcm_sframes_t avail; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	int err; | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	if (dmix->state != SND_PCM_STATE_PREPARED) | 
					
						
							|  |  |  | 		return -EBADFD; | 
					
						
							|  |  |  | 	err = snd_timer_start(dmix->timer); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	dmix->state = SND_PCM_STATE_RUNNING; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 	dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr; | 
					
						
							|  |  |  | 	avail = snd_pcm_mmap_playback_hw_avail(pcm); | 
					
						
							|  |  |  | 	if (avail < 0) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	if (avail > (snd_pcm_sframes_t)pcm->buffer_size) | 
					
						
							|  |  |  | 		avail = pcm->buffer_size; | 
					
						
							|  |  |  | 	snd_pcm_dmix_sync_area(pcm, avail); | 
					
						
							| 
									
										
										
										
											2003-02-12 09:02:49 +00:00
										 |  |  | 	gettimeofday(&dmix->trigger_tstamp, 0); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int snd_pcm_dmix_drop(snd_pcm_t *pcm) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	if (dmix->state == SND_PCM_STATE_OPEN) | 
					
						
							|  |  |  | 		return -EBADFD; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 	snd_timer_stop(dmix->timer); | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	dmix->state = SND_PCM_STATE_SETUP; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int snd_pcm_dmix_drain(snd_pcm_t *pcm) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							| 
									
										
										
										
											2003-02-12 14:54:41 +00:00
										 |  |  | 	snd_pcm_uframes_t stop_threshold; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	if (dmix->state == SND_PCM_STATE_OPEN) | 
					
						
							|  |  |  | 		return -EBADFD; | 
					
						
							| 
									
										
										
										
											2003-02-12 14:54:41 +00:00
										 |  |  | 	stop_threshold = pcm->stop_threshold; | 
					
						
							|  |  |  | 	if (pcm->stop_threshold > pcm->buffer_size) | 
					
						
							|  |  |  | 		pcm->stop_threshold = pcm->buffer_size; | 
					
						
							|  |  |  | 	while (dmix->state == SND_PCM_STATE_RUNNING) { | 
					
						
							|  |  |  | 		err = snd_pcm_dmix_sync_ptr(pcm); | 
					
						
							|  |  |  | 		if (err < 0) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		if (pcm->mode & SND_PCM_NONBLOCK) | 
					
						
							|  |  |  | 			return -EAGAIN; | 
					
						
							|  |  |  | 		snd_pcm_wait(pcm, -1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	pcm->stop_threshold = stop_threshold; | 
					
						
							|  |  |  | 	return snd_pcm_dmix_drop(pcm); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int snd_pcm_dmix_pause(snd_pcm_t *pcm, int enable) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  |         if (enable) { | 
					
						
							|  |  |  | 		if (dmix->state != SND_PCM_STATE_RUNNING) | 
					
						
							|  |  |  | 			return -EBADFD; | 
					
						
							|  |  |  | 		dmix->state = SND_PCM_STATE_PAUSED; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 		snd_timer_stop(dmix->timer); | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		if (dmix->state != SND_PCM_STATE_PAUSED) | 
					
						
							|  |  |  | 			return -EBADFD; | 
					
						
							|  |  |  |                 dmix->state = SND_PCM_STATE_RUNNING; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  |                 snd_timer_start(dmix->timer); | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static snd_pcm_sframes_t snd_pcm_dmix_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	/* FIXME: substract samples from the mix ring buffer, too? */ | 
					
						
							|  |  |  | 	snd_pcm_mmap_appl_backward(pcm, frames); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	return frames; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | static int snd_pcm_dmix_resume(snd_pcm_t *pcm ATTRIBUTE_UNUSED) | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	// snd_pcm_dmix_t *dmix = pcm->private_data;
 | 
					
						
							|  |  |  | 	// FIXME
 | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-06 19:15:53 +00:00
										 |  |  | static snd_pcm_sframes_t snd_pcm_dmix_readi(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void *buffer ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED) | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	return -ENODEV; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-06 19:15:53 +00:00
										 |  |  | static snd_pcm_sframes_t snd_pcm_dmix_readn(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void **bufs ATTRIBUTE_UNUSED, snd_pcm_uframes_t size ATTRIBUTE_UNUSED) | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	return -ENODEV; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-06 19:15:53 +00:00
										 |  |  | static int snd_pcm_dmix_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED) | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-06 19:15:53 +00:00
										 |  |  | static int snd_pcm_dmix_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED) | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int snd_pcm_dmix_close(snd_pcm_t *pcm) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dmix->timer) | 
					
						
							|  |  |  | 		snd_timer_close(dmix->timer); | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	semaphore_down(dmix, DMIX_IPC_SEM_CLIENT); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	snd_pcm_close(dmix->spcm); | 
					
						
							|  |  |  |  	if (dmix->server) | 
					
						
							|  |  |  |  		server_discard(dmix); | 
					
						
							|  |  |  |  	if (dmix->client) | 
					
						
							|  |  |  |  		client_discard(dmix); | 
					
						
							|  |  |  |  	if (shm_discard(dmix) > 0) { | 
					
						
							|  |  |  |  		if (semaphore_discard(dmix) < 0) | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  |  			semaphore_up(dmix, DMIX_IPC_SEM_CLIENT); | 
					
						
							|  |  |  |  	} else { | 
					
						
							|  |  |  | 		semaphore_up(dmix, DMIX_IPC_SEM_CLIENT); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	pcm->private_data = NULL; | 
					
						
							|  |  |  | 	free(dmix); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static snd_pcm_sframes_t snd_pcm_dmix_mmap_commit(snd_pcm_t *pcm, | 
					
						
							| 
									
										
										
										
											2003-02-06 19:15:53 +00:00
										 |  |  | 						  snd_pcm_uframes_t offset ATTRIBUTE_UNUSED, | 
					
						
							|  |  |  | 						  snd_pcm_uframes_t size) | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-06 19:15:53 +00:00
										 |  |  | 	snd_pcm_mmap_appl_forward(pcm, size); | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 	if (dmix->state == SND_PCM_STATE_RUNNING) { | 
					
						
							|  |  |  | 		err = snd_pcm_dmix_sync_ptr(pcm); | 
					
						
							|  |  |  | 		if (err < 0) | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 		/* ok, we commit the changes after the validation of area */ | 
					
						
							|  |  |  | 		/* it's intended, although the result might be crappy */ | 
					
						
							|  |  |  | 		snd_pcm_dmix_sync_area(pcm, size); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-06 19:15:53 +00:00
										 |  |  | 	return size; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-06 19:15:53 +00:00
										 |  |  | static snd_pcm_sframes_t snd_pcm_dmix_avail_update(snd_pcm_t *pcm ATTRIBUTE_UNUSED) | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	int err; | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 	if (dmix->state == SND_PCM_STATE_RUNNING) { | 
					
						
							|  |  |  | 		err = snd_pcm_dmix_sync_ptr(pcm); | 
					
						
							|  |  |  | 		if (err < 0) | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	return snd_pcm_mmap_playback_avail(pcm); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void snd_pcm_dmix_dump(snd_pcm_t *pcm, snd_output_t *out) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	snd_pcm_dmix_t *dmix = pcm->private_data; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	snd_output_printf(out, "Direct Stream Mixing PCM\n"); | 
					
						
							|  |  |  | 	if (pcm->setup) { | 
					
						
							|  |  |  | 		snd_output_printf(out, "\nIts setup is:\n"); | 
					
						
							|  |  |  | 		snd_pcm_dump_setup(pcm, out); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	if (dmix->spcm) | 
					
						
							|  |  |  | 		snd_pcm_dump(dmix->spcm, out); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static snd_pcm_ops_t snd_pcm_dmix_ops = { | 
					
						
							|  |  |  | 	close: snd_pcm_dmix_close, | 
					
						
							|  |  |  | 	info: snd_pcm_dmix_info, | 
					
						
							|  |  |  | 	hw_refine: snd_pcm_dmix_hw_refine, | 
					
						
							|  |  |  | 	hw_params: snd_pcm_dmix_hw_params, | 
					
						
							|  |  |  | 	hw_free: snd_pcm_dmix_hw_free, | 
					
						
							|  |  |  | 	sw_params: snd_pcm_dmix_sw_params, | 
					
						
							|  |  |  | 	channel_info: snd_pcm_dmix_channel_info, | 
					
						
							|  |  |  | 	dump: snd_pcm_dmix_dump, | 
					
						
							|  |  |  | 	nonblock: snd_pcm_dmix_nonblock, | 
					
						
							|  |  |  | 	async: snd_pcm_dmix_async, | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	poll_revents: snd_pcm_dmix_poll_revents, | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	mmap: snd_pcm_dmix_mmap, | 
					
						
							|  |  |  | 	munmap: snd_pcm_dmix_munmap, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static snd_pcm_fast_ops_t snd_pcm_dmix_fast_ops = { | 
					
						
							|  |  |  | 	status: snd_pcm_dmix_status, | 
					
						
							|  |  |  | 	state: snd_pcm_dmix_state, | 
					
						
							|  |  |  | 	hwsync: snd_pcm_dmix_hwsync, | 
					
						
							|  |  |  | 	delay: snd_pcm_dmix_delay, | 
					
						
							|  |  |  | 	prepare: snd_pcm_dmix_prepare, | 
					
						
							|  |  |  | 	reset: snd_pcm_dmix_reset, | 
					
						
							|  |  |  | 	start: snd_pcm_dmix_start, | 
					
						
							|  |  |  | 	drop: snd_pcm_dmix_drop, | 
					
						
							|  |  |  | 	drain: snd_pcm_dmix_drain, | 
					
						
							|  |  |  | 	pause: snd_pcm_dmix_pause, | 
					
						
							|  |  |  | 	rewind: snd_pcm_dmix_rewind, | 
					
						
							|  |  |  | 	resume: snd_pcm_dmix_resume, | 
					
						
							| 
									
										
										
										
											2003-02-08 16:35:24 +00:00
										 |  |  | 	writei: snd_pcm_mmap_writei, | 
					
						
							|  |  |  | 	writen: snd_pcm_mmap_writen, | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	readi: snd_pcm_dmix_readi, | 
					
						
							|  |  |  | 	readn: snd_pcm_dmix_readn, | 
					
						
							|  |  |  | 	avail_update: snd_pcm_dmix_avail_update, | 
					
						
							|  |  |  | 	mmap_commit: snd_pcm_dmix_mmap_commit, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * this function initializes hardware and starts playback operation with | 
					
						
							|  |  |  |  * no stop threshold (it operates all time without xrun checking) | 
					
						
							|  |  |  |  * also, the driver silences the unused ring buffer areas for us | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int snd_pcm_dmix_initialize_slave(snd_pcm_dmix_t *dmix, snd_pcm_t *spcm, struct slave_params *params) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_hw_params_t *hw_params; | 
					
						
							|  |  |  | 	snd_pcm_sw_params_t *sw_params; | 
					
						
							|  |  |  | 	int ret, buffer_is_not_initialized; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	snd_pcm_uframes_t boundary; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:46:42 +00:00
										 |  |  | 	struct pollfd fd; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	hw_params = &dmix->shmptr->hw_params; | 
					
						
							|  |  |  | 	sw_params = &dmix->shmptr->sw_params; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = snd_pcm_hw_params_any(spcm, hw_params); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		SNDERR("snd_pcm_hw_params_any failed"); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 	ret = snd_pcm_hw_params_set_access(spcm, hw_params, SND_PCM_ACCESS_MMAP_INTERLEAVED); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 		ret = snd_pcm_hw_params_set_access(spcm, hw_params, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 		if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 			SNDERR("slave plugin does not support mmap interleaved or mmap noninterleaved access"); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 			return ret; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ret = snd_pcm_hw_params_set_format(spcm, hw_params, params->format); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		SNDERR("requested format is not available"); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ret = snd_pcm_hw_params_set_channels(spcm, hw_params, params->channels); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		SNDERR("requested count of channels is not available"); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ret = INTERNAL(snd_pcm_hw_params_set_rate_near)(spcm, hw_params, ¶ms->rate, 0); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		SNDERR("requested rate is not available"); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	buffer_is_not_initialized = 0; | 
					
						
							|  |  |  | 	if (params->buffer_time > 0) { | 
					
						
							|  |  |  | 		ret = INTERNAL(snd_pcm_hw_params_set_buffer_time_near)(spcm, hw_params, ¶ms->buffer_time, 0); | 
					
						
							|  |  |  | 		if (ret < 0) { | 
					
						
							|  |  |  | 			SNDERR("unable to set buffer time"); | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else if (params->buffer_size > 0) { | 
					
						
							|  |  |  | 		ret = INTERNAL(snd_pcm_hw_params_set_buffer_size_near)(spcm, hw_params, ¶ms->buffer_size); | 
					
						
							|  |  |  | 		if (ret < 0) { | 
					
						
							|  |  |  | 			SNDERR("unable to set buffer size"); | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		buffer_is_not_initialized = 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (params->period_time > 0) { | 
					
						
							|  |  |  | 		ret = INTERNAL(snd_pcm_hw_params_set_period_time_near)(spcm, hw_params, ¶ms->period_time, 0); | 
					
						
							|  |  |  | 		if (ret < 0) { | 
					
						
							|  |  |  | 			SNDERR("unable to set period_time"); | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else if (params->period_size > 0) { | 
					
						
							|  |  |  | 		ret = INTERNAL(snd_pcm_hw_params_set_period_size_near)(spcm, hw_params, ¶ms->period_size, 0); | 
					
						
							|  |  |  | 		if (ret < 0) { | 
					
						
							|  |  |  | 			SNDERR("unable to set period_size"); | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}		 | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	if (buffer_is_not_initialized && params->periods > 0) { | 
					
						
							|  |  |  | 		ret = INTERNAL(snd_pcm_hw_params_set_periods_near)(spcm, hw_params, ¶ms->periods, 0); | 
					
						
							|  |  |  | 		if (ret < 0) { | 
					
						
							|  |  |  | 			SNDERR("unable to set requested periods"); | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	ret = snd_pcm_hw_params(spcm, hw_params); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		SNDERR("unable to install hw params"); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = snd_pcm_sw_params_current(spcm, sw_params); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		SNDERR("unable to get current sw_params"); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 	ret = snd_pcm_sw_params_get_boundary(sw_params, &boundary); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 		SNDERR("unable to get boundary\n"); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 	ret = snd_pcm_sw_params_set_stop_threshold(spcm, sw_params, boundary); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		SNDERR("unable to set stop threshold\n"); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	ret = snd_pcm_sw_params_set_silence_threshold(spcm, sw_params, 0); | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 	if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 		SNDERR("unable to set silence threshold\n"); | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	ret = snd_pcm_sw_params_set_silence_size(spcm, sw_params, boundary); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 		SNDERR("unable to set silence threshold (please upgrade to 0.9.0rc8+ driver)\n"); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = snd_pcm_sw_params(spcm, sw_params); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		SNDERR("unable to install sw params"); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	ret = snd_pcm_start(spcm); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		SNDERR("unable to start PCM stream\n"); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-11 21:46:42 +00:00
										 |  |  | 	if (snd_pcm_poll_descriptors_count(spcm) != 1) { | 
					
						
							|  |  |  | 		SNDERR("unable to use hardware pcm with fd more than one!!!\n"); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	snd_pcm_poll_descriptors(spcm, &fd, 1); | 
					
						
							|  |  |  | 	dmix->hw_fd = fd.fd; | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	dmix->shmptr->s.boundary = spcm->boundary; | 
					
						
							|  |  |  | 	dmix->shmptr->s.buffer_size = spcm->buffer_size; | 
					
						
							| 
									
										
										
										
											2003-02-12 09:02:49 +00:00
										 |  |  | 	dmix->shmptr->s.sample_bits = spcm->sample_bits; | 
					
						
							|  |  |  | 	dmix->shmptr->s.channels = spcm->channels; | 
					
						
							|  |  |  | 	dmix->shmptr->s.rate = spcm->rate; | 
					
						
							|  |  |  | 	dmix->shmptr->s.format = spcm->format; | 
					
						
							|  |  |  | 	dmix->shmptr->s.boundary = spcm->boundary; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-12 09:02:49 +00:00
										 |  |  | 	spcm->donot_close = 1; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * the trick is used here; we cannot use effectively the hardware handle because | 
					
						
							|  |  |  |  * we cannot drive multiple accesses to appl_ptr; so we use slave timer of given | 
					
						
							|  |  |  |  * PCM hardware handle; it's not this easy and cheap? | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int snd_pcm_dmix_initialize_poll_fd(snd_pcm_dmix_t *dmix) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 	snd_pcm_info_t *info; | 
					
						
							|  |  |  | 	snd_timer_params_t *params; | 
					
						
							|  |  |  | 	char name[128]; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	struct pollfd fd; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	 | 
					
						
							|  |  |  | 	snd_pcm_info_alloca(&info); | 
					
						
							|  |  |  | 	snd_timer_params_alloca(¶ms); | 
					
						
							|  |  |  | 	ret = snd_pcm_info(dmix->spcm, info); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		SNDERR("unable to info for slave pcm\n"); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sprintf(name, "hw:CLASS=%i,SCLASS=0,CARD=%i,DEV=%i,SUBDEV=%i", | 
					
						
							|  |  |  | 				(int)SND_TIMER_CLASS_PCM,  | 
					
						
							|  |  |  | 				snd_pcm_info_get_card(info), | 
					
						
							|  |  |  | 				snd_pcm_info_get_device(info), | 
					
						
							|  |  |  | 				snd_pcm_info_get_subdevice(info) * 2);	/* it's a bit trick to distict playback and capture */ | 
					
						
							|  |  |  | 	ret = snd_timer_open(&dmix->timer, name, SND_TIMER_OPEN_NONBLOCK); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		SNDERR("unable to open timer '%s'\n", name); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	snd_timer_params_set_auto_start(params, 1); | 
					
						
							|  |  |  | 	snd_timer_params_set_ticks(params, 1); | 
					
						
							|  |  |  | 	ret = snd_timer_params(dmix->timer, params); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		SNDERR("unable to set timer parameters\n", name); | 
					
						
							|  |  |  |                 return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	if (snd_timer_poll_descriptors_count(dmix->timer) != 1) { | 
					
						
							|  |  |  | 		SNDERR("unable to use timer with fd more than one!!!\n", name); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	snd_timer_poll_descriptors(dmix->timer, &fd, 1); | 
					
						
							|  |  |  | 	dmix->poll_fd = fd.fd; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * \brief Creates a new dmix PCM | 
					
						
							|  |  |  |  * \param pcmp Returns created PCM handle | 
					
						
							|  |  |  |  * \param name Name of PCM | 
					
						
							|  |  |  |  * \param ipc_key IPC key for semaphore and shared memory | 
					
						
							|  |  |  |  * \param params Parameters for slave | 
					
						
							|  |  |  |  * \param root Configuration root | 
					
						
							|  |  |  |  * \param sconf Slave configuration | 
					
						
							|  |  |  |  * \param stream PCM Direction (stream) | 
					
						
							|  |  |  |  * \param mode PCM Mode | 
					
						
							|  |  |  |  * \retval zero on success otherwise a negative error code | 
					
						
							|  |  |  |  * \warning Using of this function might be dangerous in the sense | 
					
						
							|  |  |  |  *          of compatibility reasons. The prototype might be freely | 
					
						
							|  |  |  |  *          changed in future. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int snd_pcm_dmix_open(snd_pcm_t **pcmp, const char *name, | 
					
						
							|  |  |  | 		      key_t ipc_key, struct slave_params *params, | 
					
						
							|  |  |  | 		      snd_config_t *root, snd_config_t *sconf, | 
					
						
							|  |  |  | 		      snd_pcm_stream_t stream, int mode) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_t *pcm = NULL, *spcm = NULL; | 
					
						
							|  |  |  | 	snd_pcm_dmix_t *dmix = NULL; | 
					
						
							|  |  |  | 	int ret, first_instance; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	assert(pcmp); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (stream != SND_PCM_STREAM_PLAYBACK) { | 
					
						
							|  |  |  | 		SNDERR("The dmix plugin supports only playback stream"); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dmix = calloc(1, sizeof(snd_pcm_dmix_t)); | 
					
						
							|  |  |  | 	if (!dmix) { | 
					
						
							|  |  |  | 		ret = -ENOMEM; | 
					
						
							|  |  |  | 		goto _err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	dmix->ipc_key = ipc_key; | 
					
						
							|  |  |  | 	dmix->semid = -1; | 
					
						
							|  |  |  | 	dmix->shmid = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = snd_pcm_new(&pcm, SND_PCM_TYPE_DMIX, name, stream, mode); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		goto _err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = semaphore_create_or_connect(dmix); | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		SNDERR("unable to create IPC semaphore\n"); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 		goto _err; | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	 | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	ret = semaphore_down(dmix, DMIX_IPC_SEM_CLIENT); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		semaphore_discard(dmix); | 
					
						
							|  |  |  | 		goto _err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 	first_instance = ret = shm_create_or_connect(dmix); | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		SNDERR("unable to create IPC shm instance\n"); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 		goto _err; | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 		 | 
					
						
							|  |  |  | 	pcm->ops = &snd_pcm_dmix_ops; | 
					
						
							|  |  |  | 	pcm->fast_ops = &snd_pcm_dmix_fast_ops; | 
					
						
							|  |  |  | 	pcm->private_data = dmix; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	dmix->state = SND_PCM_STATE_OPEN; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (first_instance) { | 
					
						
							|  |  |  | 		ret = snd_pcm_open_slave(&spcm, root, sconf, stream, mode); | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 		if (ret < 0) { | 
					
						
							|  |  |  | 			SNDERR("unable to open slave\n"); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 			goto _err; | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	 | 
					
						
							|  |  |  | 		if (snd_pcm_type(spcm) != SND_PCM_TYPE_HW) { | 
					
						
							|  |  |  | 			SNDERR("dmix plugin can be only connected to hw plugin"); | 
					
						
							|  |  |  | 			goto _err; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 		ret = snd_pcm_dmix_initialize_slave(dmix, spcm, params); | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 		if (ret < 0) { | 
					
						
							|  |  |  | 			SNDERR("unable to initialize slave\n"); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 			goto _err; | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		dmix->spcm = spcm; | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 		ret = server_create(dmix); | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 		if (ret < 0) { | 
					
						
							|  |  |  | 			SNDERR("unable to create server\n"); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 			goto _err; | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2003-02-11 21:46:42 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		dmix->shmptr->type = spcm->type; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		ret = client_connect(dmix); | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 		if (ret < 0) { | 
					
						
							|  |  |  | 			SNDERR("unable to connect client\n"); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 			return ret; | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 			 | 
					
						
							|  |  |  | 		ret = snd_pcm_hw_open_fd(&spcm, "dmix_client", dmix->hw_fd, 0); | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 		if (ret < 0) { | 
					
						
							|  |  |  | 			SNDERR("unable to open hardware\n"); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 			goto _err; | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2003-02-11 21:46:42 +00:00
										 |  |  | 		 | 
					
						
							| 
									
										
										
										
											2003-02-12 09:02:49 +00:00
										 |  |  | 		spcm->donot_close = 1; | 
					
						
							|  |  |  | 		spcm->setup = 1; | 
					
						
							|  |  |  | 		spcm->buffer_size = dmix->shmptr->s.buffer_size; | 
					
						
							|  |  |  | 		spcm->sample_bits = dmix->shmptr->s.sample_bits; | 
					
						
							|  |  |  | 		spcm->channels = dmix->shmptr->s.channels; | 
					
						
							|  |  |  | 		spcm->format = dmix->shmptr->s.format; | 
					
						
							|  |  |  | 		spcm->boundary = dmix->shmptr->s.boundary; | 
					
						
							|  |  |  | 		ret = snd_pcm_mmap(spcm); | 
					
						
							|  |  |  | 		if (ret < 0) { | 
					
						
							|  |  |  | 			SNDERR("unable to mmap channels\n"); | 
					
						
							|  |  |  | 			goto _err; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2003-02-11 21:46:42 +00:00
										 |  |  | 		dmix->spcm = spcm; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = snd_pcm_dmix_initialize_poll_fd(dmix); | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		SNDERR("unable to initialize poll_fd\n"); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 		goto _err; | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 		 | 
					
						
							|  |  |  | 	pcm->poll_fd = dmix->poll_fd; | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	pcm->poll_events = POLLIN;	/* it's different than other plugins */ | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 		 | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 	pcm->mmap_rw = 1; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	snd_pcm_set_hw_ptr(pcm, &dmix->hw_ptr, -1, 0); | 
					
						
							|  |  |  | 	snd_pcm_set_appl_ptr(pcm, &dmix->appl_ptr, -1, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  | 	semaphore_up(dmix, DMIX_IPC_SEM_CLIENT); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	*pcmp = pcm; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  |  _err: | 
					
						
							|  |  |  |  	if (dmix->timer) | 
					
						
							|  |  |  |  		snd_timer_close(dmix->timer); | 
					
						
							|  |  |  |  	if (dmix->server) | 
					
						
							|  |  |  |  		server_discard(dmix); | 
					
						
							|  |  |  |  	if (dmix->client) | 
					
						
							|  |  |  |  		client_discard(dmix); | 
					
						
							|  |  |  |  	if (spcm) | 
					
						
							|  |  |  |  		snd_pcm_close(spcm); | 
					
						
							|  |  |  |  	if (dmix->shmid >= 0) { | 
					
						
							|  |  |  |  		if (shm_discard(dmix) > 0) { | 
					
						
							|  |  |  | 		 	if (dmix->semid >= 0) { | 
					
						
							|  |  |  |  				if (semaphore_discard(dmix) < 0) | 
					
						
							| 
									
										
										
										
											2003-02-11 18:14:43 +00:00
										 |  |  |  					semaphore_up(dmix, DMIX_IPC_SEM_CLIENT); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  |  			} | 
					
						
							|  |  |  |  		} | 
					
						
							|  |  |  |  	} | 
					
						
							|  |  |  | 	if (dmix) | 
					
						
							|  |  |  | 		free(dmix); | 
					
						
							|  |  |  | 	if (pcm) | 
					
						
							|  |  |  | 		snd_pcm_free(pcm); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*! \page pcm_plugins
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \section pcm_plugins_dmix Plugin: dmix | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This plugin provides direct mixing of multiple streams. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \code | 
					
						
							|  |  |  | pcm.name { | 
					
						
							|  |  |  | 	type dmix		# Direct mix | 
					
						
							|  |  |  | 	ipc_key INT		# unique IPC key | 
					
						
							| 
									
										
										
										
											2003-02-12 15:22:32 +00:00
										 |  |  | 	ipc_key_add_uid BOOL	# add current uid to unique IPC key | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	slave STR | 
					
						
							|  |  |  | 	# or
 | 
					
						
							|  |  |  | 	slave {			# Slave definition | 
					
						
							|  |  |  | 		pcm STR		# slave PCM name | 
					
						
							|  |  |  | 		# or
 | 
					
						
							|  |  |  | 		pcm { }		# slave PCM definition | 
					
						
							|  |  |  | 		format STR	# format definition | 
					
						
							|  |  |  | 		rate INT	# rate definition | 
					
						
							|  |  |  | 		channels INT | 
					
						
							|  |  |  | 		period_time INT	# in usec | 
					
						
							|  |  |  | 		# or
 | 
					
						
							|  |  |  | 		period_size INT	# in bytes | 
					
						
							|  |  |  | 		buffer_time INT	# in usec | 
					
						
							|  |  |  | 		# or
 | 
					
						
							|  |  |  | 		buffer_size INT # in bytes | 
					
						
							|  |  |  | 		periods INT	# when buffer_size or buffer_time is not specified | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | \endcode | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \subsection pcm_plugins_hw_funcref Function reference | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <UL> | 
					
						
							|  |  |  |   <LI>snd_pcm_dmix_open() | 
					
						
							|  |  |  |   <LI>_snd_pcm_dmix_open() | 
					
						
							|  |  |  | </UL> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * \brief Creates a new dmix PCM | 
					
						
							|  |  |  |  * \param pcmp Returns created PCM handle | 
					
						
							|  |  |  |  * \param name Name of PCM | 
					
						
							|  |  |  |  * \param root Root configuration node | 
					
						
							|  |  |  |  * \param conf Configuration node with dmix PCM description | 
					
						
							|  |  |  |  * \param stream PCM Stream | 
					
						
							|  |  |  |  * \param mode PCM Mode | 
					
						
							|  |  |  |  * \warning Using of this function might be dangerous in the sense | 
					
						
							|  |  |  |  *          of compatibility reasons. The prototype might be freely | 
					
						
							|  |  |  |  *          changed in future. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int _snd_pcm_dmix_open(snd_pcm_t **pcmp, const char *name, | 
					
						
							|  |  |  | 		       snd_config_t *root, snd_config_t *conf, | 
					
						
							|  |  |  | 		       snd_pcm_stream_t stream, int mode) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_config_iterator_t i, next; | 
					
						
							|  |  |  | 	snd_config_t *slave = NULL, *sconf; | 
					
						
							|  |  |  | 	struct slave_params params; | 
					
						
							| 
									
										
										
										
											2003-02-12 15:22:32 +00:00
										 |  |  | 	int bsize, psize, ipc_key_add_uid = 0; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	key_t ipc_key = 0; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 	snd_config_for_each(i, next, conf) { | 
					
						
							|  |  |  | 		snd_config_t *n = snd_config_iterator_entry(i); | 
					
						
							|  |  |  | 		const char *id; | 
					
						
							|  |  |  | 		if (snd_config_get_id(n, &id) < 0) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		if (snd_pcm_conf_generic_id(id)) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		if (strcmp(id, "ipc_key") == 0) { | 
					
						
							|  |  |  | 			long key; | 
					
						
							|  |  |  | 			err = snd_config_get_integer(n, &key); | 
					
						
							|  |  |  | 			if (err < 0) { | 
					
						
							|  |  |  | 				SNDERR("The field ipc_key must be an integer type"); | 
					
						
							|  |  |  | 				return err; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			ipc_key = key; | 
					
						
							| 
									
										
										
										
											2003-02-05 10:34:41 +00:00
										 |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2003-02-12 15:22:32 +00:00
										 |  |  | 		if (strcmp(id, "ipc_key_add_uid") == 0) { | 
					
						
							|  |  |  | 			char *tmp; | 
					
						
							|  |  |  | 			err = snd_config_get_ascii(n, &tmp); | 
					
						
							|  |  |  | 			if (err < 0) { | 
					
						
							|  |  |  | 				SNDERR("The field ipc_key_add_uid must be a boolean type"); | 
					
						
							|  |  |  | 				return err; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			err = snd_config_get_bool_ascii(tmp); | 
					
						
							|  |  |  | 			free(tmp); | 
					
						
							|  |  |  | 			if (err < 0) { | 
					
						
							|  |  |  | 				SNDERR("The field ipc_key_add_uid must be a boolean type"); | 
					
						
							|  |  |  | 				return err; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			ipc_key_add_uid = err; | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 		if (strcmp(id, "slave") == 0) { | 
					
						
							|  |  |  | 			slave = n; | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		SNDERR("Unknown field %s", id); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (!slave) { | 
					
						
							|  |  |  | 		SNDERR("slave is not defined"); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2003-02-12 15:22:32 +00:00
										 |  |  | 	if (ipc_key_add_uid) | 
					
						
							|  |  |  | 		ipc_key += getuid(); | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	if (!ipc_key) { | 
					
						
							|  |  |  | 		SNDERR("Unique IPC key is not defined"); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	/* the default settings, it might be invalid for some hardware */ | 
					
						
							|  |  |  | 	params.format = SND_PCM_FORMAT_S16; | 
					
						
							|  |  |  | 	params.rate = 48000; | 
					
						
							|  |  |  | 	params.channels = 2; | 
					
						
							|  |  |  | 	params.period_time = 125000;	/* 0.125 seconds */ | 
					
						
							|  |  |  | 	params.buffer_time = -1; | 
					
						
							|  |  |  | 	bsize = psize = -1; | 
					
						
							|  |  |  | 	params.periods = 3; | 
					
						
							|  |  |  | 	err = snd_pcm_slave_conf(root, slave, &sconf, 8, | 
					
						
							|  |  |  | 				 SND_PCM_HW_PARAM_FORMAT, 0, ¶ms.format, | 
					
						
							|  |  |  | 				 SND_PCM_HW_PARAM_RATE, 0, ¶ms.rate, | 
					
						
							|  |  |  | 				 SND_PCM_HW_PARAM_CHANNELS, 0, ¶ms.channels, | 
					
						
							|  |  |  | 				 SND_PCM_HW_PARAM_PERIOD_TIME, 0, ¶ms.period_time, | 
					
						
							|  |  |  | 				 SND_PCM_HW_PARAM_BUFFER_TIME, 0, ¶ms.buffer_time, | 
					
						
							|  |  |  | 				 SND_PCM_HW_PARAM_PERIOD_SIZE, 0, &bsize, | 
					
						
							|  |  |  | 				 SND_PCM_HW_PARAM_BUFFER_SIZE, 0, &psize, | 
					
						
							|  |  |  | 				 SND_PCM_HW_PARAM_PERIODS, 0, ¶ms.periods); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		return err; | 
					
						
							| 
									
										
										
										
											2003-02-11 21:28:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	assert(params.format == SND_PCM_FORMAT_S16); /* sorry, limited features */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-02-04 14:48:51 +00:00
										 |  |  | 	params.period_size = psize; | 
					
						
							|  |  |  | 	params.buffer_size = bsize; | 
					
						
							|  |  |  | 	err = snd_pcm_dmix_open(pcmp, name, ipc_key, ¶ms, root, sconf, stream, mode); | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		snd_config_delete(sconf); | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #ifndef DOC_HIDDEN
 | 
					
						
							|  |  |  | SND_DLSYM_BUILD_VERSION(_snd_pcm_dmix_open, SND_PCM_DLSYM_VERSION); | 
					
						
							|  |  |  | #endif
 |