/*
* Digital Audio (PCM) abstract layer
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/pcm.h>
#include <sound/control.h>
#include <sound/info.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Abramo Bagnara <abramo@alsa-project.org>");
MODULE_DESCRIPTION("Midlevel PCM code for ALSA.");
MODULE_LICENSE("GPL");
snd_pcm_t *snd_pcm_devices[SNDRV_CARDS * SNDRV_PCM_DEVICES];
static LIST_HEAD(snd_pcm_notify_list);
static DECLARE_MUTEX(register_mutex);
static int snd_pcm_free(snd_pcm_t *pcm);
static int snd_pcm_dev_free(snd_device_t *device);
static int snd_pcm_dev_register(snd_device_t *device);
static int snd_pcm_dev_disconnect(snd_device_t *device);
static int snd_pcm_dev_unregister(snd_device_t *device);
static int snd_pcm_control_ioctl(snd_card_t * card,
snd_ctl_file_t * control,
unsigned int cmd, unsigned long arg)
{
unsigned int tmp;
tmp = card->number * SNDRV_PCM_DEVICES;
switch (cmd) {
case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE:
{
int device;
if (get_user(device, (int __user *)arg))
return -EFAULT;
device = device < 0 ? 0 : device + 1;
while (device < SNDRV_PCM_DEVICES) {
if (snd_pcm_devices[tmp + device])
break;
device++;
}
if (device == SNDRV_PCM_DEVICES)
device = -1;
if (put_user(device, (int __user *)arg))
return -EFAULT;
return 0;
}
case SNDRV_CTL_IOCTL_PCM_INFO:
{
snd_pcm_info_t __user *info;
unsigned int device, subdevice;
snd_pcm_stream_t stream;
snd_pcm_t *pcm;
snd_pcm_str_t *pstr;
snd_pcm_substream_t *substream;
info = (snd_pcm_info_t __user *)arg;
if (get_user(device, &info->device))
return -EFAULT;
if (device >= SNDRV_PCM_DEVICES)
return -ENXIO;
pcm = snd_pcm_devices[tmp + device];
if (pcm == NULL)
return -ENXIO;
if (get_user(stream, &info->stream))
return -EFAULT;
if (stream < 0 || stream > 1)
return -EINVAL;
pstr = &pcm->streams[stream];
if (pstr->substream_count == 0)
return -ENOENT;
if (get_user(subdevice, &info->subdevice))
return -EFAULT;
if (subdevice >= pstr->substream_count)
return -ENXIO;
for (substream = pstr->substream; substream; substream = substream->next)
if (substream->number == (int)subdevice)
break;
if (substream == NULL)
return -ENXIO;
return snd_pcm_info_user(substream, info);
}
case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE:
{
int val;
if (get_user(val, (int __user *)arg))
return -EFAULT;
control->prefer_pcm_subdevice = val;
return 0;
}
}
return -ENOIOCTLCMD;
}
#define STATE(v) [SNDRV_PCM_STATE_##v] = #v
#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v
#define READY(v) [SNDRV_PCM_READY_##v] = #v
#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v
#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v
#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v
#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v
#d