Blame src/pcm/pcm_hw.c

Packit 4a16fb
/**
Packit 4a16fb
 * \file pcm/pcm_hw.c
Packit 4a16fb
 * \ingroup PCM_Plugins
Packit 4a16fb
 * \brief PCM HW Plugin Interface
Packit 4a16fb
 * \author Abramo Bagnara <abramo@alsa-project.org>
Packit 4a16fb
 * \author Jaroslav Kysela <perex@perex.cz>
Packit 4a16fb
 * \date 2000-2001
Packit 4a16fb
 */
Packit 4a16fb
/*
Packit 4a16fb
 *  PCM - Hardware
Packit 4a16fb
 *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
Packit 4a16fb
 *
Packit 4a16fb
 *
Packit 4a16fb
 *   This library is free software; you can redistribute it and/or modify
Packit 4a16fb
 *   it under the terms of the GNU Lesser General Public License as
Packit 4a16fb
 *   published by the Free Software Foundation; either version 2.1 of
Packit 4a16fb
 *   the License, or (at your option) any later version.
Packit 4a16fb
 *
Packit 4a16fb
 *   This program is distributed in the hope that it will be useful,
Packit 4a16fb
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 4a16fb
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 4a16fb
 *   GNU Lesser General Public License for more details.
Packit 4a16fb
 *
Packit 4a16fb
 *   You should have received a copy of the GNU Lesser General Public
Packit 4a16fb
 *   License along with this library; if not, write to the Free Software
Packit 4a16fb
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit 4a16fb
 *
Packit 4a16fb
 */
Packit 4a16fb
  
Packit 4a16fb
#include <stdio.h>
Packit 4a16fb
#include <stdlib.h>
Packit 4a16fb
#include <stddef.h>
Packit 4a16fb
#include <unistd.h>
Packit 4a16fb
#include <stdbool.h>
Packit 4a16fb
#include <signal.h>
Packit 4a16fb
#include <string.h>
Packit 4a16fb
#include <fcntl.h>
Packit 4a16fb
#include <sys/ioctl.h>
Packit 4a16fb
#include <sys/mman.h>
Packit 4a16fb
#include "pcm_local.h"
Packit 4a16fb
#include "../control/control_local.h"
Packit 4a16fb
#include "../timer/timer_local.h"
Packit 4a16fb
Packit 4a16fb
//#define DEBUG_RW		/* use to debug readi/writei/readn/writen */
Packit 4a16fb
//#define DEBUG_MMAP		/* debug mmap_commit */
Packit 4a16fb
Packit 4a16fb
#ifndef PIC
Packit 4a16fb
/* entry for static linking */
Packit 4a16fb
const char *_snd_module_pcm_hw = "";
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
#ifndef DOC_HIDDEN
Packit 4a16fb
Packit 4a16fb
#ifndef F_SETSIG
Packit 4a16fb
#define F_SETSIG 10
Packit 4a16fb
#endif
Packit 4a16fb
Packit 4a16fb
/*
Packit 4a16fb
 *  Compatibility
Packit 4a16fb
 */
Packit 4a16fb
Packit 4a16fb
struct sndrv_pcm_hw_params_old {
Packit 4a16fb
	unsigned int flags;
Packit 4a16fb
	unsigned int masks[SNDRV_PCM_HW_PARAM_SUBFORMAT -
Packit 4a16fb
			   SNDRV_PCM_HW_PARAM_ACCESS + 1];
Packit 4a16fb
	struct snd_interval intervals[SNDRV_PCM_HW_PARAM_TICK_TIME -
Packit 4a16fb
					SNDRV_PCM_HW_PARAM_SAMPLE_BITS + 1];
Packit 4a16fb
	unsigned int rmask;
Packit 4a16fb
	unsigned int cmask;
Packit 4a16fb
	unsigned int info;
Packit 4a16fb
	unsigned int msbits;
Packit 4a16fb
	unsigned int rate_num;
Packit 4a16fb
	unsigned int rate_den;
Packit 4a16fb
	sndrv_pcm_uframes_t fifo_size;
Packit 4a16fb
	unsigned char reserved[64];
Packit 4a16fb
};
Packit 4a16fb
Packit 4a16fb
#define SND_PCM_IOCTL_HW_REFINE_OLD _IOWR('A', 0x10, struct sndrv_pcm_hw_params_old)
Packit 4a16fb
#define SND_PCM_IOCTL_HW_PARAMS_OLD _IOWR('A', 0x11, struct sndrv_pcm_hw_params_old)
Packit 4a16fb
Packit 4a16fb
static int use_old_hw_params_ioctl(int fd, unsigned int cmd, snd_pcm_hw_params_t *params);
Packit 4a16fb
static snd_pcm_sframes_t snd_pcm_hw_avail_update(snd_pcm_t *pcm);
Packit 4a16fb
static const snd_pcm_fast_ops_t snd_pcm_hw_fast_ops;
Packit 4a16fb
static const snd_pcm_fast_ops_t snd_pcm_hw_fast_ops_timer;
Packit 4a16fb
Packit 4a16fb
/*
Packit 4a16fb
 *
Packit 4a16fb
 */
Packit 4a16fb
Packit 4a16fb
typedef struct {
Packit 4a16fb
	int version;
Packit 4a16fb
	int fd;
Packit 4a16fb
	int card, device, subdevice;
Packit 4a16fb
Packit 4a16fb
	volatile struct snd_pcm_mmap_status * mmap_status;
Packit 4a16fb
	struct snd_pcm_mmap_control *mmap_control;
Packit 4a16fb
	bool mmap_status_fallbacked;
Packit 4a16fb
	bool mmap_control_fallbacked;
Packit 4a16fb
	struct snd_pcm_sync_ptr *sync_ptr;
Packit 4a16fb
Packit 4a16fb
	int period_event;
Packit 4a16fb
	snd_timer_t *period_timer;
Packit 4a16fb
	struct pollfd period_timer_pfd;
Packit 4a16fb
	int period_timer_need_poll;
Packit 4a16fb
	/* restricted parameters */
Packit 4a16fb
	snd_pcm_format_t format;
Packit 4a16fb
	int rate;
Packit 4a16fb
	int channels;
Packit 4a16fb
	/* for chmap */
Packit 4a16fb
	unsigned int chmap_caps;
Packit 4a16fb
	snd_pcm_chmap_query_t **chmap_override;
Packit 4a16fb
} snd_pcm_hw_t;
Packit 4a16fb
Packit 4a16fb
#define SNDRV_FILE_PCM_STREAM_PLAYBACK		ALSA_DEVICE_DIRECTORY "pcmC%iD%ip"
Packit 4a16fb
#define SNDRV_FILE_PCM_STREAM_CAPTURE		ALSA_DEVICE_DIRECTORY "pcmC%iD%ic"
Packit 4a16fb
#define SNDRV_PCM_VERSION_MAX			SNDRV_PROTOCOL_VERSION(2, 0, 9)
Packit 4a16fb
Packit 4a16fb
/* update appl_ptr with driver */
Packit 4a16fb
#define FAST_PCM_STATE(hw) \
Packit 4a16fb
	((snd_pcm_state_t) (hw)->mmap_status->state)
Packit 4a16fb
#define FAST_PCM_TSTAMP(hw) \
Packit 4a16fb
	((hw)->mmap_status->tstamp)
Packit 4a16fb
Packit 4a16fb
struct timespec snd_pcm_hw_fast_tstamp(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	struct timespec res;
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	res = FAST_PCM_TSTAMP(hw);
Packit 4a16fb
	if (SNDRV_PROTOCOL_VERSION(2, 0, 5) > hw->version)
Packit 4a16fb
		res.tv_nsec *= 1000L;
Packit 4a16fb
	return res;
Packit 4a16fb
}
Packit 4a16fb
#endif /* DOC_HIDDEN */
Packit 4a16fb
Packit 4a16fb
static int sync_ptr1(snd_pcm_hw_t *hw, unsigned int flags)
Packit 4a16fb
{
Packit 4a16fb
	int err;
Packit 4a16fb
	hw->sync_ptr->flags = flags;
Packit 4a16fb
	if (ioctl(hw->fd, SNDRV_PCM_IOCTL_SYNC_PTR, hw->sync_ptr) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_SYNC_PTR failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int issue_avail_min(snd_pcm_hw_t *hw)
Packit 4a16fb
{
Packit 4a16fb
	if (!hw->mmap_control_fallbacked)
Packit 4a16fb
		return 0;
Packit 4a16fb
Packit 4a16fb
	/* Avoid unexpected change of applptr in kernel space. */
Packit 4a16fb
	return sync_ptr1(hw, SNDRV_PCM_SYNC_PTR_APPL);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int issue_applptr(snd_pcm_hw_t *hw)
Packit 4a16fb
{
Packit 4a16fb
	if (!hw->mmap_control_fallbacked)
Packit 4a16fb
		return 0;
Packit 4a16fb
Packit 4a16fb
	/* Avoid unexpected change of avail_min in kernel space. */
Packit 4a16fb
	return sync_ptr1(hw, SNDRV_PCM_SYNC_PTR_AVAIL_MIN);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int request_hwsync(snd_pcm_hw_t *hw)
Packit 4a16fb
{
Packit 4a16fb
	if (!hw->mmap_status_fallbacked)
Packit 4a16fb
		return 0;
Packit 4a16fb
Packit 4a16fb
	/*
Packit 4a16fb
	 * Query both of control/status data to avoid unexpected change of
Packit 4a16fb
	 * control data in kernel space.
Packit 4a16fb
	 */
Packit 4a16fb
	return sync_ptr1(hw,
Packit 4a16fb
			 SNDRV_PCM_SYNC_PTR_HWSYNC |
Packit 4a16fb
			 SNDRV_PCM_SYNC_PTR_APPL |
Packit 4a16fb
			 SNDRV_PCM_SYNC_PTR_AVAIL_MIN);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int query_status_and_control_data(snd_pcm_hw_t *hw)
Packit 4a16fb
{
Packit 4a16fb
	if (!hw->mmap_control_fallbacked)
Packit 4a16fb
		return 0;
Packit 4a16fb
Packit 4a16fb
	/*
Packit 4a16fb
	 * Query both of control/status data to avoid unexpected change of
Packit 4a16fb
	 * control data in kernel space.
Packit 4a16fb
	 */
Packit 4a16fb
	return sync_ptr1(hw,
Packit 4a16fb
			 SNDRV_PCM_SYNC_PTR_APPL |
Packit 4a16fb
			 SNDRV_PCM_SYNC_PTR_AVAIL_MIN);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int query_status_data(snd_pcm_hw_t *hw)
Packit 4a16fb
{
Packit 4a16fb
	if (!hw->mmap_status_fallbacked)
Packit 4a16fb
		return 0;
Packit 4a16fb
Packit 4a16fb
	/*
Packit 4a16fb
	 * Query both of control/status data to avoid unexpected change of
Packit 4a16fb
	 * control data in kernel space.
Packit 4a16fb
	 */
Packit 4a16fb
	return sync_ptr1(hw,
Packit 4a16fb
			 SNDRV_PCM_SYNC_PTR_APPL |
Packit 4a16fb
			 SNDRV_PCM_SYNC_PTR_AVAIL_MIN);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_clear_timer_queue(snd_pcm_hw_t *hw)
Packit 4a16fb
{
Packit 4a16fb
	if (hw->period_timer_need_poll) {
Packit 4a16fb
		while (poll(&hw->period_timer_pfd, 1, 0) > 0) {
Packit 4a16fb
			snd_timer_tread_t rbuf[4];
Packit 4a16fb
			snd_timer_read(hw->period_timer, rbuf, sizeof(rbuf));
Packit 4a16fb
		}
Packit 4a16fb
	} else {
Packit 4a16fb
		snd_timer_tread_t rbuf[4];
Packit 4a16fb
		snd_timer_read(hw->period_timer, rbuf, sizeof(rbuf));
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_poll_descriptors_count(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
Packit 4a16fb
{
Packit 4a16fb
	return 2;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
Packit 4a16fb
	if (space < 2)
Packit 4a16fb
		return -ENOMEM;
Packit 4a16fb
	pfds[0].fd = hw->fd;
Packit 4a16fb
	pfds[0].events = pcm->poll_events | POLLERR | POLLNVAL;
Packit 4a16fb
	pfds[1].fd = hw->period_timer_pfd.fd;
Packit 4a16fb
	pfds[1].events = POLLIN | POLLERR | POLLNVAL;
Packit 4a16fb
	return 2;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned nfds, unsigned short *revents)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	unsigned int events;
Packit 4a16fb
Packit 4a16fb
	if (nfds != 2 || pfds[0].fd != hw->fd || pfds[1].fd != hw->period_timer_pfd.fd)
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	events = pfds[0].revents;
Packit 4a16fb
	if (pfds[1].revents & POLLIN) {
Packit 4a16fb
		snd_pcm_hw_clear_timer_queue(hw);
Packit 4a16fb
		events |= pcm->poll_events & ~(POLLERR|POLLNVAL);
Packit 4a16fb
	}
Packit 4a16fb
	*revents = events;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_nonblock(snd_pcm_t *pcm, int nonblock)
Packit 4a16fb
{
Packit 4a16fb
	long flags;
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int fd = hw->fd, err;
Packit 4a16fb
Packit 4a16fb
	if ((flags = fcntl(fd, F_GETFL)) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("F_GETFL failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (nonblock)
Packit 4a16fb
		flags |= O_NONBLOCK;
Packit 4a16fb
	else
Packit 4a16fb
		flags &= ~O_NONBLOCK;
Packit 4a16fb
	if (fcntl(fd, F_SETFL, flags) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("F_SETFL for O_NONBLOCK failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_async(snd_pcm_t *pcm, int sig, pid_t pid)
Packit 4a16fb
{
Packit 4a16fb
	long flags;
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int fd = hw->fd, err;
Packit 4a16fb
Packit 4a16fb
	if ((flags = fcntl(fd, F_GETFL)) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("F_GETFL failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (sig >= 0)
Packit 4a16fb
		flags |= O_ASYNC;
Packit 4a16fb
	else
Packit 4a16fb
		flags &= ~O_ASYNC;
Packit 4a16fb
	if (fcntl(fd, F_SETFL, flags) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("F_SETFL for O_ASYNC failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (sig < 0)
Packit 4a16fb
		return 0;
Packit 4a16fb
	if (fcntl(fd, F_SETSIG, (long)sig) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("F_SETSIG failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (fcntl(fd, F_SETOWN, (long)pid) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("F_SETOWN failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int fd = hw->fd, err;
Packit 4a16fb
	if (ioctl(fd, SNDRV_PCM_IOCTL_INFO, info) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_INFO failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static inline int hw_refine_call(snd_pcm_hw_t *pcm_hw, snd_pcm_hw_params_t *params)
Packit 4a16fb
{
Packit 4a16fb
	/* check for new hw_params structure; it's available from 2.0.2 version of PCM API */
Packit 4a16fb
	if (SNDRV_PROTOCOL_VERSION(2, 0, 2) <= pcm_hw->version)
Packit 4a16fb
		return ioctl(pcm_hw->fd, SNDRV_PCM_IOCTL_HW_REFINE, params);
Packit 4a16fb
	return use_old_hw_params_ioctl(pcm_hw->fd, SND_PCM_IOCTL_HW_REFINE_OLD, params);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
Packit 4a16fb
	if (hw->format != SND_PCM_FORMAT_UNKNOWN) {
Packit 4a16fb
		err = _snd_pcm_hw_params_set_format(params, hw->format);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (hw->channels > 0) {
Packit 4a16fb
		err = _snd_pcm_hw_param_set(params, SND_PCM_HW_PARAM_CHANNELS,
Packit 4a16fb
					    hw->channels, 0);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
	if (hw->rate > 0) {
Packit 4a16fb
		err = _snd_pcm_hw_param_set_minmax(params, SND_PCM_HW_PARAM_RATE,
Packit 4a16fb
						   hw->rate, 0, hw->rate + 1, -1);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	if (hw_refine_call(hw, params) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		// SYSMSG("SNDRV_PCM_IOCTL_HW_REFINE failed");
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	if (params->info != ~0U) {
Packit 4a16fb
		params->info &= ~0xf0000000;
Packit 4a16fb
		if (pcm->tstamp_type != SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY)
Packit 4a16fb
			params->info |= SND_PCM_INFO_MONOTONIC;
Packit 4a16fb
	}
Packit 4a16fb
	
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static inline int hw_params_call(snd_pcm_hw_t *pcm_hw, snd_pcm_hw_params_t *params)
Packit 4a16fb
{
Packit 4a16fb
	/* check for new hw_params structure; it's available from 2.0.2 version of PCM API */
Packit 4a16fb
	if (SNDRV_PROTOCOL_VERSION(2, 0, 2) <= pcm_hw->version)
Packit 4a16fb
		return ioctl(pcm_hw->fd, SNDRV_PCM_IOCTL_HW_PARAMS, params);
Packit 4a16fb
	return use_old_hw_params_ioctl(pcm_hw->fd, SND_PCM_IOCTL_HW_PARAMS_OLD, params);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
	if (hw_params_call(hw, params) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_HW_PARAMS failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	params->info &= ~0xf0000000;
Packit 4a16fb
	if (pcm->tstamp_type != SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY)
Packit 4a16fb
		params->info |= SND_PCM_INFO_MONOTONIC;
Packit 4a16fb
	return query_status_data(hw);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void snd_pcm_hw_close_timer(snd_pcm_hw_t *hw)
Packit 4a16fb
{
Packit 4a16fb
	if (hw->period_timer) {
Packit 4a16fb
		snd_timer_close(hw->period_timer);
Packit 4a16fb
		hw->period_timer = NULL;
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_change_timer(snd_pcm_t *pcm, int enable)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	snd_timer_params_t params = {0};
Packit 4a16fb
	unsigned int suspend, resume;
Packit 4a16fb
	int err;
Packit 4a16fb
	
Packit 4a16fb
	if (enable) {
Packit 4a16fb
		err = snd_timer_hw_open(&hw->period_timer,
Packit 4a16fb
				"hw-pcm-period-event",
Packit 4a16fb
				SND_TIMER_CLASS_PCM, SND_TIMER_SCLASS_NONE,
Packit 4a16fb
				hw->card, hw->device,
Packit 4a16fb
				(hw->subdevice << 1) | (pcm->stream & 1),
Packit 4a16fb
				SND_TIMER_OPEN_NONBLOCK | SND_TIMER_OPEN_TREAD);
Packit 4a16fb
		if (err < 0) {
Packit 4a16fb
			err = snd_timer_hw_open(&hw->period_timer,
Packit 4a16fb
				"hw-pcm-period-event",
Packit 4a16fb
				SND_TIMER_CLASS_PCM, SND_TIMER_SCLASS_NONE,
Packit 4a16fb
				hw->card, hw->device,
Packit 4a16fb
				(hw->subdevice << 1) | (pcm->stream & 1),
Packit 4a16fb
				SND_TIMER_OPEN_NONBLOCK);
Packit 4a16fb
			return err;
Packit 4a16fb
		}
Packit 4a16fb
		if (snd_timer_poll_descriptors_count(hw->period_timer) != 1) {
Packit 4a16fb
			snd_pcm_hw_close_timer(hw);
Packit 4a16fb
			return -EINVAL;
Packit 4a16fb
		}
Packit 4a16fb
		hw->period_timer_pfd.events = POLLIN;
Packit 4a16fb
 		hw->period_timer_pfd.revents = 0;
Packit 4a16fb
		snd_timer_poll_descriptors(hw->period_timer,
Packit 4a16fb
					   &hw->period_timer_pfd, 1);
Packit 4a16fb
		hw->period_timer_need_poll = 0;
Packit 4a16fb
		suspend = 1<
Packit 4a16fb
		resume = 1<
Packit 4a16fb
		/*
Packit 4a16fb
		 * hacks for older kernel drivers
Packit 4a16fb
		 */
Packit 4a16fb
		{
Packit 4a16fb
			int ver = 0;
Packit 4a16fb
			ioctl(hw->period_timer_pfd.fd,
Packit 4a16fb
			      SNDRV_TIMER_IOCTL_PVERSION, &ver);
Packit 4a16fb
			/*
Packit 4a16fb
			 * In older versions, check via poll before read() is
Packit 4a16fb
			 * needed because of the confliction between
Packit 4a16fb
			 * TIMER_START and FIONBIO ioctls.
Packit 4a16fb
                         */
Packit 4a16fb
			if (ver < SNDRV_PROTOCOL_VERSION(2, 0, 4))
Packit 4a16fb
				hw->period_timer_need_poll = 1;
Packit 4a16fb
			/*
Packit 4a16fb
			 * In older versions, timer uses pause events instead
Packit 4a16fb
			 * suspend/resume events.
Packit 4a16fb
			 */
Packit 4a16fb
			if (ver < SNDRV_PROTOCOL_VERSION(2, 0, 5)) {
Packit 4a16fb
				suspend = 1<
Packit 4a16fb
				resume = 1<
Packit 4a16fb
			}
Packit 4a16fb
		}
Packit 4a16fb
		snd_timer_params_set_auto_start(&params, 1);
Packit 4a16fb
		snd_timer_params_set_ticks(&params, 1);
Packit 4a16fb
		INTERNAL(snd_timer_params_set_filter)(&params, (1<
Packit 4a16fb
					    suspend | resume);
Packit 4a16fb
		err = snd_timer_params(hw->period_timer, &params);
Packit 4a16fb
		if (err < 0) {
Packit 4a16fb
			snd_pcm_hw_close_timer(hw);
Packit 4a16fb
			return err;
Packit 4a16fb
		}
Packit 4a16fb
		err = snd_timer_start(hw->period_timer);
Packit 4a16fb
		if (err < 0) {
Packit 4a16fb
			snd_pcm_hw_close_timer(hw);
Packit 4a16fb
			return err;
Packit 4a16fb
		}
Packit 4a16fb
		pcm->fast_ops = &snd_pcm_hw_fast_ops_timer;
Packit 4a16fb
	} else {
Packit 4a16fb
		snd_pcm_hw_close_timer(hw);
Packit 4a16fb
		pcm->fast_ops = &snd_pcm_hw_fast_ops;
Packit 4a16fb
		hw->period_event = 0;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_hw_free(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int fd = hw->fd, err;
Packit 4a16fb
	snd_pcm_hw_change_timer(pcm, 0);
Packit 4a16fb
	if (ioctl(fd, SNDRV_PCM_IOCTL_HW_FREE) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_HW_FREE failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int fd = hw->fd, err = 0;
Packit 4a16fb
	int old_period_event = sw_get_period_event(params);
Packit 4a16fb
	sw_set_period_event(params, 0);
Packit 4a16fb
	if ((snd_pcm_tstamp_t) params->tstamp_mode == pcm->tstamp_mode &&
Packit 4a16fb
	    (snd_pcm_tstamp_type_t) params->tstamp_type == pcm->tstamp_type &&
Packit 4a16fb
	    params->period_step == pcm->period_step &&
Packit 4a16fb
	    params->start_threshold == pcm->start_threshold &&
Packit 4a16fb
	    params->stop_threshold == pcm->stop_threshold &&
Packit 4a16fb
	    params->silence_threshold == pcm->silence_threshold &&
Packit 4a16fb
	    params->silence_size == pcm->silence_size &&
Packit 4a16fb
	    old_period_event == hw->period_event) {
Packit 4a16fb
		hw->mmap_control->avail_min = params->avail_min;
Packit 4a16fb
		err = issue_avail_min(hw);
Packit 4a16fb
		goto out;
Packit 4a16fb
	}
Packit 4a16fb
	if (params->tstamp_type == SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW &&
Packit 4a16fb
	    hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 12)) {
Packit 4a16fb
		SYSMSG("Kernel doesn't support SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW");
Packit 4a16fb
		err = -EINVAL;
Packit 4a16fb
		goto out;
Packit 4a16fb
	}
Packit 4a16fb
	if (params->tstamp_type == SND_PCM_TSTAMP_TYPE_MONOTONIC &&
Packit 4a16fb
	    hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 5)) {
Packit 4a16fb
		SYSMSG("Kernel doesn't support SND_PCM_TSTAMP_TYPE_MONOTONIC");
Packit 4a16fb
		err = -EINVAL;
Packit 4a16fb
		goto out;
Packit 4a16fb
	}
Packit 4a16fb
	if (ioctl(fd, SNDRV_PCM_IOCTL_SW_PARAMS, params) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err);
Packit 4a16fb
		goto out;
Packit 4a16fb
	}
Packit 4a16fb
	if ((snd_pcm_tstamp_type_t) params->tstamp_type != pcm->tstamp_type) {
Packit 4a16fb
		if (hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 12)) {
Packit 4a16fb
			int on = (snd_pcm_tstamp_type_t) params->tstamp_type ==
Packit 4a16fb
				SND_PCM_TSTAMP_TYPE_MONOTONIC;
Packit 4a16fb
			if (ioctl(fd, SNDRV_PCM_IOCTL_TSTAMP, &on) < 0) {
Packit 4a16fb
				err = -errno;
Packit 4a16fb
				SNDMSG("TSTAMP failed\n");
Packit 4a16fb
				goto out;
Packit 4a16fb
			}
Packit 4a16fb
		}
Packit 4a16fb
		pcm->tstamp_type = params->tstamp_type;
Packit 4a16fb
	}
Packit 4a16fb
	hw->mmap_control->avail_min = params->avail_min;
Packit 4a16fb
	if (hw->period_event != old_period_event) {
Packit 4a16fb
		err = snd_pcm_hw_change_timer(pcm, old_period_event);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			goto out;
Packit 4a16fb
		hw->period_event = old_period_event;
Packit 4a16fb
	}
Packit 4a16fb
 out:
Packit 4a16fb
	sw_set_period_event(params, old_period_event);
Packit 4a16fb
	return err;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	struct snd_pcm_channel_info i;
Packit 4a16fb
	int fd = hw->fd, err;
Packit 4a16fb
	i.channel = info->channel;
Packit 4a16fb
	if (ioctl(fd, SNDRV_PCM_IOCTL_CHANNEL_INFO, &i) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_CHANNEL_INFO failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	info->channel = i.channel;
Packit 4a16fb
	info->addr = 0;
Packit 4a16fb
	info->first = i.first;
Packit 4a16fb
	info->step = i.step;
Packit 4a16fb
	info->type = SND_PCM_AREA_MMAP;
Packit 4a16fb
	info->u.mmap.fd = fd;
Packit 4a16fb
	info->u.mmap.offset = i.offset;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int fd = hw->fd, err;
Packit 4a16fb
	if (SNDRV_PROTOCOL_VERSION(2, 0, 13) > hw->version) {
Packit 4a16fb
		if (ioctl(fd, SNDRV_PCM_IOCTL_STATUS, status) < 0) {
Packit 4a16fb
			err = -errno;
Packit 4a16fb
			SYSMSG("SNDRV_PCM_IOCTL_STATUS failed (%i)", err);
Packit 4a16fb
			return err;
Packit 4a16fb
		}
Packit 4a16fb
	} else {
Packit 4a16fb
		if (ioctl(fd, SNDRV_PCM_IOCTL_STATUS_EXT, status) < 0) {
Packit 4a16fb
			err = -errno;
Packit 4a16fb
			SYSMSG("SNDRV_PCM_IOCTL_STATUS_EXT failed (%i)", err);
Packit 4a16fb
			return err;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	if (SNDRV_PROTOCOL_VERSION(2, 0, 5) > hw->version) {
Packit 4a16fb
		status->tstamp.tv_nsec *= 1000L;
Packit 4a16fb
		status->trigger_tstamp.tv_nsec *= 1000L;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_state_t snd_pcm_hw_state(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int err = query_status_data(hw);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	return (snd_pcm_state_t) hw->mmap_status->state;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int fd = hw->fd, err;
Packit 4a16fb
	if (ioctl(fd, SNDRV_PCM_IOCTL_DELAY, delayp) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_DELAY failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_hwsync(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int fd = hw->fd, err;
Packit 4a16fb
	if (SNDRV_PROTOCOL_VERSION(2, 0, 3) <= hw->version) {
Packit 4a16fb
		if (hw->mmap_status_fallbacked) {
Packit 4a16fb
			err = request_hwsync(hw);
Packit 4a16fb
			if (err < 0)
Packit 4a16fb
				return err;
Packit 4a16fb
		} else {
Packit 4a16fb
			if (ioctl(fd, SNDRV_PCM_IOCTL_HWSYNC) < 0) {
Packit 4a16fb
				err = -errno;
Packit 4a16fb
				SYSMSG("SNDRV_PCM_IOCTL_HWSYNC failed (%i)", err);
Packit 4a16fb
				return err;
Packit 4a16fb
			}
Packit 4a16fb
		}
Packit 4a16fb
	} else {
Packit 4a16fb
		snd_pcm_sframes_t delay;
Packit 4a16fb
		int err = snd_pcm_hw_delay(pcm, &delay);
Packit 4a16fb
		if (err < 0) {
Packit 4a16fb
			switch (FAST_PCM_STATE(hw)) {
Packit 4a16fb
			case SND_PCM_STATE_PREPARED:
Packit 4a16fb
			case SND_PCM_STATE_SUSPENDED:
Packit 4a16fb
				return 0;
Packit 4a16fb
			default:
Packit 4a16fb
				return err;
Packit 4a16fb
			}
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_prepare(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int fd = hw->fd, err;
Packit 4a16fb
	if (ioctl(fd, SNDRV_PCM_IOCTL_PREPARE) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_PREPARE failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	return query_status_and_control_data(hw);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_reset(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int fd = hw->fd, err;
Packit 4a16fb
	if (ioctl(fd, SNDRV_PCM_IOCTL_RESET) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_RESET failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	return query_status_and_control_data(hw);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_start(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
#if 0
Packit 4a16fb
	assert(pcm->stream != SND_PCM_STREAM_PLAYBACK ||
Packit 4a16fb
	       snd_pcm_mmap_playback_hw_avail(pcm) > 0);
Packit 4a16fb
#endif
Packit 4a16fb
	issue_applptr(hw);
Packit 4a16fb
	if (ioctl(hw->fd, SNDRV_PCM_IOCTL_START) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_START failed (%i)", err);
Packit 4a16fb
#if 0
Packit 4a16fb
		if (err == -EBADFD)
Packit 4a16fb
			SNDERR("PCM state = %s", snd_pcm_state_name(snd_pcm_hw_state(pcm)));
Packit 4a16fb
#endif
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_drop(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
	if (ioctl(hw->fd, SNDRV_PCM_IOCTL_DROP) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_DROP failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	} else {
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_drain(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
	if (ioctl(hw->fd, SNDRV_PCM_IOCTL_DRAIN) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_DRAIN failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_pause(snd_pcm_t *pcm, int enable)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
	if (ioctl(hw->fd, SNDRV_PCM_IOCTL_PAUSE, enable) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_PAUSE failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t snd_pcm_hw_rewindable(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	return snd_pcm_mmap_hw_rewindable(pcm);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t snd_pcm_hw_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
	if (ioctl(hw->fd, SNDRV_PCM_IOCTL_REWIND, &frames) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_REWIND failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	err = query_status_and_control_data(hw);
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return err;
Packit 4a16fb
	return frames;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t snd_pcm_hw_forwardable(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	return snd_pcm_mmap_avail(pcm);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t snd_pcm_hw_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int err;
Packit 4a16fb
	if (SNDRV_PROTOCOL_VERSION(2, 0, 4) <= hw->version) {
Packit 4a16fb
		if (ioctl(hw->fd, SNDRV_PCM_IOCTL_FORWARD, &frames) < 0) {
Packit 4a16fb
			err = -errno;
Packit 4a16fb
			SYSMSG("SNDRV_PCM_IOCTL_FORWARD failed (%i)", err);
Packit 4a16fb
			return err;
Packit 4a16fb
		}
Packit 4a16fb
		err = query_status_and_control_data(hw);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
		return frames;
Packit 4a16fb
	} else {
Packit 4a16fb
		snd_pcm_sframes_t avail;
Packit 4a16fb
Packit 4a16fb
		switch (FAST_PCM_STATE(hw)) {
Packit 4a16fb
		case SNDRV_PCM_STATE_RUNNING:
Packit 4a16fb
		case SNDRV_PCM_STATE_DRAINING:
Packit 4a16fb
		case SNDRV_PCM_STATE_PAUSED:
Packit 4a16fb
		case SNDRV_PCM_STATE_PREPARED:
Packit 4a16fb
			break;
Packit 4a16fb
		case SNDRV_PCM_STATE_XRUN:
Packit 4a16fb
			return -EPIPE;
Packit 4a16fb
		default:
Packit 4a16fb
			return -EBADFD;
Packit 4a16fb
		}
Packit 4a16fb
		avail = snd_pcm_mmap_avail(pcm);
Packit 4a16fb
		if (avail < 0)
Packit 4a16fb
			return 0;
Packit 4a16fb
		if (frames > (snd_pcm_uframes_t)avail)
Packit 4a16fb
			frames = avail;
Packit 4a16fb
		snd_pcm_mmap_appl_forward(pcm, frames);
Packit 4a16fb
		return frames;
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_resume(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int fd = hw->fd, err;
Packit 4a16fb
	if (ioctl(fd, SNDRV_PCM_IOCTL_RESUME) < 0) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_RESUME failed (%i)", err);
Packit 4a16fb
		return err;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int hw_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw1 = pcm1->private_data;
Packit 4a16fb
	snd_pcm_hw_t *hw2 = pcm2->private_data;
Packit 4a16fb
	if (ioctl(hw1->fd, SNDRV_PCM_IOCTL_LINK, hw2->fd) < 0) {
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_LINK failed (%i)", -errno);
Packit 4a16fb
		return -errno;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_link_slaves(snd_pcm_t *pcm, snd_pcm_t *master)
Packit 4a16fb
{
Packit 4a16fb
	if (master->type != SND_PCM_TYPE_HW) {
Packit 4a16fb
		SYSMSG("Invalid type for SNDRV_PCM_IOCTL_LINK (%i)", master->type);
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	}
Packit 4a16fb
	return hw_link(master, pcm);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2)
Packit 4a16fb
{
Packit 4a16fb
	if (pcm2->type != SND_PCM_TYPE_HW) {
Packit 4a16fb
		if (pcm2->fast_ops->link_slaves)
Packit 4a16fb
			return pcm2->fast_ops->link_slaves(pcm2, pcm1);
Packit 4a16fb
		return -ENOSYS;
Packit 4a16fb
	}
Packit 4a16fb
	return hw_link(pcm1, pcm2);
Packit 4a16fb
 }
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_unlink(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
Packit 4a16fb
	if (ioctl(hw->fd, SNDRV_PCM_IOCTL_UNLINK) < 0) {
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_UNLINK failed (%i)", -errno);
Packit 4a16fb
		return -errno;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t snd_pcm_hw_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
Packit 4a16fb
{
Packit 4a16fb
	int err;
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int fd = hw->fd;
Packit 4a16fb
	struct snd_xferi xferi;
Packit 4a16fb
	xferi.buf = (char*) buffer;
Packit 4a16fb
	xferi.frames = size;
Packit 4a16fb
	xferi.result = 0; /* make valgrind happy */
Packit 4a16fb
	if (ioctl(fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &xferi) < 0)
Packit 4a16fb
		err = -errno;
Packit 4a16fb
	else
Packit 4a16fb
		err = query_status_and_control_data(hw);
Packit 4a16fb
#ifdef DEBUG_RW
Packit 4a16fb
	fprintf(stderr, "hw_writei: frames = %li, xferi.result = %li, err = %i\n", size, xferi.result, err);
Packit 4a16fb
#endif
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return snd_pcm_check_error(pcm, err);
Packit 4a16fb
	return xferi.result;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t snd_pcm_hw_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
Packit 4a16fb
{
Packit 4a16fb
	int err;
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int fd = hw->fd;
Packit 4a16fb
	struct snd_xfern xfern;
Packit 4a16fb
	memset(&xfern, 0, sizeof(xfern)); /* make valgrind happy */
Packit 4a16fb
	xfern.bufs = bufs;
Packit 4a16fb
	xfern.frames = size;
Packit 4a16fb
	if (ioctl(fd, SNDRV_PCM_IOCTL_WRITEN_FRAMES, &xfern) < 0)
Packit 4a16fb
		err = -errno;
Packit 4a16fb
	else
Packit 4a16fb
		err = query_status_and_control_data(hw);
Packit 4a16fb
#ifdef DEBUG_RW
Packit 4a16fb
	fprintf(stderr, "hw_writen: frames = %li, result = %li, err = %i\n", size, xfern.result, err);
Packit 4a16fb
#endif
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return snd_pcm_check_error(pcm, err);
Packit 4a16fb
	return xfern.result;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t snd_pcm_hw_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
Packit 4a16fb
{
Packit 4a16fb
	int err;
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int fd = hw->fd;
Packit 4a16fb
	struct snd_xferi xferi;
Packit 4a16fb
	xferi.buf = buffer;
Packit 4a16fb
	xferi.frames = size;
Packit 4a16fb
	xferi.result = 0; /* make valgrind happy */
Packit 4a16fb
	if (ioctl(fd, SNDRV_PCM_IOCTL_READI_FRAMES, &xferi) < 0)
Packit 4a16fb
		err = -errno;
Packit 4a16fb
	else
Packit 4a16fb
		err = query_status_and_control_data(hw);
Packit 4a16fb
#ifdef DEBUG_RW
Packit 4a16fb
	fprintf(stderr, "hw_readi: frames = %li, result = %li, err = %i\n", size, xferi.result, err);
Packit 4a16fb
#endif
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return snd_pcm_check_error(pcm, err);
Packit 4a16fb
	return xferi.result;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t snd_pcm_hw_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
Packit 4a16fb
{
Packit 4a16fb
	int err;
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int fd = hw->fd;
Packit 4a16fb
	struct snd_xfern xfern;
Packit 4a16fb
	memset(&xfern, 0, sizeof(xfern)); /* make valgrind happy */
Packit 4a16fb
	xfern.bufs = bufs;
Packit 4a16fb
	xfern.frames = size;
Packit 4a16fb
	if (ioctl(fd, SNDRV_PCM_IOCTL_READN_FRAMES, &xfern) < 0)
Packit 4a16fb
		err = -errno;
Packit 4a16fb
	else
Packit 4a16fb
		err = query_status_and_control_data(hw);
Packit 4a16fb
#ifdef DEBUG_RW
Packit 4a16fb
	fprintf(stderr, "hw_readn: frames = %li, result = %li, err = %i\n", size, xfern.result, err);
Packit 4a16fb
#endif
Packit 4a16fb
	if (err < 0)
Packit 4a16fb
		return snd_pcm_check_error(pcm, err);
Packit 4a16fb
	return xfern.result;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static bool map_status_data(snd_pcm_hw_t *hw, struct snd_pcm_sync_ptr *sync_ptr,
Packit 4a16fb
			    bool force_fallback)
Packit 4a16fb
{
Packit 4a16fb
	struct snd_pcm_mmap_status *mmap_status;
Packit 4a16fb
	bool fallbacked;
Packit 4a16fb
Packit 4a16fb
	mmap_status = MAP_FAILED;
Packit 4a16fb
	if (!force_fallback) {
Packit 4a16fb
		mmap_status = mmap(NULL, page_align(sizeof(*mmap_status)),
Packit 4a16fb
				   PROT_READ, MAP_FILE|MAP_SHARED,
Packit 4a16fb
				   hw->fd, SNDRV_PCM_MMAP_OFFSET_STATUS);
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	if (mmap_status == MAP_FAILED || mmap_status == NULL) {
Packit 4a16fb
		mmap_status = &sync_ptr->s.status;
Packit 4a16fb
		fallbacked = true;
Packit 4a16fb
	} else {
Packit 4a16fb
		fallbacked = false;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	hw->mmap_status = mmap_status;
Packit 4a16fb
Packit 4a16fb
	return fallbacked;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static bool map_control_data(snd_pcm_hw_t *hw,
Packit 4a16fb
			     struct snd_pcm_sync_ptr *sync_ptr,
Packit 4a16fb
			     bool force_fallback)
Packit 4a16fb
{
Packit 4a16fb
	struct snd_pcm_mmap_control *mmap_control;
Packit 4a16fb
	bool fallbacked;
Packit 4a16fb
Packit 4a16fb
	mmap_control = MAP_FAILED;
Packit 4a16fb
	if (!force_fallback) {
Packit 4a16fb
		mmap_control = mmap(NULL, page_align(sizeof(*mmap_control)),
Packit 4a16fb
				    PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED,
Packit 4a16fb
				    hw->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL);
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	if (mmap_control == MAP_FAILED || mmap_control == NULL) {
Packit 4a16fb
		mmap_control = &sync_ptr->c.control;
Packit 4a16fb
		fallbacked = true;
Packit 4a16fb
	} else {
Packit 4a16fb
		fallbacked = false;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	hw->mmap_control = mmap_control;
Packit 4a16fb
Packit 4a16fb
	return fallbacked;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int map_status_and_control_data(snd_pcm_t *pcm, bool force_fallback)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	struct snd_pcm_sync_ptr *sync_ptr;
Packit 4a16fb
	int err;
Packit 4a16fb
Packit 4a16fb
	/* Preparation for fallback to failure of mmap(2). */
Packit 4a16fb
	sync_ptr = malloc(sizeof(*sync_ptr));
Packit 4a16fb
	if (sync_ptr == NULL)
Packit 4a16fb
		return -ENOMEM;
Packit 4a16fb
	memset(sync_ptr, 0, sizeof(*sync_ptr));
Packit 4a16fb
Packit 4a16fb
	hw->mmap_status_fallbacked =
Packit 4a16fb
			map_status_data(hw, sync_ptr, force_fallback);
Packit 4a16fb
	hw->mmap_control_fallbacked =
Packit 4a16fb
			map_control_data(hw, sync_ptr, force_fallback);
Packit 4a16fb
Packit 4a16fb
	/* Any fallback mode needs to keep the buffer. */
Packit 4a16fb
	if (hw->mmap_status_fallbacked || hw->mmap_control_fallbacked) {
Packit 4a16fb
		hw->sync_ptr = sync_ptr;
Packit 4a16fb
	} else {
Packit 4a16fb
		free(sync_ptr);
Packit 4a16fb
		hw->sync_ptr = NULL;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	/* do not initialize in case of append and keep the values from the
Packit 4a16fb
	 * kernel
Packit 4a16fb
	 */
Packit 4a16fb
	if (!(pcm->mode & SND_PCM_APPEND)) {
Packit 4a16fb
		/* Initialize the data. */
Packit 4a16fb
		hw->mmap_control->appl_ptr = 0;
Packit 4a16fb
		hw->mmap_control->avail_min = 1;
Packit 4a16fb
	}
Packit 4a16fb
	snd_pcm_set_hw_ptr(pcm, &hw->mmap_status->hw_ptr, hw->fd,
Packit 4a16fb
			   SNDRV_PCM_MMAP_OFFSET_STATUS +
Packit 4a16fb
				offsetof(struct snd_pcm_mmap_status, hw_ptr));
Packit 4a16fb
	snd_pcm_set_appl_ptr(pcm, &hw->mmap_control->appl_ptr, hw->fd,
Packit 4a16fb
			     SNDRV_PCM_MMAP_OFFSET_CONTROL);
Packit 4a16fb
	if (hw->mmap_control_fallbacked) {
Packit 4a16fb
		unsigned int flags = 0;
Packit 4a16fb
		/* read appl_ptr and avail_min from kernel when device opened
Packit 4a16fb
		 * with SND_PCM_APPEND flag
Packit 4a16fb
		 */
Packit 4a16fb
		if (pcm->mode & SND_PCM_APPEND)
Packit 4a16fb
			flags = SNDRV_PCM_SYNC_PTR_APPL |
Packit 4a16fb
				SNDRV_PCM_SYNC_PTR_AVAIL_MIN;
Packit 4a16fb
		err = sync_ptr1(hw, flags);
Packit 4a16fb
		if (err < 0)
Packit 4a16fb
			return err;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void unmap_status_data(snd_pcm_hw_t *hw)
Packit 4a16fb
{
Packit 4a16fb
	if (!hw->mmap_status_fallbacked) {
Packit 4a16fb
		if (munmap((void *)hw->mmap_status,
Packit 4a16fb
			   page_align(sizeof(*hw->mmap_status))) < 0)
Packit 4a16fb
			SYSMSG("status munmap failed (%u)", errno);
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void unmap_control_data(snd_pcm_hw_t *hw)
Packit 4a16fb
{
Packit 4a16fb
	if (!hw->mmap_control_fallbacked) {
Packit 4a16fb
		if (munmap((void *)hw->mmap_control,
Packit 4a16fb
			   page_align(sizeof(*hw->mmap_control))) < 0)
Packit 4a16fb
			SYSMSG("control munmap failed (%u)", errno);
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void unmap_status_and_control_data(snd_pcm_hw_t *hw)
Packit 4a16fb
{
Packit 4a16fb
	unmap_status_data(hw);
Packit 4a16fb
	unmap_control_data(hw);
Packit 4a16fb
Packit 4a16fb
	if (hw->mmap_status_fallbacked || hw->mmap_control_fallbacked)
Packit 4a16fb
		free(hw->sync_ptr);
Packit 4a16fb
Packit 4a16fb
	hw->mmap_status = NULL;
Packit 4a16fb
	hw->mmap_control = NULL;
Packit 4a16fb
	hw->mmap_status_fallbacked = false;
Packit 4a16fb
	hw->mmap_control_fallbacked = false;
Packit 4a16fb
	hw->sync_ptr = NULL;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
Packit 4a16fb
{
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
Packit 4a16fb
{
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_close(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	int err = 0;
Packit 4a16fb
	if (close(hw->fd)) {
Packit 4a16fb
		err = -errno;
Packit 4a16fb
		SYSMSG("close failed (%i)\n", err);
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	unmap_status_and_control_data(hw);
Packit 4a16fb
Packit 4a16fb
	free(hw);
Packit 4a16fb
	return err;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t snd_pcm_hw_mmap_commit(snd_pcm_t *pcm,
Packit 4a16fb
						snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
Packit 4a16fb
						snd_pcm_uframes_t size)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
Packit 4a16fb
	snd_pcm_mmap_appl_forward(pcm, size);
Packit 4a16fb
	issue_applptr(hw);
Packit 4a16fb
#ifdef DEBUG_MMAP
Packit 4a16fb
	fprintf(stderr, "appl_forward: hw_ptr = %li, appl_ptr = %li, size = %li\n", *pcm->hw.ptr, *pcm->appl.ptr, size);
Packit 4a16fb
#endif
Packit 4a16fb
	return size;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_sframes_t snd_pcm_hw_avail_update(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	snd_pcm_uframes_t avail;
Packit 4a16fb
Packit 4a16fb
	query_status_data(hw);
Packit 4a16fb
	avail = snd_pcm_mmap_avail(pcm);
Packit 4a16fb
	switch (FAST_PCM_STATE(hw)) {
Packit 4a16fb
	case SNDRV_PCM_STATE_RUNNING:
Packit 4a16fb
		if (avail >= pcm->stop_threshold) {
Packit 4a16fb
			/* SNDRV_PCM_IOCTL_XRUN ioctl has been implemented since PCM kernel API 2.0.1 */
Packit 4a16fb
			if (SNDRV_PROTOCOL_VERSION(2, 0, 1) <= hw->version) {
Packit 4a16fb
				if (ioctl(hw->fd, SNDRV_PCM_IOCTL_XRUN) < 0)
Packit 4a16fb
					return -errno;
Packit 4a16fb
			}
Packit 4a16fb
			/* everything is ok, state == SND_PCM_STATE_XRUN at the moment */
Packit 4a16fb
			return -EPIPE;
Packit 4a16fb
		}
Packit 4a16fb
		break;
Packit 4a16fb
	case SNDRV_PCM_STATE_XRUN:
Packit 4a16fb
		return -EPIPE;
Packit 4a16fb
	default:
Packit 4a16fb
		break;
Packit 4a16fb
	}
Packit 4a16fb
	return avail;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_htimestamp(snd_pcm_t *pcm, snd_pcm_uframes_t *avail,
Packit 4a16fb
				 snd_htimestamp_t *tstamp)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_sframes_t avail1;
Packit 4a16fb
	int ok = 0;
Packit 4a16fb
Packit 4a16fb
	/* unfortunately, loop is necessary to ensure valid timestamp */
Packit 4a16fb
	while (1) {
Packit 4a16fb
		avail1 = snd_pcm_hw_avail_update(pcm);
Packit 4a16fb
		if (avail1 < 0)
Packit 4a16fb
			return avail1;
Packit 4a16fb
		if (ok && (snd_pcm_uframes_t)avail1 == *avail)
Packit 4a16fb
			break;
Packit 4a16fb
		*avail = avail1;
Packit 4a16fb
		*tstamp = snd_pcm_hw_fast_tstamp(pcm);
Packit 4a16fb
		ok = 1;
Packit 4a16fb
	}
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void __fill_chmap_ctl_id(snd_ctl_elem_id_t *id, int dev, int subdev,
Packit 4a16fb
				int stream)
Packit 4a16fb
{
Packit 4a16fb
	snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_PCM);
Packit 4a16fb
	if (stream == SND_PCM_STREAM_PLAYBACK)
Packit 4a16fb
		snd_ctl_elem_id_set_name(id, "Playback Channel Map");
Packit 4a16fb
	else
Packit 4a16fb
		snd_ctl_elem_id_set_name(id, "Capture Channel Map");
Packit 4a16fb
	snd_ctl_elem_id_set_device(id, dev);
Packit 4a16fb
	snd_ctl_elem_id_set_index(id, subdev);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void fill_chmap_ctl_id(snd_pcm_t *pcm, snd_ctl_elem_id_t *id)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	__fill_chmap_ctl_id(id, hw->device, hw->subdevice, pcm->stream);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int is_chmap_type(int type)
Packit 4a16fb
{
Packit 4a16fb
	return (type >= SND_CTL_TLVT_CHMAP_FIXED &&
Packit 4a16fb
		type <= SND_CTL_TLVT_CHMAP_PAIRED);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/**
Packit 4a16fb
 * \!brief Query the available channel maps
Packit 4a16fb
 * \param card the card number
Packit 4a16fb
 * \param dev the PCM device number
Packit 4a16fb
 * \param subdev the PCM substream index
Packit 4a16fb
 * \param stream the direction of PCM stream
Packit 4a16fb
 * \return the NULL-terminated array of integer pointers, or NULL at error.
Packit 4a16fb
 *
Packit 4a16fb
 * This function works like snd_pcm_query_chmaps() but it takes the card,
Packit 4a16fb
 * device, substream and stream numbers instead of the already opened
Packit 4a16fb
 * snd_pcm_t instance, so that you can query available channel maps of
Packit 4a16fb
 * a PCM before actually opening it.
Packit 4a16fb
 *
Packit 4a16fb
 * As the parameters stand, the query is performed only to the hw PCM
Packit 4a16fb
 * devices, not the abstracted PCM object in alsa-lib.
Packit 4a16fb
 */
Packit 4a16fb
snd_pcm_chmap_query_t **
Packit 4a16fb
snd_pcm_query_chmaps_from_hw(int card, int dev, int subdev,
Packit 4a16fb
			     snd_pcm_stream_t stream)
Packit 4a16fb
{
Packit 4a16fb
	snd_ctl_t *ctl;
Packit 4a16fb
	snd_ctl_elem_id_t id = {0};
Packit 4a16fb
	unsigned int tlv[2048], *start;
Packit 4a16fb
	unsigned int type;
Packit 4a16fb
	snd_pcm_chmap_query_t **map;
Packit 4a16fb
	int i, ret, nums;
Packit 4a16fb
Packit 4a16fb
	ret = snd_ctl_hw_open(&ctl, NULL, card, 0);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		SYSMSG("Cannot open the associated CTL\n");
Packit 4a16fb
		return NULL;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	__fill_chmap_ctl_id(&id, dev, subdev, stream);
Packit 4a16fb
	ret = snd_ctl_elem_tlv_read(ctl, &id, tlv, sizeof(tlv));
Packit 4a16fb
	snd_ctl_close(ctl);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		SYSMSG("Cannot read Channel Map TLV\n");
Packit 4a16fb
		return NULL;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
#if 0
Packit 4a16fb
	for (i = 0; i < 32; i++)
Packit 4a16fb
		fprintf(stderr, "%02x: %08x\n", i, tlv[i]);
Packit 4a16fb
#endif
Packit 4a16fb
	/* FIXME: the parser below assumes that the TLV only contains
Packit 4a16fb
	 * chmap-related blocks
Packit 4a16fb
	 */
Packit 4a16fb
	type = tlv[SNDRV_CTL_TLVO_TYPE];
Packit 4a16fb
	if (type != SND_CTL_TLVT_CONTAINER) {
Packit 4a16fb
		if (!is_chmap_type(type)) {
Packit 4a16fb
			SYSMSG("Invalid TLV type %d\n", type);
Packit 4a16fb
			return NULL;
Packit 4a16fb
		}
Packit 4a16fb
		start = tlv;
Packit 4a16fb
		nums = 1;
Packit 4a16fb
	} else {
Packit 4a16fb
		unsigned int *p;
Packit 4a16fb
		int size;
Packit 4a16fb
		start = tlv + 2;
Packit 4a16fb
		size = tlv[SNDRV_CTL_TLVO_LEN];
Packit 4a16fb
		nums = 0;
Packit 4a16fb
		for (p = start; size > 0; ) {
Packit 4a16fb
			if (!is_chmap_type(p[0])) {
Packit 4a16fb
				SYSMSG("Invalid TLV type %d\n", p[0]);
Packit 4a16fb
				return NULL;
Packit 4a16fb
			}
Packit 4a16fb
			nums++;
Packit 4a16fb
			size -= p[1] + 8;
Packit 4a16fb
			p += p[1] / 4 + 2;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	map = calloc(nums + 1, sizeof(int *));
Packit 4a16fb
	if (!map)
Packit 4a16fb
		return NULL;
Packit 4a16fb
	for (i = 0; i < nums; i++) {
Packit 4a16fb
		map[i] = malloc(start[1] + 8);
Packit 4a16fb
		if (!map[i]) {
Packit 4a16fb
			snd_pcm_free_chmaps(map);
Packit 4a16fb
			return NULL;
Packit 4a16fb
		}
Packit 4a16fb
		map[i]->type = start[0] - 0x100;
Packit 4a16fb
		map[i]->map.channels = start[1] / 4;
Packit 4a16fb
		memcpy(map[i]->map.pos, start + 2, start[1]);
Packit 4a16fb
		start += start[1] / 4 + 2;
Packit 4a16fb
	}
Packit 4a16fb
	return map;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
enum { CHMAP_CTL_QUERY, CHMAP_CTL_GET, CHMAP_CTL_SET };
Packit 4a16fb
Packit 4a16fb
static int chmap_caps(snd_pcm_hw_t *hw, int type)
Packit 4a16fb
{
Packit 4a16fb
	if (hw->chmap_caps & (1 << type))
Packit 4a16fb
		return 1;
Packit 4a16fb
	if (hw->chmap_caps & (1 << (type + 8)))
Packit 4a16fb
		return 0;
Packit 4a16fb
	return 1;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void chmap_caps_set_ok(snd_pcm_hw_t *hw, int type)
Packit 4a16fb
{
Packit 4a16fb
	hw->chmap_caps |= (1 << type);
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void chmap_caps_set_error(snd_pcm_hw_t *hw, int type)
Packit 4a16fb
{
Packit 4a16fb
	hw->chmap_caps |= (1 << (type + 8));
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_chmap_query_t **snd_pcm_hw_query_chmaps(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	snd_pcm_chmap_query_t **map;
Packit 4a16fb
Packit 4a16fb
	if (hw->chmap_override)
Packit 4a16fb
		return _snd_pcm_copy_chmap_query(hw->chmap_override);
Packit 4a16fb
Packit 4a16fb
	if (!chmap_caps(hw, CHMAP_CTL_QUERY))
Packit 4a16fb
		return NULL;
Packit 4a16fb
Packit 4a16fb
	map = snd_pcm_query_chmaps_from_hw(hw->card, hw->device,
Packit 4a16fb
					   hw->subdevice, pcm->stream);
Packit 4a16fb
	if (map)
Packit 4a16fb
		chmap_caps_set_ok(hw, CHMAP_CTL_QUERY);
Packit 4a16fb
	else
Packit 4a16fb
		chmap_caps_set_error(hw, CHMAP_CTL_QUERY);
Packit 4a16fb
	return map;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static snd_pcm_chmap_t *snd_pcm_hw_get_chmap(snd_pcm_t *pcm)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	snd_pcm_chmap_t *map;
Packit 4a16fb
	snd_ctl_t *ctl;
Packit 4a16fb
	snd_ctl_elem_id_t id = {0};
Packit 4a16fb
	snd_ctl_elem_value_t val = {0};
Packit 4a16fb
	unsigned int i;
Packit 4a16fb
	int ret;
Packit 4a16fb
Packit 4a16fb
	if (hw->chmap_override)
Packit 4a16fb
		return _snd_pcm_choose_fixed_chmap(pcm, hw->chmap_override);
Packit 4a16fb
Packit 4a16fb
	if (!chmap_caps(hw, CHMAP_CTL_GET))
Packit 4a16fb
		return NULL;
Packit 4a16fb
Packit 4a16fb
	switch (FAST_PCM_STATE(hw)) {
Packit 4a16fb
	case SNDRV_PCM_STATE_PREPARED:
Packit 4a16fb
	case SNDRV_PCM_STATE_RUNNING:
Packit 4a16fb
	case SNDRV_PCM_STATE_XRUN:
Packit 4a16fb
	case SNDRV_PCM_STATE_DRAINING:
Packit 4a16fb
	case SNDRV_PCM_STATE_PAUSED:
Packit 4a16fb
	case SNDRV_PCM_STATE_SUSPENDED:
Packit 4a16fb
		break;
Packit 4a16fb
	default:
Packit 4a16fb
		SYSMSG("Invalid PCM state for chmap_get: %s\n",
Packit 4a16fb
		       snd_pcm_state_name(FAST_PCM_STATE(hw)));
Packit 4a16fb
		return NULL;
Packit 4a16fb
	}
Packit 4a16fb
	map = malloc(pcm->channels * sizeof(map->pos[0]) + sizeof(*map));
Packit 4a16fb
	if (!map)
Packit 4a16fb
		return NULL;
Packit 4a16fb
	map->channels = pcm->channels;
Packit 4a16fb
	ret = snd_ctl_hw_open(&ctl, NULL, hw->card, 0);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		free(map);
Packit 4a16fb
		SYSMSG("Cannot open the associated CTL\n");
Packit 4a16fb
		chmap_caps_set_error(hw, CHMAP_CTL_GET);
Packit 4a16fb
		return NULL;
Packit 4a16fb
	}
Packit 4a16fb
	fill_chmap_ctl_id(pcm, &id;;
Packit 4a16fb
	snd_ctl_elem_value_set_id(&val, &id;;
Packit 4a16fb
	ret = snd_ctl_elem_read(ctl, &val;;
Packit 4a16fb
	snd_ctl_close(ctl);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		free(map);
Packit 4a16fb
		SYSMSG("Cannot read Channel Map ctl\n");
Packit 4a16fb
		chmap_caps_set_error(hw, CHMAP_CTL_GET);
Packit 4a16fb
		return NULL;
Packit 4a16fb
	}
Packit 4a16fb
	for (i = 0; i < pcm->channels; i++)
Packit 4a16fb
		map->pos[i] = snd_ctl_elem_value_get_integer(&val, i);
Packit 4a16fb
	chmap_caps_set_ok(hw, CHMAP_CTL_GET);
Packit 4a16fb
	return map;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static int snd_pcm_hw_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	snd_ctl_t *ctl;
Packit 4a16fb
	snd_ctl_elem_id_t id = {0};
Packit 4a16fb
	snd_ctl_elem_value_t val = {0};
Packit 4a16fb
	unsigned int i;
Packit 4a16fb
	int ret;
Packit 4a16fb
Packit 4a16fb
	if (hw->chmap_override)
Packit 4a16fb
		return -ENXIO;
Packit 4a16fb
Packit 4a16fb
	if (!chmap_caps(hw, CHMAP_CTL_SET))
Packit 4a16fb
		return -ENXIO;
Packit 4a16fb
Packit 4a16fb
	if (map->channels > 128) {
Packit 4a16fb
		SYSMSG("Invalid number of channels %d\n", map->channels);
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	}
Packit 4a16fb
	if (FAST_PCM_STATE(hw) != SNDRV_PCM_STATE_PREPARED) {
Packit 4a16fb
		SYSMSG("Invalid PCM state for chmap_set: %s\n",
Packit 4a16fb
		       snd_pcm_state_name(FAST_PCM_STATE(hw)));
Packit 4a16fb
		return -EBADFD;
Packit 4a16fb
	}
Packit 4a16fb
	ret = snd_ctl_hw_open(&ctl, NULL, hw->card, 0);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		SYSMSG("Cannot open the associated CTL\n");
Packit 4a16fb
		chmap_caps_set_error(hw, CHMAP_CTL_SET);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	fill_chmap_ctl_id(pcm, &id;;
Packit 4a16fb
	snd_ctl_elem_value_set_id(&val, &id;;
Packit 4a16fb
	for (i = 0; i < map->channels; i++)
Packit 4a16fb
		snd_ctl_elem_value_set_integer(&val, i, map->pos[i]);
Packit 4a16fb
	ret = snd_ctl_elem_write(ctl, &val;;
Packit 4a16fb
	snd_ctl_close(ctl);
Packit 4a16fb
	if (ret >= 0)
Packit 4a16fb
		chmap_caps_set_ok(hw, CHMAP_CTL_SET);
Packit 4a16fb
	else if (ret == -ENOENT || ret == -EPERM || ret == -ENXIO) {
Packit 4a16fb
		chmap_caps_set_error(hw, CHMAP_CTL_SET);
Packit 4a16fb
		ret = -ENXIO;
Packit 4a16fb
	}
Packit 4a16fb
	if (ret < 0)
Packit 4a16fb
		SYSMSG("Cannot write Channel Map ctl\n");
Packit 4a16fb
	return ret;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static void snd_pcm_hw_dump(snd_pcm_t *pcm, snd_output_t *out)
Packit 4a16fb
{
Packit 4a16fb
	snd_pcm_hw_t *hw = pcm->private_data;
Packit 4a16fb
	char *name;
Packit 4a16fb
	int err = snd_card_get_name(hw->card, &name);
Packit 4a16fb
	if (err < 0) {
Packit 4a16fb
		SNDERR("cannot get card name");
Packit 4a16fb
		return;
Packit 4a16fb
	}
Packit 4a16fb
	snd_output_printf(out, "Hardware PCM card %d '%s' device %d subdevice %d\n",
Packit 4a16fb
			  hw->card, name, hw->device, hw->subdevice);
Packit 4a16fb
	free(name);
Packit 4a16fb
	if (pcm->setup) {
Packit 4a16fb
		snd_output_printf(out, "Its setup is:\n");
Packit 4a16fb
		snd_pcm_dump_setup(pcm, out);
Packit 4a16fb
		snd_output_printf(out, "  appl_ptr     : %li\n", hw->mmap_control->appl_ptr);
Packit 4a16fb
		snd_output_printf(out, "  hw_ptr       : %li\n", hw->mmap_status->hw_ptr);
Packit 4a16fb
	}
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
static const snd_pcm_ops_t snd_pcm_hw_ops = {
Packit 4a16fb
	.close = snd_pcm_hw_close,
Packit 4a16fb
	.info = snd_pcm_hw_info,
Packit 4a16fb
	.hw_refine = snd_pcm_hw_hw_refine,
Packit 4a16fb
	.hw_params = snd_pcm_hw_hw_params,
Packit 4a16fb
	.hw_free = snd_pcm_hw_hw_free,
Packit 4a16fb
	.sw_params = snd_pcm_hw_sw_params,
Packit 4a16fb
	.channel_info = snd_pcm_hw_channel_info,
Packit 4a16fb
	.dump = snd_pcm_hw_dump,
Packit 4a16fb
	.nonblock = snd_pcm_hw_nonblock,
Packit 4a16fb
	.async = snd_pcm_hw_async,
Packit 4a16fb
	.mmap = snd_pcm_hw_mmap,
Packit 4a16fb
	.munmap = snd_pcm_hw_munmap,
Packit 4a16fb
	.query_chmaps = snd_pcm_hw_query_chmaps,
Packit 4a16fb
	.get_chmap = snd_pcm_hw_get_chmap,
Packit 4a16fb
	.set_chmap = snd_pcm_hw_set_chmap,
Packit 4a16fb
};
Packit 4a16fb
Packit 4a16fb
static const snd_pcm_fast_ops_t snd_pcm_hw_fast_ops = {
Packit 4a16fb
	.status = snd_pcm_hw_status,
Packit 4a16fb
	.state = snd_pcm_hw_state,
Packit 4a16fb
	.hwsync = snd_pcm_hw_hwsync,
Packit 4a16fb
	.delay = snd_pcm_hw_delay,
Packit 4a16fb
	.prepare = snd_pcm_hw_prepare,
Packit 4a16fb
	.reset = snd_pcm_hw_reset,
Packit 4a16fb
	.start = snd_pcm_hw_start,
Packit 4a16fb
	.drop = snd_pcm_hw_drop,
Packit 4a16fb
	.drain = snd_pcm_hw_drain,
Packit 4a16fb
	.pause = snd_pcm_hw_pause,
Packit 4a16fb
	.rewindable = snd_pcm_hw_rewindable,
Packit 4a16fb
	.rewind = snd_pcm_hw_rewind,
Packit 4a16fb
	.forwardable = snd_pcm_hw_forwardable,
Packit 4a16fb
	.forward = snd_pcm_hw_forward,
Packit 4a16fb
	.resume = snd_pcm_hw_resume,
Packit 4a16fb
	.link = snd_pcm_hw_link,
Packit 4a16fb
	.link_slaves = snd_pcm_hw_link_slaves,
Packit 4a16fb
	.unlink = snd_pcm_hw_unlink,
Packit 4a16fb
	.writei = snd_pcm_hw_writei,
Packit 4a16fb
	.writen = snd_pcm_hw_writen,
Packit 4a16fb
	.readi = snd_pcm_hw_readi,
Packit 4a16fb
	.readn = snd_pcm_hw_readn,
Packit 4a16fb
	.avail_update = snd_pcm_hw_avail_update,
Packit 4a16fb
	.mmap_commit = snd_pcm_hw_mmap_commit,
Packit 4a16fb
	.htimestamp = snd_pcm_hw_htimestamp,
Packit 4a16fb
	.poll_descriptors = NULL,
Packit 4a16fb
	.poll_descriptors_count = NULL,
Packit 4a16fb
	.poll_revents = NULL,
Packit 4a16fb
};
Packit 4a16fb
Packit 4a16fb
static const snd_pcm_fast_ops_t snd_pcm_hw_fast_ops_timer = {
Packit 4a16fb
	.status = snd_pcm_hw_status,
Packit 4a16fb
	.state = snd_pcm_hw_state,
Packit 4a16fb
	.hwsync = snd_pcm_hw_hwsync,
Packit 4a16fb
	.delay = snd_pcm_hw_delay,
Packit 4a16fb
	.prepare = snd_pcm_hw_prepare,
Packit 4a16fb
	.reset = snd_pcm_hw_reset,
Packit 4a16fb
	.start = snd_pcm_hw_start,
Packit 4a16fb
	.drop = snd_pcm_hw_drop,
Packit 4a16fb
	.drain = snd_pcm_hw_drain,
Packit 4a16fb
	.pause = snd_pcm_hw_pause,
Packit 4a16fb
	.rewindable = snd_pcm_hw_rewindable,
Packit 4a16fb
	.rewind = snd_pcm_hw_rewind,
Packit 4a16fb
	.forwardable = snd_pcm_hw_forwardable,
Packit 4a16fb
	.forward = snd_pcm_hw_forward,
Packit 4a16fb
	.resume = snd_pcm_hw_resume,
Packit 4a16fb
	.link = snd_pcm_hw_link,
Packit 4a16fb
	.link_slaves = snd_pcm_hw_link_slaves,
Packit 4a16fb
	.unlink = snd_pcm_hw_unlink,
Packit 4a16fb
	.writei = snd_pcm_hw_writei,
Packit 4a16fb
	.writen = snd_pcm_hw_writen,
Packit 4a16fb
	.readi = snd_pcm_hw_readi,
Packit 4a16fb
	.readn = snd_pcm_hw_readn,
Packit 4a16fb
	.avail_update = snd_pcm_hw_avail_update,
Packit 4a16fb
	.mmap_commit = snd_pcm_hw_mmap_commit,
Packit 4a16fb
	.htimestamp = snd_pcm_hw_htimestamp,
Packit 4a16fb
	.poll_descriptors = snd_pcm_hw_poll_descriptors,
Packit 4a16fb
	.poll_descriptors_count = snd_pcm_hw_poll_descriptors_count,
Packit 4a16fb
	.poll_revents = snd_pcm_hw_poll_revents,
Packit 4a16fb
};
Packit 4a16fb
Packit 4a16fb
/**
Packit 4a16fb
 * \brief Creates a new hw PCM
Packit 4a16fb
 * \param pcmp Returns created PCM handle
Packit 4a16fb
 * \param name Name of PCM
Packit 4a16fb
 * \param fd File descriptor
Packit 4a16fb
 * \param sync_ptr_ioctl Boolean flag for sync_ptr ioctl
Packit 4a16fb
 * \retval zero on success otherwise a negative error code
Packit 4a16fb
 * \warning Using of this function might be dangerous in the sense
Packit 4a16fb
 *          of compatibility reasons. The prototype might be freely
Packit 4a16fb
 *          changed in future.
Packit 4a16fb
 */
Packit 4a16fb
int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd,
Packit 4a16fb
		       int sync_ptr_ioctl)
Packit 4a16fb
{
Packit 4a16fb
	int ver, mode;
Packit 4a16fb
	snd_pcm_tstamp_type_t tstamp_type = SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY;
Packit 4a16fb
	long fmode;
Packit 4a16fb
	snd_pcm_t *pcm = NULL;
Packit 4a16fb
	snd_pcm_hw_t *hw = NULL;
Packit 4a16fb
	snd_pcm_info_t info;
Packit 4a16fb
	int ret;
Packit 4a16fb
Packit 4a16fb
	assert(pcmp);
Packit 4a16fb
Packit 4a16fb
	memset(&info, 0, sizeof(info));
Packit 4a16fb
	if (ioctl(fd, SNDRV_PCM_IOCTL_INFO, &info) < 0) {
Packit 4a16fb
		ret = -errno;
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_INFO failed (%i)", ret);
Packit 4a16fb
		close(fd);
Packit 4a16fb
		return ret;
Packit 4a16fb
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	if ((fmode = fcntl(fd, F_GETFL)) < 0) {
Packit 4a16fb
		ret = -errno;
Packit 4a16fb
		close(fd);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
	mode = 0;
Packit 4a16fb
	if (fmode & O_NONBLOCK)
Packit 4a16fb
		mode |= SND_PCM_NONBLOCK;
Packit 4a16fb
	if (fmode & O_ASYNC)
Packit 4a16fb
		mode |= SND_PCM_ASYNC;
Packit 4a16fb
	if (fmode & O_APPEND)
Packit 4a16fb
		mode |= SND_PCM_APPEND;
Packit 4a16fb
Packit 4a16fb
	if (ioctl(fd, SNDRV_PCM_IOCTL_PVERSION, &ver) < 0) {
Packit 4a16fb
		ret = -errno;
Packit 4a16fb
		SYSMSG("SNDRV_PCM_IOCTL_PVERSION failed (%i)", ret);
Packit 4a16fb
		close(fd);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
	if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_PCM_VERSION_MAX))
Packit 4a16fb
		return -SND_ERROR_INCOMPATIBLE_VERSION;
Packit 4a16fb
Packit 4a16fb
	if (SNDRV_PROTOCOL_VERSION(2, 0, 14) <= ver) {
Packit 4a16fb
		/* inform the protocol version we're supporting */
Packit 4a16fb
		unsigned int user_ver = SNDRV_PCM_VERSION;
Packit 4a16fb
		if (ioctl(fd, SNDRV_PCM_IOCTL_USER_PVERSION, &user_ver) < 0) {
Packit 4a16fb
			ret = -errno;
Packit 4a16fb
			SNDMSG("USER_PVERSION failed\n");
Packit 4a16fb
			return ret;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
Packit 4a16fb
	if (SNDRV_PROTOCOL_VERSION(2, 0, 9) <= ver) {
Packit 4a16fb
		struct timespec timespec;
Packit 4a16fb
		if (clock_gettime(CLOCK_MONOTONIC, &timespec) == 0) {
Packit 4a16fb
			int on = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
Packit 4a16fb
			if (ioctl(fd, SNDRV_PCM_IOCTL_TTSTAMP, &on) < 0) {
Packit 4a16fb
				ret = -errno;
Packit 4a16fb
				SNDMSG("TTSTAMP failed\n");
Packit 4a16fb
				return ret;
Packit 4a16fb
			}
Packit 4a16fb
			tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC;
Packit 4a16fb
		}
Packit 4a16fb
	} else
Packit 4a16fb
#endif
Packit 4a16fb
	  if (SNDRV_PROTOCOL_VERSION(2, 0, 5) <= ver) {
Packit 4a16fb
		int on = 1;
Packit 4a16fb
		if (ioctl(fd, SNDRV_PCM_IOCTL_TSTAMP, &on) < 0) {
Packit 4a16fb
			ret = -errno;
Packit 4a16fb
			SNDMSG("TSTAMP failed\n");
Packit 4a16fb
			return ret;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	
Packit 4a16fb
	hw = calloc(1, sizeof(snd_pcm_hw_t));
Packit 4a16fb
	if (!hw) {
Packit 4a16fb
		close(fd);
Packit 4a16fb
		return -ENOMEM;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	hw->version = ver;
Packit 4a16fb
	hw->card = info.card;
Packit 4a16fb
	hw->device = info.device;
Packit 4a16fb
	hw->subdevice = info.subdevice;
Packit 4a16fb
	hw->fd = fd;
Packit 4a16fb
	/* no restriction */
Packit 4a16fb
	hw->format = SND_PCM_FORMAT_UNKNOWN;
Packit 4a16fb
	hw->rate = 0;
Packit 4a16fb
	hw->channels = 0;
Packit 4a16fb
Packit 4a16fb
	ret = snd_pcm_new(&pcm, SND_PCM_TYPE_HW, name, info.stream, mode);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		free(hw);
Packit 4a16fb
		close(fd);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	pcm->ops = &snd_pcm_hw_ops;
Packit 4a16fb
	pcm->fast_ops = &snd_pcm_hw_fast_ops;
Packit 4a16fb
	pcm->private_data = hw;
Packit 4a16fb
	pcm->poll_fd = fd;
Packit 4a16fb
	pcm->poll_events = info.stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
Packit 4a16fb
	pcm->tstamp_type = tstamp_type;
Packit 4a16fb
#ifdef THREAD_SAFE_API
Packit 4a16fb
	pcm->need_lock = 0;	/* hw plugin is thread-safe */
Packit 4a16fb
#endif
Packit 4a16fb
	pcm->own_state_check = 1; /* skip the common state check */
Packit 4a16fb
Packit 4a16fb
	ret = map_status_and_control_data(pcm, !!sync_ptr_ioctl);
Packit 4a16fb
	if (ret < 0) {
Packit 4a16fb
		snd_pcm_close(pcm);
Packit 4a16fb
		return ret;
Packit 4a16fb
	}
Packit 4a16fb
Packit 4a16fb
	*pcmp = pcm;
Packit 4a16fb
	return 0;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/**
Packit 4a16fb
 * \brief Creates a new hw PCM
Packit 4a16fb
 * \param pcmp Returns created PCM handle
Packit 4a16fb
 * \param name Name of PCM
Packit 4a16fb
 * \param card Number of card
Packit 4a16fb
 * \param device Number of device
Packit 4a16fb
 * \param subdevice Number of subdevice
Packit 4a16fb
 * \param stream PCM Stream
Packit 4a16fb
 * \param mode PCM Mode
Packit 4a16fb
 * \param mmap_emulation Obsoleted parameter
Packit 4a16fb
 * \param sync_ptr_ioctl Use SYNC_PTR ioctl rather than mmap for control structures
Packit 4a16fb
 * \retval zero on success otherwise a negative error code
Packit 4a16fb
 * \warning Using of this function might be dangerous in the sense
Packit 4a16fb
 *          of compatibility reasons. The prototype might be freely
Packit 4a16fb
 *          changed in future.
Packit 4a16fb
 */
Packit 4a16fb
int snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
Packit 4a16fb
		    int card, int device, int subdevice,
Packit 4a16fb
		    snd_pcm_stream_t stream, int mode,
Packit 4a16fb
		    int mmap_emulation ATTRIBUTE_UNUSED,
Packit 4a16fb
		    int sync_ptr_ioctl)
Packit 4a16fb
{
Packit 4a16fb
	char filename[sizeof(SNDRV_FILE_PCM_STREAM_PLAYBACK) + 20];
Packit 4a16fb
	const char *filefmt;
Packit 4a16fb
	int ret = 0, fd = -1;
Packit 4a16fb
	int attempt = 0;
Packit 4a16fb
	snd_pcm_info_t info;
Packit 4a16fb
	int fmode;
Packit 4a16fb
	snd_ctl_t *ctl;
Packit 4a16fb
Packit 4a16fb
	assert(pcmp);
Packit 4a16fb
Packit 4a16fb
	if ((ret = snd_ctl_hw_open(&ctl, NULL, card, 0)) < 0)
Packit 4a16fb
		return ret;
Packit 4a16fb
Packit 4a16fb
	switch (stream) {
Packit 4a16fb
	case SND_PCM_STREAM_PLAYBACK:
Packit 4a16fb
		filefmt = SNDRV_FILE_PCM_STREAM_PLAYBACK;
Packit 4a16fb
		break;
Packit 4a16fb
	case SND_PCM_STREAM_CAPTURE:
Packit 4a16fb
		filefmt = SNDRV_FILE_PCM_STREAM_CAPTURE;
Packit 4a16fb
		break;
Packit 4a16fb
	default:
Packit 4a16fb
		SNDERR("invalid stream %d", stream);
Packit 4a16fb
		return -EINVAL;
Packit 4a16fb
	}
Packit 4a16fb
	sprintf(filename, filefmt, card, device);
Packit 4a16fb
Packit 4a16fb
      __again:
Packit 4a16fb
      	if (attempt++ > 3) {
Packit 4a16fb
		ret = -EBUSY;
Packit 4a16fb
		goto _err;
Packit 4a16fb
	}
Packit 4a16fb
	ret = snd_ctl_pcm_prefer_subdevice(ctl, subdevice);
Packit 4a16fb
	if (ret < 0)
Packit 4a16fb
		goto _err;
Packit 4a16fb
	fmode = O_RDWR;
Packit 4a16fb
	if (mode & SND_PCM_NONBLOCK)
Packit 4a16fb
		fmode |= O_NONBLOCK;
Packit 4a16fb
	if (mode & SND_PCM_ASYNC)
Packit 4a16fb
		fmode |= O_ASYNC;
Packit 4a16fb
	if (mode & SND_PCM_APPEND)
Packit 4a16fb
		fmode |= O_APPEND;
Packit 4a16fb
	fd = snd_open_device(filename, fmode);
Packit 4a16fb
	if (fd < 0) {
Packit 4a16fb
		ret = -errno;
Packit 4a16fb
		SYSMSG("open '%s' failed (%i)", filename, ret);
Packit 4a16fb
		goto _err;
Packit 4a16fb
	}
Packit 4a16fb
	if (subdevice >= 0) {
Packit 4a16fb
		memset(&info, 0, sizeof(info));
Packit 4a16fb
		if (ioctl(fd, SNDRV_PCM_IOCTL_INFO, &info) < 0) {
Packit 4a16fb
			ret = -errno;
Packit 4a16fb
			SYSMSG("SNDRV_PCM_IOCTL_INFO failed (%i)", ret);
Packit 4a16fb
			goto _err;
Packit 4a16fb
		}
Packit 4a16fb
		if (info.subdevice != (unsigned int) subdevice) {
Packit 4a16fb
			close(fd);
Packit 4a16fb
			fd = -1;
Packit 4a16fb
			goto __again;
Packit 4a16fb
		}
Packit 4a16fb
	}
Packit 4a16fb
	snd_ctl_close(ctl);
Packit 4a16fb
	return snd_pcm_hw_open_fd(pcmp, name, fd, sync_ptr_ioctl);
Packit 4a16fb
       _err:
Packit 4a16fb
	if (fd >= 0)
Packit 4a16fb
		close(fd);
Packit 4a16fb
	snd_ctl_close(ctl);
Packit 4a16fb
	return ret;
Packit 4a16fb
}
Packit 4a16fb
Packit 4a16fb
/*! \page pcm_plugins
Packit 4a16fb
Packit 4a16fb
\section pcm_plugins_hw Plugin: hw
Packit 4a16fb
Packit 4a16fb
This plugin communicates directly with the ALSA kernel driver. It is a raw
Packit 4a16fb
communication without any conversions. The emulation of mmap access can be
Packit 4a16fb
optionally enabled, but expect worse latency in the case.
Packit 4a16fb
Packit 4a16fb
The nonblock option specifies whether the device is opened in a non-blocking
Packit 4a16fb
manner.  Note that the blocking behavior for read/write access won't be
Packit 4a16fb
changed by this option.  This influences only on the blocking behavior at
Packit 4a16fb
opening the device.  If you would like to keep the compatibility with the
Packit 4a16fb
older ALSA stuff, turn this option off.
Packit 4a16fb
Packit 4a16fb
\code
Packit 4a16fb
pcm.name {
Packit 4a16fb
	type hw			# Kernel PCM
Packit 4a16fb
	card INT/STR		# Card name (string) or number (integer)
Packit 4a16fb
	[device INT]		# Device number (default 0)
Packit 4a16fb
	[subdevice INT]		# Subdevice number (default -1: first available)
Packit 4a16fb
	[sync_ptr_ioctl BOOL]	# Use SYNC_PTR ioctl rather than the direct mmap access for control structures
Packit 4a16fb
	[nonblock BOOL]		# Force non-blocking open mode
Packit 4a16fb
	[format STR]		# Restrict only to the given format
Packit 4a16fb
	[channels INT]		# Restrict only to the given channels
Packit 4a16fb
	[rate INT]		# Restrict only to the given rate
Packit 4a16fb
	[chmap MAP]		# Override channel maps; MAP is a string array
Packit 4a16fb
}
Packit 4a16fb
\endcode
Packit 4a16fb
Packit 4a16fb
\subsection pcm_plugins_hw_funcref Function reference
Packit 4a16fb
Packit 4a16fb
    Packit 4a16fb
      
  • snd_pcm_hw_open()
  • Packit 4a16fb
      
  • _snd_pcm_hw_open()
  • Packit 4a16fb
    Packit 4a16fb
    Packit 4a16fb
    */
    Packit 4a16fb
    Packit 4a16fb
    /**
    Packit 4a16fb
     * \brief Creates a new hw PCM
    Packit 4a16fb
     * \param pcmp Returns created PCM handle
    Packit 4a16fb
     * \param name Name of PCM
    Packit 4a16fb
     * \param root Root configuration node
    Packit 4a16fb
     * \param conf Configuration node with hw PCM description
    Packit 4a16fb
     * \param stream PCM Stream
    Packit 4a16fb
     * \param mode PCM Mode
    Packit 4a16fb
     * \warning Using of this function might be dangerous in the sense
    Packit 4a16fb
     *          of compatibility reasons. The prototype might be freely
    Packit 4a16fb
     *          changed in future.
    Packit 4a16fb
     */
    Packit 4a16fb
    int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
    Packit 4a16fb
    		     snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf,
    Packit 4a16fb
    		     snd_pcm_stream_t stream, int mode)
    Packit 4a16fb
    {
    Packit 4a16fb
    	snd_config_iterator_t i, next;
    Packit 4a16fb
    	long card = -1, device = 0, subdevice = -1;
    Packit 4a16fb
    	const char *str;
    Packit 4a16fb
    	int err, sync_ptr_ioctl = 0;
    Packit 4a16fb
    	int rate = 0, channels = 0;
    Packit 4a16fb
    	snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
    Packit 4a16fb
    	snd_config_t *n;
    Packit 4a16fb
    	int nonblock = 1; /* non-block per default */
    Packit 4a16fb
    	snd_pcm_chmap_query_t **chmap = NULL;
    Packit 4a16fb
    	snd_pcm_hw_t *hw;
    Packit 4a16fb
    Packit 4a16fb
    	/* look for defaults.pcm.nonblock definition */
    Packit 4a16fb
    	if (snd_config_search(root, "defaults.pcm.nonblock", &n) >= 0) {
    Packit 4a16fb
    		err = snd_config_get_bool(n);
    Packit 4a16fb
    		if (err >= 0)
    Packit 4a16fb
    			nonblock = err;
    Packit 4a16fb
    	}
    Packit 4a16fb
    	snd_config_for_each(i, next, conf) {
    Packit 4a16fb
    		const char *id;
    Packit 4a16fb
    		n = snd_config_iterator_entry(i);
    Packit 4a16fb
    		if (snd_config_get_id(n, &id) < 0)
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		if (snd_pcm_conf_generic_id(id))
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		if (strcmp(id, "card") == 0) {
    Packit 4a16fb
    			err = snd_config_get_integer(n, &card;;
    Packit 4a16fb
    			if (err < 0) {
    Packit 4a16fb
    				err = snd_config_get_string(n, &str);
    Packit 4a16fb
    				if (err < 0) {
    Packit 4a16fb
    					SNDERR("Invalid type for %s", id);
    Packit 4a16fb
    					err = -EINVAL;
    Packit 4a16fb
    					goto fail;
    Packit 4a16fb
    				}
    Packit 4a16fb
    				card = snd_card_get_index(str);
    Packit 4a16fb
    				if (card < 0) {
    Packit 4a16fb
    					SNDERR("Invalid value for %s", id);
    Packit 4a16fb
    					err = card;
    Packit 4a16fb
    					goto fail;
    Packit 4a16fb
    				}
    Packit 4a16fb
    			}
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		if (strcmp(id, "device") == 0) {
    Packit 4a16fb
    			err = snd_config_get_integer(n, &device);
    Packit 4a16fb
    			if (err < 0) {
    Packit 4a16fb
    				SNDERR("Invalid type for %s", id);
    Packit 4a16fb
    				goto fail;
    Packit 4a16fb
    			}
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		if (strcmp(id, "subdevice") == 0) {
    Packit 4a16fb
    			err = snd_config_get_integer(n, &subdevice);
    Packit 4a16fb
    			if (err < 0) {
    Packit 4a16fb
    				SNDERR("Invalid type for %s", id);
    Packit 4a16fb
    				goto fail;
    Packit 4a16fb
    			}
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		if (strcmp(id, "sync_ptr_ioctl") == 0) {
    Packit 4a16fb
    			err = snd_config_get_bool(n);
    Packit 4a16fb
    			if (err < 0)
    Packit 4a16fb
    				continue;
    Packit 4a16fb
    			sync_ptr_ioctl = err;
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		if (strcmp(id, "nonblock") == 0) {
    Packit 4a16fb
    			err = snd_config_get_bool(n);
    Packit 4a16fb
    			if (err < 0)
    Packit 4a16fb
    				continue;
    Packit 4a16fb
    			nonblock = err;
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		if (strcmp(id, "rate") == 0) {
    Packit 4a16fb
    			long val;
    Packit 4a16fb
    			err = snd_config_get_integer(n, &val;;
    Packit 4a16fb
    			if (err < 0) {
    Packit 4a16fb
    				SNDERR("Invalid type for %s", id);
    Packit 4a16fb
    				goto fail;
    Packit 4a16fb
    			}
    Packit 4a16fb
    			rate = val;
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		if (strcmp(id, "format") == 0) {
    Packit 4a16fb
    			err = snd_config_get_string(n, &str);
    Packit 4a16fb
    			if (err < 0) {
    Packit 4a16fb
    				SNDERR("invalid type for %s", id);
    Packit 4a16fb
    				goto fail;
    Packit 4a16fb
    			}
    Packit 4a16fb
    			format = snd_pcm_format_value(str);
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		if (strcmp(id, "channels") == 0) {
    Packit 4a16fb
    			long val;
    Packit 4a16fb
    			err = snd_config_get_integer(n, &val;;
    Packit 4a16fb
    			if (err < 0) {
    Packit 4a16fb
    				SNDERR("Invalid type for %s", id);
    Packit 4a16fb
    				goto fail;
    Packit 4a16fb
    			}
    Packit 4a16fb
    			channels = val;
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		if (strcmp(id, "chmap") == 0) {
    Packit 4a16fb
    			snd_pcm_free_chmaps(chmap);
    Packit 4a16fb
    			chmap = _snd_pcm_parse_config_chmaps(n);
    Packit 4a16fb
    			if (!chmap) {
    Packit 4a16fb
    				SNDERR("Invalid channel map for %s", id);
    Packit 4a16fb
    				err = -EINVAL;
    Packit 4a16fb
    				goto fail;
    Packit 4a16fb
    			}
    Packit 4a16fb
    			continue;
    Packit 4a16fb
    		}
    Packit 4a16fb
    		SNDERR("Unknown field %s", id);
    Packit 4a16fb
    		err = -EINVAL;
    Packit 4a16fb
    		goto fail;
    Packit 4a16fb
    	}
    Packit 4a16fb
    	if (card < 0) {
    Packit 4a16fb
    		SNDERR("card is not defined");
    Packit 4a16fb
    		err = -EINVAL;
    Packit 4a16fb
    		goto fail;
    Packit 4a16fb
    	}
    Packit 4a16fb
    	err = snd_pcm_hw_open(pcmp, name, card, device, subdevice, stream,
    Packit 4a16fb
    			      mode | (nonblock ? SND_PCM_NONBLOCK : 0),
    Packit 4a16fb
    			      0, sync_ptr_ioctl);
    Packit 4a16fb
    	if (err < 0)
    Packit 4a16fb
    		goto fail;
    Packit 4a16fb
    	if (nonblock && ! (mode & SND_PCM_NONBLOCK)) {
    Packit 4a16fb
    		/* revert to blocking mode for read/write access */
    Packit 4a16fb
    		snd_pcm_hw_nonblock(*pcmp, 0);
    Packit 4a16fb
    		(*pcmp)->mode = mode;
    Packit 4a16fb
    	} else
    Packit 4a16fb
    		/* make sure the SND_PCM_NO_xxx flags don't get lost on the
    Packit 4a16fb
    		 * way */
    Packit 4a16fb
    		(*pcmp)->mode |= mode & (SND_PCM_NO_AUTO_RESAMPLE|
    Packit 4a16fb
    					 SND_PCM_NO_AUTO_CHANNELS|
    Packit 4a16fb
    					 SND_PCM_NO_AUTO_FORMAT|
    Packit 4a16fb
    					 SND_PCM_NO_SOFTVOL);
    Packit 4a16fb
    Packit 4a16fb
    	hw = (*pcmp)->private_data;
    Packit 4a16fb
    	if (format != SND_PCM_FORMAT_UNKNOWN)
    Packit 4a16fb
    		hw->format = format;
    Packit 4a16fb
    	if (channels > 0)
    Packit 4a16fb
    		hw->channels = channels;
    Packit 4a16fb
    	if (rate > 0)
    Packit 4a16fb
    		hw->rate = rate;
    Packit 4a16fb
    	if (chmap)
    Packit 4a16fb
    		hw->chmap_override = chmap;
    Packit 4a16fb
    Packit 4a16fb
    	return 0;
    Packit 4a16fb
    Packit 4a16fb
    fail:
    Packit 4a16fb
            snd_pcm_free_chmaps(chmap);
    Packit 4a16fb
            return err;
    Packit 4a16fb
    }
    Packit 4a16fb
    Packit 4a16fb
    #ifndef DOC_HIDDEN
    Packit 4a16fb
    SND_DLSYM_BUILD_VERSION(_snd_pcm_hw_open, SND_PCM_DLSYM_VERSION);
    Packit 4a16fb
    #endif
    Packit 4a16fb
    Packit 4a16fb
    /*
    Packit 4a16fb
     *  To be removed helpers, but keep binary compatibility at the time
    Packit 4a16fb
     */
    Packit 4a16fb
    Packit 4a16fb
    #ifndef DOC_HIDDEN
    Packit 4a16fb
    #define __OLD_TO_NEW_MASK(x) ((x&7)|((x&0x07fffff8)<<5))
    Packit 4a16fb
    #define __NEW_TO_OLD_MASK(x) ((x&7)|((x&0xffffff00)>>5))
    Packit 4a16fb
    #endif
    Packit 4a16fb
    Packit 4a16fb
    static void snd_pcm_hw_convert_from_old_params(snd_pcm_hw_params_t *params,
    Packit 4a16fb
    					       struct sndrv_pcm_hw_params_old *oparams)
    Packit 4a16fb
    {
    Packit 4a16fb
    	unsigned int i;
    Packit 4a16fb
    Packit 4a16fb
    	memset(params, 0, sizeof(*params));
    Packit 4a16fb
    	params->flags = oparams->flags;
    Packit 4a16fb
    	for (i = 0; i < sizeof(oparams->masks) / sizeof(unsigned int); i++)
    Packit 4a16fb
    		params->masks[i].bits[0] = oparams->masks[i];
    Packit 4a16fb
    	memcpy(params->intervals, oparams->intervals, sizeof(oparams->intervals));
    Packit 4a16fb
    	params->rmask = __OLD_TO_NEW_MASK(oparams->rmask);
    Packit 4a16fb
    	params->cmask = __OLD_TO_NEW_MASK(oparams->cmask);
    Packit 4a16fb
    	params->info = oparams->info;
    Packit 4a16fb
    	params->msbits = oparams->msbits;
    Packit 4a16fb
    	params->rate_num = oparams->rate_num;
    Packit 4a16fb
    	params->rate_den = oparams->rate_den;
    Packit 4a16fb
    	params->fifo_size = oparams->fifo_size;
    Packit 4a16fb
    }
    Packit 4a16fb
    Packit 4a16fb
    static void snd_pcm_hw_convert_to_old_params(struct sndrv_pcm_hw_params_old *oparams,
    Packit 4a16fb
    					     snd_pcm_hw_params_t *params,
    Packit 4a16fb
    					     unsigned int *cmask)
    Packit 4a16fb
    {
    Packit 4a16fb
    	unsigned int i, j;
    Packit 4a16fb
    Packit 4a16fb
    	memset(oparams, 0, sizeof(*oparams));
    Packit 4a16fb
    	oparams->flags = params->flags;
    Packit 4a16fb
    	for (i = 0; i < sizeof(oparams->masks) / sizeof(unsigned int); i++) {
    Packit 4a16fb
    		oparams->masks[i] = params->masks[i].bits[0];
    Packit 4a16fb
    		for (j = 1; j < sizeof(params->masks[i].bits) / sizeof(unsigned int); j++)
    Packit 4a16fb
    			if (params->masks[i].bits[j]) {
    Packit 4a16fb
    				*cmask |= 1 << i;
    Packit 4a16fb
    				break;
    Packit 4a16fb
    			}
    Packit 4a16fb
    	}
    Packit 4a16fb
    	memcpy(oparams->intervals, params->intervals, sizeof(oparams->intervals));
    Packit 4a16fb
    	oparams->rmask = __NEW_TO_OLD_MASK(params->rmask);
    Packit 4a16fb
    	oparams->cmask = __NEW_TO_OLD_MASK(params->cmask);
    Packit 4a16fb
    	oparams->info = params->info;
    Packit 4a16fb
    	oparams->msbits = params->msbits;
    Packit 4a16fb
    	oparams->rate_num = params->rate_num;
    Packit 4a16fb
    	oparams->rate_den = params->rate_den;
    Packit 4a16fb
    	oparams->fifo_size = params->fifo_size;
    Packit 4a16fb
    }
    Packit 4a16fb
    Packit 4a16fb
    static int use_old_hw_params_ioctl(int fd, unsigned int cmd, snd_pcm_hw_params_t *params)
    Packit 4a16fb
    {
    Packit 4a16fb
    	struct sndrv_pcm_hw_params_old oparams;
    Packit 4a16fb
    	unsigned int cmask = 0;
    Packit 4a16fb
    	int res;
    Packit 4a16fb
    	
    Packit 4a16fb
    	snd_pcm_hw_convert_to_old_params(&oparams, params, &cmask);
    Packit 4a16fb
    	res = ioctl(fd, cmd, &oparams);
    Packit 4a16fb
    	snd_pcm_hw_convert_from_old_params(params, &oparams);
    Packit 4a16fb
    	params->cmask |= cmask;
    Packit 4a16fb
    	return res;
    Packit 4a16fb
    }