/*
* Digital Audio (PCM) abstract layer
- * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
*
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <sound/control.h>
#include <sound/info.h>
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Abramo Bagnara <abramo@alsa-project.org>");
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>");
MODULE_DESCRIPTION("Midlevel PCM code for ALSA.");
MODULE_LICENSE("GPL");
static int snd_pcm_dev_free(struct snd_device *device);
static int snd_pcm_dev_register(struct snd_device *device);
static int snd_pcm_dev_disconnect(struct snd_device *device);
-static int snd_pcm_dev_unregister(struct snd_device *device);
-static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device)
+static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device)
{
- struct list_head *p;
struct snd_pcm *pcm;
- list_for_each(p, &snd_pcm_devices) {
- pcm = list_entry(p, struct snd_pcm, list);
+ list_for_each_entry(pcm, &snd_pcm_devices, list) {
if (pcm->card == card && pcm->device == device)
return pcm;
}
return NULL;
}
+static int snd_pcm_next(struct snd_card *card, int device)
+{
+ struct snd_pcm *pcm;
+
+ list_for_each_entry(pcm, &snd_pcm_devices, list) {
+ if (pcm->card == card && pcm->device > device)
+ return pcm->device;
+ else if (pcm->card->number > card->number)
+ return -1;
+ }
+ return -1;
+}
+
+static int snd_pcm_add(struct snd_pcm *newpcm)
+{
+ struct snd_pcm *pcm;
+
+ list_for_each_entry(pcm, &snd_pcm_devices, list) {
+ if (pcm->card == newpcm->card && pcm->device == newpcm->device)
+ return -EBUSY;
+ if (pcm->card->number > newpcm->card->number ||
+ (pcm->card == newpcm->card &&
+ pcm->device > newpcm->device)) {
+ list_add(&newpcm->list, pcm->list.prev);
+ return 0;
+ }
+ }
+ list_add_tail(&newpcm->list, &snd_pcm_devices);
+ return 0;
+}
+
static int snd_pcm_control_ioctl(struct snd_card *card,
struct snd_ctl_file *control,
unsigned int cmd, unsigned long arg)
if (get_user(device, (int __user *)arg))
return -EFAULT;
mutex_lock(®ister_mutex);
- device = device < 0 ? 0 : device + 1;
- while (device < SNDRV_PCM_DEVICES) {
- if (snd_pcm_search(card, device))
- break;
- device++;
- }
- if (device == SNDRV_PCM_DEVICES)
- device = -1;
+ device = snd_pcm_next(card, device);
mutex_unlock(®ister_mutex);
if (put_user(device, (int __user *)arg))
return -EFAULT;
if (get_user(subdevice, &info->subdevice))
return -EFAULT;
mutex_lock(®ister_mutex);
- pcm = snd_pcm_search(card, device);
+ pcm = snd_pcm_get(card, device);
if (pcm == NULL) {
err = -ENXIO;
goto _error;
static char *snd_pcm_tstamp_mode_names[] = {
TSTAMP(NONE),
- TSTAMP(MMAP),
+ TSTAMP(ENABLE),
};
static const char *snd_pcm_stream_name(int stream)
{
- snd_assert(stream <= SNDRV_PCM_STREAM_LAST, return NULL);
return snd_pcm_stream_names[stream];
}
static const char *snd_pcm_tstamp_mode_name(int mode)
{
- snd_assert(mode <= SNDRV_PCM_TSTAMP_LAST, return NULL);
return snd_pcm_tstamp_mode_names[mode];
}
snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den);
snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size);
snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size);
- snd_iprintf(buffer, "tick_time: %u\n", runtime->tick_time);
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
if (substream->oss.oss) {
snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format));
}
snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode));
snd_iprintf(buffer, "period_step: %u\n", runtime->period_step);
- snd_iprintf(buffer, "sleep_min: %u\n", runtime->sleep_min);
snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min);
- snd_iprintf(buffer, "xfer_align: %lu\n", runtime->xfer_align);
snd_iprintf(buffer, "start_threshold: %lu\n", runtime->start_threshold);
snd_iprintf(buffer, "stop_threshold: %lu\n", runtime->stop_threshold);
snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold);
static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr)
{
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
- if (pstr->proc_xrun_debug_entry) {
- snd_info_unregister(pstr->proc_xrun_debug_entry);
- pstr->proc_xrun_debug_entry = NULL;
- }
+ snd_info_free_entry(pstr->proc_xrun_debug_entry);
+ pstr->proc_xrun_debug_entry = NULL;
#endif
- if (pstr->proc_info_entry) {
- snd_info_unregister(pstr->proc_info_entry);
- pstr->proc_info_entry = NULL;
- }
- if (pstr->proc_root) {
- snd_info_unregister(pstr->proc_root);
- pstr->proc_root = NULL;
- }
+ snd_info_free_entry(pstr->proc_info_entry);
+ pstr->proc_info_entry = NULL;
+ snd_info_free_entry(pstr->proc_root);
+ pstr->proc_root = NULL;
return 0;
}
return 0;
}
-
+
static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream)
{
- if (substream->proc_info_entry) {
- snd_info_unregister(substream->proc_info_entry);
- substream->proc_info_entry = NULL;
- }
- if (substream->proc_hw_params_entry) {
- snd_info_unregister(substream->proc_hw_params_entry);
- substream->proc_hw_params_entry = NULL;
- }
- if (substream->proc_sw_params_entry) {
- snd_info_unregister(substream->proc_sw_params_entry);
- substream->proc_sw_params_entry = NULL;
- }
- if (substream->proc_status_entry) {
- snd_info_unregister(substream->proc_status_entry);
- substream->proc_status_entry = NULL;
- }
- if (substream->proc_root) {
- snd_info_unregister(substream->proc_root);
- substream->proc_root = NULL;
- }
+ snd_info_free_entry(substream->proc_info_entry);
+ substream->proc_info_entry = NULL;
+ snd_info_free_entry(substream->proc_hw_params_entry);
+ substream->proc_hw_params_entry = NULL;
+ snd_info_free_entry(substream->proc_sw_params_entry);
+ substream->proc_sw_params_entry = NULL;
+ snd_info_free_entry(substream->proc_status_entry);
+ substream->proc_status_entry = NULL;
+ snd_info_free_entry(substream->proc_root);
+ substream->proc_root = NULL;
return 0;
}
#else /* !CONFIG_SND_VERBOSE_PROCFS */
substream->number = idx;
substream->stream = stream;
sprintf(substream->name, "subdevice #%i", idx);
+ snprintf(substream->latency_id, sizeof(substream->latency_id),
+ "ALSA-PCM%d-%d%c%d", pcm->card->number, pcm->device,
+ (stream ? 'c' : 'p'), idx);
substream->buffer_bytes_max = UINT_MAX;
if (prev == NULL)
pstr->substream = substream;
err = snd_pcm_substream_proc_init(substream);
if (err < 0) {
snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n");
+ if (prev == NULL)
+ pstr->substream = NULL;
+ else
+ prev->next = NULL;
kfree(substream);
return err;
}
.dev_free = snd_pcm_dev_free,
.dev_register = snd_pcm_dev_register,
.dev_disconnect = snd_pcm_dev_disconnect,
- .dev_unregister = snd_pcm_dev_unregister
};
- snd_assert(rpcm != NULL, return -EINVAL);
- *rpcm = NULL;
- snd_assert(card != NULL, return -ENXIO);
+ if (snd_BUG_ON(!card))
+ return -ENXIO;
+ if (rpcm)
+ *rpcm = NULL;
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
if (pcm == NULL) {
snd_printk(KERN_ERR "Cannot allocate PCM\n");
snd_pcm_free(pcm);
return err;
}
- *rpcm = pcm;
+ if (rpcm)
+ *rpcm = pcm;
return 0;
}
substream = pstr->substream;
while (substream) {
substream_next = substream->next;
+ snd_pcm_timer_done(substream);
snd_pcm_substream_proc_done(substream);
kfree(substream);
substream = substream_next;
static int snd_pcm_free(struct snd_pcm *pcm)
{
- snd_assert(pcm != NULL, return -ENXIO);
+ struct snd_pcm_notify *notify;
+
+ if (!pcm)
+ return 0;
+ list_for_each_entry(notify, &snd_pcm_notify_list, list) {
+ notify->n_unregister(pcm);
+ }
if (pcm->private_free)
pcm->private_free(pcm);
snd_pcm_lib_preallocate_free_for_all(pcm);
return snd_pcm_free(pcm);
}
-static void snd_pcm_tick_timer_func(unsigned long data)
-{
- struct snd_pcm_substream *substream = (struct snd_pcm_substream *) data;
- snd_pcm_tick_elapsed(substream);
-}
-
int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
struct file *file,
struct snd_pcm_substream **rsubstream)
struct snd_pcm_runtime *runtime;
struct snd_ctl_file *kctl;
struct snd_card *card;
- struct list_head *list;
int prefer_subdevice = -1;
size_t size;
- snd_assert(rsubstream != NULL, return -EINVAL);
+ if (snd_BUG_ON(!pcm || !rsubstream))
+ return -ENXIO;
*rsubstream = NULL;
- snd_assert(pcm != NULL, return -ENXIO);
pstr = &pcm->streams[stream];
if (pstr->substream == NULL || pstr->substream_count == 0)
return -ENODEV;
card = pcm->card;
- down_read(&card->controls_rwsem);
- list_for_each(list, &card->ctl_files) {
- kctl = snd_ctl_file(list);
+ read_lock(&card->ctl_files_rwlock);
+ list_for_each_entry(kctl, &card->ctl_files, list) {
if (kctl->pid == current->pid) {
prefer_subdevice = kctl->prefer_pcm_subdevice;
- break;
+ if (prefer_subdevice != -1)
+ break;
}
}
- up_read(&card->controls_rwsem);
+ read_unlock(&card->ctl_files_rwlock);
switch (stream) {
case SNDRV_PCM_STREAM_PLAYBACK:
memset((void*)runtime->control, 0, size);
init_waitqueue_head(&runtime->sleep);
- init_timer(&runtime->tick_timer);
- runtime->tick_timer.function = snd_pcm_tick_timer_func;
- runtime->tick_timer.data = (unsigned long) substream;
runtime->status->state = SNDRV_PCM_STATE_OPEN;
{
struct snd_pcm_runtime *runtime;
+ if (PCM_RUNTIME_CHECK(substream))
+ return;
runtime = substream->runtime;
- snd_assert(runtime != NULL, return);
if (runtime->private_free != NULL)
runtime->private_free(runtime);
snd_free_pages((void*)runtime->status,
substream->pstr->substream_opened--;
}
+static ssize_t show_pcm_class(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_pcm *pcm;
+ const char *str;
+ static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = {
+ [SNDRV_PCM_CLASS_GENERIC] = "generic",
+ [SNDRV_PCM_CLASS_MULTI] = "multi",
+ [SNDRV_PCM_CLASS_MODEM] = "modem",
+ [SNDRV_PCM_CLASS_DIGITIZER] = "digitizer",
+ };
+
+ if (! (pcm = dev_get_drvdata(dev)) ||
+ pcm->dev_class > SNDRV_PCM_CLASS_LAST)
+ str = "none";
+ else
+ str = strs[pcm->dev_class];
+ return snprintf(buf, PAGE_SIZE, "%s\n", str);
+}
+
+static struct device_attribute pcm_attrs =
+ __ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL);
+
static int snd_pcm_dev_register(struct snd_device *device)
{
int cidx, err;
struct snd_pcm_substream *substream;
- struct list_head *list;
+ struct snd_pcm_notify *notify;
char str[16];
struct snd_pcm *pcm = device->device_data;
+ struct device *dev;
- snd_assert(pcm != NULL && device != NULL, return -ENXIO);
+ if (snd_BUG_ON(!pcm || !device))
+ return -ENXIO;
mutex_lock(®ister_mutex);
- if (snd_pcm_search(pcm->card, pcm->device)) {
+ err = snd_pcm_add(pcm);
+ if (err) {
mutex_unlock(®ister_mutex);
- return -EBUSY;
+ return err;
}
- list_add_tail(&pcm->list, &snd_pcm_devices);
for (cidx = 0; cidx < 2; cidx++) {
int devtype = -1;
if (pcm->streams[cidx].substream == NULL)
devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
break;
}
- if ((err = snd_register_device(devtype, pcm->card,
- pcm->device,
- &snd_pcm_f_ops[cidx],
- pcm, str)) < 0)
- {
+ /* device pointer to use, pcm->dev takes precedence if
+ * it is assigned, otherwise fall back to card's device
+ * if possible */
+ dev = pcm->dev;
+ if (!dev)
+ dev = snd_card_get_device_link(pcm->card);
+ /* register pcm */
+ err = snd_register_device_for_dev(devtype, pcm->card,
+ pcm->device,
+ &snd_pcm_f_ops[cidx],
+ pcm, str, dev);
+ if (err < 0) {
list_del(&pcm->list);
mutex_unlock(®ister_mutex);
return err;
}
+ snd_add_device_sysfs_file(devtype, pcm->card, pcm->device,
+ &pcm_attrs);
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
snd_pcm_timer_init(substream);
}
- list_for_each(list, &snd_pcm_notify_list) {
- struct snd_pcm_notify *notify;
- notify = list_entry(list, struct snd_pcm_notify, list);
+
+ list_for_each_entry(notify, &snd_pcm_notify_list, list)
notify->n_register(pcm);
- }
+
mutex_unlock(®ister_mutex);
return 0;
}
static int snd_pcm_dev_disconnect(struct snd_device *device)
{
struct snd_pcm *pcm = device->device_data;
- struct list_head *list;
+ struct snd_pcm_notify *notify;
struct snd_pcm_substream *substream;
- int cidx;
+ int cidx, devtype;
mutex_lock(®ister_mutex);
+ if (list_empty(&pcm->list))
+ goto unlock;
+
list_del_init(&pcm->list);
for (cidx = 0; cidx < 2; cidx++)
for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
if (substream->runtime)
substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED;
- list_for_each(list, &snd_pcm_notify_list) {
- struct snd_pcm_notify *notify;
- notify = list_entry(list, struct snd_pcm_notify, list);
+ list_for_each_entry(notify, &snd_pcm_notify_list, list) {
notify->n_disconnect(pcm);
}
- mutex_unlock(®ister_mutex);
- return 0;
-}
-
-static int snd_pcm_dev_unregister(struct snd_device *device)
-{
- int cidx, devtype;
- struct snd_pcm_substream *substream;
- struct list_head *list;
- struct snd_pcm *pcm = device->device_data;
-
- snd_assert(pcm != NULL, return -ENXIO);
- mutex_lock(®ister_mutex);
- list_del(&pcm->list);
for (cidx = 0; cidx < 2; cidx++) {
devtype = -1;
switch (cidx) {
break;
}
snd_unregister_device(devtype, pcm->card, pcm->device);
- for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
- snd_pcm_timer_done(substream);
- }
- list_for_each(list, &snd_pcm_notify_list) {
- struct snd_pcm_notify *notify;
- notify = list_entry(list, struct snd_pcm_notify, list);
- notify->n_unregister(pcm);
}
+ unlock:
mutex_unlock(®ister_mutex);
- return snd_pcm_free(pcm);
+ return 0;
}
int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
{
- struct list_head *p;
+ struct snd_pcm *pcm;
- snd_assert(notify != NULL && notify->n_register != NULL && notify->n_unregister != NULL, return -EINVAL);
+ if (snd_BUG_ON(!notify ||
+ !notify->n_register ||
+ !notify->n_unregister ||
+ !notify->n_disconnect))
+ return -EINVAL;
mutex_lock(®ister_mutex);
if (nfree) {
list_del(¬ify->list);
- list_for_each(p, &snd_pcm_devices)
- notify->n_unregister(list_entry(p,
- struct snd_pcm, list));
+ list_for_each_entry(pcm, &snd_pcm_devices, list)
+ notify->n_unregister(pcm);
} else {
list_add_tail(¬ify->list, &snd_pcm_notify_list);
- list_for_each(p, &snd_pcm_devices)
- notify->n_register(list_entry(p, struct snd_pcm, list));
+ list_for_each_entry(pcm, &snd_pcm_devices, list)
+ notify->n_register(pcm);
}
mutex_unlock(®ister_mutex);
return 0;
static void snd_pcm_proc_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
- struct list_head *p;
struct snd_pcm *pcm;
mutex_lock(®ister_mutex);
- list_for_each(p, &snd_pcm_devices) {
- pcm = list_entry(p, struct snd_pcm, list);
+ list_for_each_entry(pcm, &snd_pcm_devices, list) {
snd_iprintf(buffer, "%02i-%02i: %s : %s",
pcm->card->number, pcm->device, pcm->id, pcm->name);
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
static void snd_pcm_proc_done(void)
{
- if (snd_pcm_proc_entry)
- snd_info_unregister(snd_pcm_proc_entry);
+ snd_info_free_entry(snd_pcm_proc_entry);
}
#else /* !CONFIG_PROC_FS */