string: factorize skip_spaces and export it to be generally available
[safe/jmp/linux-2.6] / sound / core / pcm.c
index b860247..6884ae0 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  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
@@ -19,7 +19,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/time.h>
@@ -30,7 +29,7 @@
 #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");
 
@@ -42,21 +41,49 @@ static int snd_pcm_free(struct snd_pcm *pcm);
 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)
@@ -69,14 +96,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
                        if (get_user(device, (int __user *)arg))
                                return -EFAULT;
                        mutex_lock(&register_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(&register_mutex);
                        if (put_user(device, (int __user *)arg))
                                return -EFAULT;
@@ -102,7 +122,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
                        if (get_user(subdevice, &info->subdevice))
                                return -EFAULT;
                        mutex_lock(&register_mutex);
-                       pcm = snd_pcm_search(card, device);
+                       pcm = snd_pcm_get(card, device);
                        if (pcm == NULL) {
                                err = -ENXIO;
                                goto _error;
@@ -142,18 +162,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
        return -ENOIOCTLCMD;
 }
 
-#ifdef CONFIG_SND_VERBOSE_PROCFS
-
-#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
-#define START(v) [SNDRV_PCM_START_##v] = #v
 #define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v
-#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v 
 
 static char *snd_pcm_format_names[] = {
        FORMAT(S8),
@@ -196,10 +205,23 @@ static char *snd_pcm_format_names[] = {
        FORMAT(U18_3BE),
 };
 
-static const char *snd_pcm_format_name(snd_pcm_format_t format)
+const char *snd_pcm_format_name(snd_pcm_format_t format)
 {
        return snd_pcm_format_names[format];
 }
+EXPORT_SYMBOL_GPL(snd_pcm_format_name);
+
+#ifdef CONFIG_SND_VERBOSE_PROCFS
+
+#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
+#define START(v) [SNDRV_PCM_START_##v] = #v
+#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v 
 
 static char *snd_pcm_stream_names[] = {
        STREAM(PLAYBACK),
@@ -231,12 +253,11 @@ static char *snd_pcm_subformat_names[] = {
 
 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];
 }
 
@@ -252,7 +273,6 @@ static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat)
 
 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];
 }
 
@@ -362,7 +382,6 @@ static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
        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));
@@ -390,9 +409,7 @@ static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry,
        }
        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);
@@ -418,6 +435,7 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
                return;
        }
        snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state));
+       snd_iprintf(buffer, "owner_pid   : %d\n", pid_vnr(substream->pid));
        snd_iprintf(buffer, "trigger_time: %ld.%09ld\n",
                status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec);
        snd_iprintf(buffer, "tstamp      : %ld.%09ld\n",
@@ -630,6 +648,9 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
                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;
@@ -638,6 +659,10 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
                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;
                }
@@ -645,7 +670,6 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
                spin_lock_init(&substream->self_group.lock);
                INIT_LIST_HEAD(&substream->self_group.substreams);
                list_add_tail(&substream->link_list, &substream->self_group.substreams);
-               spin_lock_init(&substream->timer_lock);
                atomic_set(&substream->mmap_count, 0);
                prev = substream;
        }
@@ -670,7 +694,7 @@ EXPORT_SYMBOL(snd_pcm_new_stream);
  *
  * Returns zero if successful, or a negative error code on failure.
  */
-int snd_pcm_new(struct snd_card *card, char *id, int device,
+int snd_pcm_new(struct snd_card *card, const char *id, int device,
                int playback_count, int capture_count,
                struct snd_pcm ** rpcm)
 {
@@ -680,12 +704,12 @@ int snd_pcm_new(struct snd_card *card, char *id, int device,
                .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");
@@ -709,7 +733,8 @@ int snd_pcm_new(struct snd_card *card, char *id, int device,
                snd_pcm_free(pcm);
                return err;
        }
-       *rpcm = pcm;
+       if (rpcm)
+               *rpcm = pcm;
        return 0;
 }
 
@@ -724,6 +749,7 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
        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;
@@ -740,7 +766,13 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
 
 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);
@@ -756,12 +788,6 @@ static int snd_pcm_dev_free(struct snd_device *device)
        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)
@@ -771,27 +797,26 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
        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);
-               if (kctl->pid == current->pid) {
+       read_lock(&card->ctl_files_rwlock);
+       list_for_each_entry(kctl, &card->ctl_files, list) {
+               if (kctl->pid == task_pid(current)) {
                        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:
@@ -869,9 +894,6 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
        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;
 
@@ -879,6 +901,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
        substream->private_data = pcm->private_data;
        substream->ref_count = 1;
        substream->f_flags = file->f_flags;
+       substream->pid = get_pid(task_pid(current));
        pstr->substream_opened++;
        *rsubstream = substream;
        return 0;
@@ -888,8 +911,9 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
 {
        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,
@@ -899,24 +923,52 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
        kfree(runtime->hw_constraints.rules);
        kfree(runtime);
        substream->runtime = NULL;
+       put_pid(substream->pid);
+       substream->pid = NULL;
        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 snd_pcm *pcm;
+       struct device *dev;
 
-       snd_assert(pcm != NULL && device != NULL, return -ENXIO);
+       if (snd_BUG_ON(!device || !device->device_data))
+               return -ENXIO;
+       pcm = device->device_data;
        mutex_lock(&register_mutex);
-       if (snd_pcm_search(pcm->card, pcm->device)) {
+       err = snd_pcm_add(pcm);
+       if (err) {
                mutex_unlock(&register_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)
@@ -931,23 +983,31 @@ static int snd_pcm_dev_register(struct snd_device *device)
                        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(&register_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(&register_mutex);
        return 0;
 }
@@ -955,35 +1015,22 @@ static int snd_pcm_dev_register(struct snd_device *device)
 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(&register_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(&register_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(&register_mutex);
-       list_del(&pcm->list);
        for (cidx = 0; cidx < 2; cidx++) {
                devtype = -1;
                switch (cidx) {
@@ -995,33 +1042,30 @@ static int snd_pcm_dev_unregister(struct snd_device *device)
                        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(&register_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(&register_mutex);
        if (nfree) {
                list_del(&notify->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(&notify->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(&register_mutex);
        return 0;
@@ -1037,12 +1081,10 @@ EXPORT_SYMBOL(snd_pcm_notify);
 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(&register_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)