11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Digital Audio (PCM) abstract layer 31da177e4SLinus Torvalds * Copyright (c) by Jaroslav Kysela <perex@suse.cz> 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 71da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 81da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 91da177e4SLinus Torvalds * (at your option) any later version. 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 121da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 131da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 141da177e4SLinus Torvalds * GNU General Public License for more details. 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 171da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 181da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 191da177e4SLinus Torvalds * 201da177e4SLinus Torvalds */ 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds #include <sound/driver.h> 231da177e4SLinus Torvalds #include <linux/init.h> 241da177e4SLinus Torvalds #include <linux/slab.h> 251da177e4SLinus Torvalds #include <linux/time.h> 261a60d4c5SIngo Molnar #include <linux/mutex.h> 271da177e4SLinus Torvalds #include <sound/core.h> 281da177e4SLinus Torvalds #include <sound/minors.h> 291da177e4SLinus Torvalds #include <sound/pcm.h> 301da177e4SLinus Torvalds #include <sound/control.h> 311da177e4SLinus Torvalds #include <sound/info.h> 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Abramo Bagnara <abramo@alsa-project.org>"); 341da177e4SLinus Torvalds MODULE_DESCRIPTION("Midlevel PCM code for ALSA."); 351da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 361da177e4SLinus Torvalds 37f87135f5SClemens Ladisch static LIST_HEAD(snd_pcm_devices); 381da177e4SLinus Torvalds static LIST_HEAD(snd_pcm_notify_list); 391a60d4c5SIngo Molnar static DEFINE_MUTEX(register_mutex); 401da177e4SLinus Torvalds 41877211f5STakashi Iwai static int snd_pcm_free(struct snd_pcm *pcm); 42877211f5STakashi Iwai static int snd_pcm_dev_free(struct snd_device *device); 43877211f5STakashi Iwai static int snd_pcm_dev_register(struct snd_device *device); 44877211f5STakashi Iwai static int snd_pcm_dev_disconnect(struct snd_device *device); 451da177e4SLinus Torvalds 46f87135f5SClemens Ladisch static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device) 47f87135f5SClemens Ladisch { 48f87135f5SClemens Ladisch struct list_head *p; 49f87135f5SClemens Ladisch struct snd_pcm *pcm; 50f87135f5SClemens Ladisch 51f87135f5SClemens Ladisch list_for_each(p, &snd_pcm_devices) { 52f87135f5SClemens Ladisch pcm = list_entry(p, struct snd_pcm, list); 53f87135f5SClemens Ladisch if (pcm->card == card && pcm->device == device) 54f87135f5SClemens Ladisch return pcm; 55f87135f5SClemens Ladisch } 56f87135f5SClemens Ladisch return NULL; 57f87135f5SClemens Ladisch } 58f87135f5SClemens Ladisch 59877211f5STakashi Iwai static int snd_pcm_control_ioctl(struct snd_card *card, 60877211f5STakashi Iwai struct snd_ctl_file *control, 611da177e4SLinus Torvalds unsigned int cmd, unsigned long arg) 621da177e4SLinus Torvalds { 631da177e4SLinus Torvalds switch (cmd) { 641da177e4SLinus Torvalds case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE: 651da177e4SLinus Torvalds { 661da177e4SLinus Torvalds int device; 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds if (get_user(device, (int __user *)arg)) 691da177e4SLinus Torvalds return -EFAULT; 701a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 711da177e4SLinus Torvalds device = device < 0 ? 0 : device + 1; 721da177e4SLinus Torvalds while (device < SNDRV_PCM_DEVICES) { 73f87135f5SClemens Ladisch if (snd_pcm_search(card, device)) 741da177e4SLinus Torvalds break; 751da177e4SLinus Torvalds device++; 761da177e4SLinus Torvalds } 771da177e4SLinus Torvalds if (device == SNDRV_PCM_DEVICES) 781da177e4SLinus Torvalds device = -1; 791a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 801da177e4SLinus Torvalds if (put_user(device, (int __user *)arg)) 811da177e4SLinus Torvalds return -EFAULT; 821da177e4SLinus Torvalds return 0; 831da177e4SLinus Torvalds } 841da177e4SLinus Torvalds case SNDRV_CTL_IOCTL_PCM_INFO: 851da177e4SLinus Torvalds { 86877211f5STakashi Iwai struct snd_pcm_info __user *info; 871da177e4SLinus Torvalds unsigned int device, subdevice; 88877211f5STakashi Iwai int stream; 89877211f5STakashi Iwai struct snd_pcm *pcm; 90877211f5STakashi Iwai struct snd_pcm_str *pstr; 91877211f5STakashi Iwai struct snd_pcm_substream *substream; 92f87135f5SClemens Ladisch int err; 93f87135f5SClemens Ladisch 94877211f5STakashi Iwai info = (struct snd_pcm_info __user *)arg; 951da177e4SLinus Torvalds if (get_user(device, &info->device)) 961da177e4SLinus Torvalds return -EFAULT; 971da177e4SLinus Torvalds if (get_user(stream, &info->stream)) 981da177e4SLinus Torvalds return -EFAULT; 991da177e4SLinus Torvalds if (stream < 0 || stream > 1) 1001da177e4SLinus Torvalds return -EINVAL; 1011da177e4SLinus Torvalds if (get_user(subdevice, &info->subdevice)) 1021da177e4SLinus Torvalds return -EFAULT; 1031a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 104f87135f5SClemens Ladisch pcm = snd_pcm_search(card, device); 105f87135f5SClemens Ladisch if (pcm == NULL) { 106f87135f5SClemens Ladisch err = -ENXIO; 107f87135f5SClemens Ladisch goto _error; 108f87135f5SClemens Ladisch } 109f87135f5SClemens Ladisch pstr = &pcm->streams[stream]; 110f87135f5SClemens Ladisch if (pstr->substream_count == 0) { 111f87135f5SClemens Ladisch err = -ENOENT; 112f87135f5SClemens Ladisch goto _error; 113f87135f5SClemens Ladisch } 114f87135f5SClemens Ladisch if (subdevice >= pstr->substream_count) { 115f87135f5SClemens Ladisch err = -ENXIO; 116f87135f5SClemens Ladisch goto _error; 117f87135f5SClemens Ladisch } 118f87135f5SClemens Ladisch for (substream = pstr->substream; substream; 119f87135f5SClemens Ladisch substream = substream->next) 1201da177e4SLinus Torvalds if (substream->number == (int)subdevice) 1211da177e4SLinus Torvalds break; 122f87135f5SClemens Ladisch if (substream == NULL) { 123f87135f5SClemens Ladisch err = -ENXIO; 124f87135f5SClemens Ladisch goto _error; 125f87135f5SClemens Ladisch } 126f87135f5SClemens Ladisch err = snd_pcm_info_user(substream, info); 127f87135f5SClemens Ladisch _error: 1281a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 129f87135f5SClemens Ladisch return err; 1301da177e4SLinus Torvalds } 1311da177e4SLinus Torvalds case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE: 1321da177e4SLinus Torvalds { 1331da177e4SLinus Torvalds int val; 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds if (get_user(val, (int __user *)arg)) 1361da177e4SLinus Torvalds return -EFAULT; 1371da177e4SLinus Torvalds control->prefer_pcm_subdevice = val; 1381da177e4SLinus Torvalds return 0; 1391da177e4SLinus Torvalds } 1401da177e4SLinus Torvalds } 1411da177e4SLinus Torvalds return -ENOIOCTLCMD; 1421da177e4SLinus Torvalds } 14321a3479aSJaroslav Kysela 144b7d90a35STakashi Iwai #ifdef CONFIG_SND_VERBOSE_PROCFS 14521a3479aSJaroslav Kysela 1461da177e4SLinus Torvalds #define STATE(v) [SNDRV_PCM_STATE_##v] = #v 1471da177e4SLinus Torvalds #define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v 1481da177e4SLinus Torvalds #define READY(v) [SNDRV_PCM_READY_##v] = #v 1491da177e4SLinus Torvalds #define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v 1501da177e4SLinus Torvalds #define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v 1511da177e4SLinus Torvalds #define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v 1521da177e4SLinus Torvalds #define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v 1531da177e4SLinus Torvalds #define START(v) [SNDRV_PCM_START_##v] = #v 1541da177e4SLinus Torvalds #define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v 1551da177e4SLinus Torvalds #define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v 1561da177e4SLinus Torvalds 1571da177e4SLinus Torvalds static char *snd_pcm_format_names[] = { 1581da177e4SLinus Torvalds FORMAT(S8), 1591da177e4SLinus Torvalds FORMAT(U8), 1601da177e4SLinus Torvalds FORMAT(S16_LE), 1611da177e4SLinus Torvalds FORMAT(S16_BE), 1621da177e4SLinus Torvalds FORMAT(U16_LE), 1631da177e4SLinus Torvalds FORMAT(U16_BE), 1641da177e4SLinus Torvalds FORMAT(S24_LE), 1651da177e4SLinus Torvalds FORMAT(S24_BE), 1661da177e4SLinus Torvalds FORMAT(U24_LE), 1671da177e4SLinus Torvalds FORMAT(U24_BE), 1681da177e4SLinus Torvalds FORMAT(S32_LE), 1691da177e4SLinus Torvalds FORMAT(S32_BE), 1701da177e4SLinus Torvalds FORMAT(U32_LE), 1711da177e4SLinus Torvalds FORMAT(U32_BE), 1721da177e4SLinus Torvalds FORMAT(FLOAT_LE), 1731da177e4SLinus Torvalds FORMAT(FLOAT_BE), 1741da177e4SLinus Torvalds FORMAT(FLOAT64_LE), 1751da177e4SLinus Torvalds FORMAT(FLOAT64_BE), 1761da177e4SLinus Torvalds FORMAT(IEC958_SUBFRAME_LE), 1771da177e4SLinus Torvalds FORMAT(IEC958_SUBFRAME_BE), 1781da177e4SLinus Torvalds FORMAT(MU_LAW), 1791da177e4SLinus Torvalds FORMAT(A_LAW), 1801da177e4SLinus Torvalds FORMAT(IMA_ADPCM), 1811da177e4SLinus Torvalds FORMAT(MPEG), 1821da177e4SLinus Torvalds FORMAT(GSM), 1831da177e4SLinus Torvalds FORMAT(SPECIAL), 1841da177e4SLinus Torvalds FORMAT(S24_3LE), 1851da177e4SLinus Torvalds FORMAT(S24_3BE), 1861da177e4SLinus Torvalds FORMAT(U24_3LE), 1871da177e4SLinus Torvalds FORMAT(U24_3BE), 1881da177e4SLinus Torvalds FORMAT(S20_3LE), 1891da177e4SLinus Torvalds FORMAT(S20_3BE), 1901da177e4SLinus Torvalds FORMAT(U20_3LE), 1911da177e4SLinus Torvalds FORMAT(U20_3BE), 1921da177e4SLinus Torvalds FORMAT(S18_3LE), 1931da177e4SLinus Torvalds FORMAT(S18_3BE), 1941da177e4SLinus Torvalds FORMAT(U18_3LE), 1951da177e4SLinus Torvalds FORMAT(U18_3BE), 1961da177e4SLinus Torvalds }; 1971da177e4SLinus Torvalds 19812831c15SAdrian Bunk static const char *snd_pcm_format_name(snd_pcm_format_t format) 199e28563ccSTakashi Iwai { 200e28563ccSTakashi Iwai return snd_pcm_format_names[format]; 201e28563ccSTakashi Iwai } 202e28563ccSTakashi Iwai 203e28563ccSTakashi Iwai static char *snd_pcm_stream_names[] = { 204e28563ccSTakashi Iwai STREAM(PLAYBACK), 205e28563ccSTakashi Iwai STREAM(CAPTURE), 206e28563ccSTakashi Iwai }; 207e28563ccSTakashi Iwai 208e28563ccSTakashi Iwai static char *snd_pcm_state_names[] = { 209e28563ccSTakashi Iwai STATE(OPEN), 210e28563ccSTakashi Iwai STATE(SETUP), 211e28563ccSTakashi Iwai STATE(PREPARED), 212e28563ccSTakashi Iwai STATE(RUNNING), 213e28563ccSTakashi Iwai STATE(XRUN), 214e28563ccSTakashi Iwai STATE(DRAINING), 215e28563ccSTakashi Iwai STATE(PAUSED), 216e28563ccSTakashi Iwai STATE(SUSPENDED), 217e28563ccSTakashi Iwai }; 218e28563ccSTakashi Iwai 219e28563ccSTakashi Iwai static char *snd_pcm_access_names[] = { 220e28563ccSTakashi Iwai ACCESS(MMAP_INTERLEAVED), 221e28563ccSTakashi Iwai ACCESS(MMAP_NONINTERLEAVED), 222e28563ccSTakashi Iwai ACCESS(MMAP_COMPLEX), 223e28563ccSTakashi Iwai ACCESS(RW_INTERLEAVED), 224e28563ccSTakashi Iwai ACCESS(RW_NONINTERLEAVED), 225e28563ccSTakashi Iwai }; 226e28563ccSTakashi Iwai 2271da177e4SLinus Torvalds static char *snd_pcm_subformat_names[] = { 2281da177e4SLinus Torvalds SUBFORMAT(STD), 2291da177e4SLinus Torvalds }; 2301da177e4SLinus Torvalds 2311da177e4SLinus Torvalds static char *snd_pcm_tstamp_mode_names[] = { 2321da177e4SLinus Torvalds TSTAMP(NONE), 2331da177e4SLinus Torvalds TSTAMP(MMAP), 2341da177e4SLinus Torvalds }; 2351da177e4SLinus Torvalds 236877211f5STakashi Iwai static const char *snd_pcm_stream_name(int stream) 2371da177e4SLinus Torvalds { 2381da177e4SLinus Torvalds snd_assert(stream <= SNDRV_PCM_STREAM_LAST, return NULL); 2391da177e4SLinus Torvalds return snd_pcm_stream_names[stream]; 2401da177e4SLinus Torvalds } 2411da177e4SLinus Torvalds 2421da177e4SLinus Torvalds static const char *snd_pcm_access_name(snd_pcm_access_t access) 2431da177e4SLinus Torvalds { 2441da177e4SLinus Torvalds return snd_pcm_access_names[access]; 2451da177e4SLinus Torvalds } 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat) 2481da177e4SLinus Torvalds { 2491da177e4SLinus Torvalds return snd_pcm_subformat_names[subformat]; 2501da177e4SLinus Torvalds } 2511da177e4SLinus Torvalds 252877211f5STakashi Iwai static const char *snd_pcm_tstamp_mode_name(int mode) 2531da177e4SLinus Torvalds { 2541da177e4SLinus Torvalds snd_assert(mode <= SNDRV_PCM_TSTAMP_LAST, return NULL); 2551da177e4SLinus Torvalds return snd_pcm_tstamp_mode_names[mode]; 2561da177e4SLinus Torvalds } 2571da177e4SLinus Torvalds 2581da177e4SLinus Torvalds static const char *snd_pcm_state_name(snd_pcm_state_t state) 2591da177e4SLinus Torvalds { 2601da177e4SLinus Torvalds return snd_pcm_state_names[state]; 2611da177e4SLinus Torvalds } 2621da177e4SLinus Torvalds 2631da177e4SLinus Torvalds #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) 2641da177e4SLinus Torvalds #include <linux/soundcard.h> 2651a60d4c5SIngo Molnar 2661da177e4SLinus Torvalds static const char *snd_pcm_oss_format_name(int format) 2671da177e4SLinus Torvalds { 2681da177e4SLinus Torvalds switch (format) { 2691da177e4SLinus Torvalds case AFMT_MU_LAW: 2701da177e4SLinus Torvalds return "MU_LAW"; 2711da177e4SLinus Torvalds case AFMT_A_LAW: 2721da177e4SLinus Torvalds return "A_LAW"; 2731da177e4SLinus Torvalds case AFMT_IMA_ADPCM: 2741da177e4SLinus Torvalds return "IMA_ADPCM"; 2751da177e4SLinus Torvalds case AFMT_U8: 2761da177e4SLinus Torvalds return "U8"; 2771da177e4SLinus Torvalds case AFMT_S16_LE: 2781da177e4SLinus Torvalds return "S16_LE"; 2791da177e4SLinus Torvalds case AFMT_S16_BE: 2801da177e4SLinus Torvalds return "S16_BE"; 2811da177e4SLinus Torvalds case AFMT_S8: 2821da177e4SLinus Torvalds return "S8"; 2831da177e4SLinus Torvalds case AFMT_U16_LE: 2841da177e4SLinus Torvalds return "U16_LE"; 2851da177e4SLinus Torvalds case AFMT_U16_BE: 2861da177e4SLinus Torvalds return "U16_BE"; 2871da177e4SLinus Torvalds case AFMT_MPEG: 2881da177e4SLinus Torvalds return "MPEG"; 2891da177e4SLinus Torvalds default: 2901da177e4SLinus Torvalds return "unknown"; 2911da177e4SLinus Torvalds } 2921da177e4SLinus Torvalds } 2931da177e4SLinus Torvalds #endif 2941da177e4SLinus Torvalds 295877211f5STakashi Iwai static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream, 296877211f5STakashi Iwai struct snd_info_buffer *buffer) 2971da177e4SLinus Torvalds { 298877211f5STakashi Iwai struct snd_pcm_info *info; 2991da177e4SLinus Torvalds int err; 3001da177e4SLinus Torvalds 3017c22f1aaSTakashi Iwai if (! substream) 3027c22f1aaSTakashi Iwai return; 3031da177e4SLinus Torvalds 3041da177e4SLinus Torvalds info = kmalloc(sizeof(*info), GFP_KERNEL); 3051da177e4SLinus Torvalds if (! info) { 3061da177e4SLinus Torvalds printk(KERN_DEBUG "snd_pcm_proc_info_read: cannot malloc\n"); 3071da177e4SLinus Torvalds return; 3081da177e4SLinus Torvalds } 3091da177e4SLinus Torvalds 3101da177e4SLinus Torvalds err = snd_pcm_info(substream, info); 3111da177e4SLinus Torvalds if (err < 0) { 3121da177e4SLinus Torvalds snd_iprintf(buffer, "error %d\n", err); 3131da177e4SLinus Torvalds kfree(info); 3141da177e4SLinus Torvalds return; 3151da177e4SLinus Torvalds } 3161da177e4SLinus Torvalds snd_iprintf(buffer, "card: %d\n", info->card); 3171da177e4SLinus Torvalds snd_iprintf(buffer, "device: %d\n", info->device); 3181da177e4SLinus Torvalds snd_iprintf(buffer, "subdevice: %d\n", info->subdevice); 3191da177e4SLinus Torvalds snd_iprintf(buffer, "stream: %s\n", snd_pcm_stream_name(info->stream)); 3201da177e4SLinus Torvalds snd_iprintf(buffer, "id: %s\n", info->id); 3211da177e4SLinus Torvalds snd_iprintf(buffer, "name: %s\n", info->name); 3221da177e4SLinus Torvalds snd_iprintf(buffer, "subname: %s\n", info->subname); 3231da177e4SLinus Torvalds snd_iprintf(buffer, "class: %d\n", info->dev_class); 3241da177e4SLinus Torvalds snd_iprintf(buffer, "subclass: %d\n", info->dev_subclass); 3251da177e4SLinus Torvalds snd_iprintf(buffer, "subdevices_count: %d\n", info->subdevices_count); 3261da177e4SLinus Torvalds snd_iprintf(buffer, "subdevices_avail: %d\n", info->subdevices_avail); 3271da177e4SLinus Torvalds kfree(info); 3281da177e4SLinus Torvalds } 3291da177e4SLinus Torvalds 330877211f5STakashi Iwai static void snd_pcm_stream_proc_info_read(struct snd_info_entry *entry, 331877211f5STakashi Iwai struct snd_info_buffer *buffer) 3321da177e4SLinus Torvalds { 333877211f5STakashi Iwai snd_pcm_proc_info_read(((struct snd_pcm_str *)entry->private_data)->substream, 334877211f5STakashi Iwai buffer); 3351da177e4SLinus Torvalds } 3361da177e4SLinus Torvalds 337877211f5STakashi Iwai static void snd_pcm_substream_proc_info_read(struct snd_info_entry *entry, 338877211f5STakashi Iwai struct snd_info_buffer *buffer) 3391da177e4SLinus Torvalds { 340877211f5STakashi Iwai snd_pcm_proc_info_read((struct snd_pcm_substream *)entry->private_data, 341877211f5STakashi Iwai buffer); 3421da177e4SLinus Torvalds } 3431da177e4SLinus Torvalds 344877211f5STakashi Iwai static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry, 345877211f5STakashi Iwai struct snd_info_buffer *buffer) 3461da177e4SLinus Torvalds { 347877211f5STakashi Iwai struct snd_pcm_substream *substream = entry->private_data; 348877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 3491da177e4SLinus Torvalds if (!runtime) { 3501da177e4SLinus Torvalds snd_iprintf(buffer, "closed\n"); 3511da177e4SLinus Torvalds return; 3521da177e4SLinus Torvalds } 3531da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { 3541da177e4SLinus Torvalds snd_iprintf(buffer, "no setup\n"); 3551da177e4SLinus Torvalds return; 3561da177e4SLinus Torvalds } 3571da177e4SLinus Torvalds snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access)); 3581da177e4SLinus Torvalds snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format)); 3591da177e4SLinus Torvalds snd_iprintf(buffer, "subformat: %s\n", snd_pcm_subformat_name(runtime->subformat)); 3601da177e4SLinus Torvalds snd_iprintf(buffer, "channels: %u\n", runtime->channels); 3611da177e4SLinus Torvalds snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den); 3621da177e4SLinus Torvalds snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size); 3631da177e4SLinus Torvalds snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size); 3641da177e4SLinus Torvalds snd_iprintf(buffer, "tick_time: %u\n", runtime->tick_time); 3651da177e4SLinus Torvalds #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) 3661da177e4SLinus Torvalds if (substream->oss.oss) { 3671da177e4SLinus Torvalds snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format)); 3681da177e4SLinus Torvalds snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels); 3691da177e4SLinus Torvalds snd_iprintf(buffer, "OSS rate: %u\n", runtime->oss.rate); 3701da177e4SLinus Torvalds snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes); 3711da177e4SLinus Torvalds snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods); 3721da177e4SLinus Torvalds snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames); 3731da177e4SLinus Torvalds } 3741da177e4SLinus Torvalds #endif 3751da177e4SLinus Torvalds } 3761da177e4SLinus Torvalds 377877211f5STakashi Iwai static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, 378877211f5STakashi Iwai struct snd_info_buffer *buffer) 3791da177e4SLinus Torvalds { 380877211f5STakashi Iwai struct snd_pcm_substream *substream = entry->private_data; 381877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 3821da177e4SLinus Torvalds if (!runtime) { 3831da177e4SLinus Torvalds snd_iprintf(buffer, "closed\n"); 3841da177e4SLinus Torvalds return; 3851da177e4SLinus Torvalds } 3861da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { 3871da177e4SLinus Torvalds snd_iprintf(buffer, "no setup\n"); 3881da177e4SLinus Torvalds return; 3891da177e4SLinus Torvalds } 3901da177e4SLinus Torvalds snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode)); 3911da177e4SLinus Torvalds snd_iprintf(buffer, "period_step: %u\n", runtime->period_step); 3921da177e4SLinus Torvalds snd_iprintf(buffer, "sleep_min: %u\n", runtime->sleep_min); 3931da177e4SLinus Torvalds snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min); 3941da177e4SLinus Torvalds snd_iprintf(buffer, "xfer_align: %lu\n", runtime->xfer_align); 3951da177e4SLinus Torvalds snd_iprintf(buffer, "start_threshold: %lu\n", runtime->start_threshold); 3961da177e4SLinus Torvalds snd_iprintf(buffer, "stop_threshold: %lu\n", runtime->stop_threshold); 3971da177e4SLinus Torvalds snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold); 3981da177e4SLinus Torvalds snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size); 3991da177e4SLinus Torvalds snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary); 4001da177e4SLinus Torvalds } 4011da177e4SLinus Torvalds 402877211f5STakashi Iwai static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, 403877211f5STakashi Iwai struct snd_info_buffer *buffer) 4041da177e4SLinus Torvalds { 405877211f5STakashi Iwai struct snd_pcm_substream *substream = entry->private_data; 406877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 407877211f5STakashi Iwai struct snd_pcm_status status; 4081da177e4SLinus Torvalds int err; 4091da177e4SLinus Torvalds if (!runtime) { 4101da177e4SLinus Torvalds snd_iprintf(buffer, "closed\n"); 4111da177e4SLinus Torvalds return; 4121da177e4SLinus Torvalds } 4131da177e4SLinus Torvalds memset(&status, 0, sizeof(status)); 4141da177e4SLinus Torvalds err = snd_pcm_status(substream, &status); 4151da177e4SLinus Torvalds if (err < 0) { 4161da177e4SLinus Torvalds snd_iprintf(buffer, "error %d\n", err); 4171da177e4SLinus Torvalds return; 4181da177e4SLinus Torvalds } 4191da177e4SLinus Torvalds snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state)); 4201da177e4SLinus Torvalds snd_iprintf(buffer, "trigger_time: %ld.%09ld\n", 4211da177e4SLinus Torvalds status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec); 4221da177e4SLinus Torvalds snd_iprintf(buffer, "tstamp : %ld.%09ld\n", 4231da177e4SLinus Torvalds status.tstamp.tv_sec, status.tstamp.tv_nsec); 4241da177e4SLinus Torvalds snd_iprintf(buffer, "delay : %ld\n", status.delay); 4251da177e4SLinus Torvalds snd_iprintf(buffer, "avail : %ld\n", status.avail); 4261da177e4SLinus Torvalds snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max); 4271da177e4SLinus Torvalds snd_iprintf(buffer, "-----\n"); 4281da177e4SLinus Torvalds snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr); 4291da177e4SLinus Torvalds snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr); 4301da177e4SLinus Torvalds } 4311da177e4SLinus Torvalds 43261fb63c0SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 433877211f5STakashi Iwai static void snd_pcm_xrun_debug_read(struct snd_info_entry *entry, 434877211f5STakashi Iwai struct snd_info_buffer *buffer) 4351da177e4SLinus Torvalds { 436877211f5STakashi Iwai struct snd_pcm_str *pstr = entry->private_data; 4371da177e4SLinus Torvalds snd_iprintf(buffer, "%d\n", pstr->xrun_debug); 4381da177e4SLinus Torvalds } 4391da177e4SLinus Torvalds 440877211f5STakashi Iwai static void snd_pcm_xrun_debug_write(struct snd_info_entry *entry, 441877211f5STakashi Iwai struct snd_info_buffer *buffer) 4421da177e4SLinus Torvalds { 443877211f5STakashi Iwai struct snd_pcm_str *pstr = entry->private_data; 4441da177e4SLinus Torvalds char line[64]; 4451da177e4SLinus Torvalds if (!snd_info_get_line(buffer, line, sizeof(line))) 4461da177e4SLinus Torvalds pstr->xrun_debug = simple_strtoul(line, NULL, 10); 4471da177e4SLinus Torvalds } 4481da177e4SLinus Torvalds #endif 4491da177e4SLinus Torvalds 450877211f5STakashi Iwai static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) 4511da177e4SLinus Torvalds { 452877211f5STakashi Iwai struct snd_pcm *pcm = pstr->pcm; 453877211f5STakashi Iwai struct snd_info_entry *entry; 4541da177e4SLinus Torvalds char name[16]; 4551da177e4SLinus Torvalds 4561da177e4SLinus Torvalds sprintf(name, "pcm%i%c", pcm->device, 4571da177e4SLinus Torvalds pstr->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); 4581da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(pcm->card, name, pcm->card->proc_root)) == NULL) 4591da177e4SLinus Torvalds return -ENOMEM; 4601da177e4SLinus Torvalds entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; 4611da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 4621da177e4SLinus Torvalds snd_info_free_entry(entry); 4631da177e4SLinus Torvalds return -ENOMEM; 4641da177e4SLinus Torvalds } 4651da177e4SLinus Torvalds pstr->proc_root = entry; 4661da177e4SLinus Torvalds 4671da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root)) != NULL) { 468bf850204STakashi Iwai snd_info_set_text_ops(entry, pstr, snd_pcm_stream_proc_info_read); 4691da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 4701da177e4SLinus Torvalds snd_info_free_entry(entry); 4711da177e4SLinus Torvalds entry = NULL; 4721da177e4SLinus Torvalds } 4731da177e4SLinus Torvalds } 4741da177e4SLinus Torvalds pstr->proc_info_entry = entry; 4751da177e4SLinus Torvalds 47661fb63c0SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 477877211f5STakashi Iwai if ((entry = snd_info_create_card_entry(pcm->card, "xrun_debug", 478877211f5STakashi Iwai pstr->proc_root)) != NULL) { 4791da177e4SLinus Torvalds entry->c.text.read = snd_pcm_xrun_debug_read; 4801da177e4SLinus Torvalds entry->c.text.write = snd_pcm_xrun_debug_write; 481bd7bf042STakashi Iwai entry->mode |= S_IWUSR; 4821da177e4SLinus Torvalds entry->private_data = pstr; 4831da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 4841da177e4SLinus Torvalds snd_info_free_entry(entry); 4851da177e4SLinus Torvalds entry = NULL; 4861da177e4SLinus Torvalds } 4871da177e4SLinus Torvalds } 4881da177e4SLinus Torvalds pstr->proc_xrun_debug_entry = entry; 4891da177e4SLinus Torvalds #endif 4901da177e4SLinus Torvalds return 0; 4911da177e4SLinus Torvalds } 4921da177e4SLinus Torvalds 493877211f5STakashi Iwai static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) 4941da177e4SLinus Torvalds { 49561fb63c0SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 496746d4a02STakashi Iwai snd_info_free_entry(pstr->proc_xrun_debug_entry); 4971da177e4SLinus Torvalds pstr->proc_xrun_debug_entry = NULL; 4981da177e4SLinus Torvalds #endif 499746d4a02STakashi Iwai snd_info_free_entry(pstr->proc_info_entry); 5001da177e4SLinus Torvalds pstr->proc_info_entry = NULL; 501746d4a02STakashi Iwai snd_info_free_entry(pstr->proc_root); 5021da177e4SLinus Torvalds pstr->proc_root = NULL; 5031da177e4SLinus Torvalds return 0; 5041da177e4SLinus Torvalds } 5051da177e4SLinus Torvalds 506877211f5STakashi Iwai static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) 5071da177e4SLinus Torvalds { 508877211f5STakashi Iwai struct snd_info_entry *entry; 509877211f5STakashi Iwai struct snd_card *card; 5101da177e4SLinus Torvalds char name[16]; 5111da177e4SLinus Torvalds 5121da177e4SLinus Torvalds card = substream->pcm->card; 5131da177e4SLinus Torvalds 5141da177e4SLinus Torvalds sprintf(name, "sub%i", substream->number); 5151da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(card, name, substream->pstr->proc_root)) == NULL) 5161da177e4SLinus Torvalds return -ENOMEM; 5171da177e4SLinus Torvalds entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; 5181da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5191da177e4SLinus Torvalds snd_info_free_entry(entry); 5201da177e4SLinus Torvalds return -ENOMEM; 5211da177e4SLinus Torvalds } 5221da177e4SLinus Torvalds substream->proc_root = entry; 5231da177e4SLinus Torvalds 5241da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(card, "info", substream->proc_root)) != NULL) { 525bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 526bf850204STakashi Iwai snd_pcm_substream_proc_info_read); 5271da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5281da177e4SLinus Torvalds snd_info_free_entry(entry); 5291da177e4SLinus Torvalds entry = NULL; 5301da177e4SLinus Torvalds } 5311da177e4SLinus Torvalds } 5321da177e4SLinus Torvalds substream->proc_info_entry = entry; 5331da177e4SLinus Torvalds 5341da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(card, "hw_params", substream->proc_root)) != NULL) { 535bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 536bf850204STakashi Iwai snd_pcm_substream_proc_hw_params_read); 5371da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5381da177e4SLinus Torvalds snd_info_free_entry(entry); 5391da177e4SLinus Torvalds entry = NULL; 5401da177e4SLinus Torvalds } 5411da177e4SLinus Torvalds } 5421da177e4SLinus Torvalds substream->proc_hw_params_entry = entry; 5431da177e4SLinus Torvalds 5441da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(card, "sw_params", substream->proc_root)) != NULL) { 545bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 546bf850204STakashi Iwai snd_pcm_substream_proc_sw_params_read); 5471da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5481da177e4SLinus Torvalds snd_info_free_entry(entry); 5491da177e4SLinus Torvalds entry = NULL; 5501da177e4SLinus Torvalds } 5511da177e4SLinus Torvalds } 5521da177e4SLinus Torvalds substream->proc_sw_params_entry = entry; 5531da177e4SLinus Torvalds 5541da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(card, "status", substream->proc_root)) != NULL) { 555bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 556bf850204STakashi Iwai snd_pcm_substream_proc_status_read); 5571da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5581da177e4SLinus Torvalds snd_info_free_entry(entry); 5591da177e4SLinus Torvalds entry = NULL; 5601da177e4SLinus Torvalds } 5611da177e4SLinus Torvalds } 5621da177e4SLinus Torvalds substream->proc_status_entry = entry; 5631da177e4SLinus Torvalds 5641da177e4SLinus Torvalds return 0; 5651da177e4SLinus Torvalds } 5661da177e4SLinus Torvalds 567877211f5STakashi Iwai static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) 5681da177e4SLinus Torvalds { 569746d4a02STakashi Iwai snd_info_free_entry(substream->proc_info_entry); 5701da177e4SLinus Torvalds substream->proc_info_entry = NULL; 571746d4a02STakashi Iwai snd_info_free_entry(substream->proc_hw_params_entry); 5721da177e4SLinus Torvalds substream->proc_hw_params_entry = NULL; 573746d4a02STakashi Iwai snd_info_free_entry(substream->proc_sw_params_entry); 5741da177e4SLinus Torvalds substream->proc_sw_params_entry = NULL; 575746d4a02STakashi Iwai snd_info_free_entry(substream->proc_status_entry); 5761da177e4SLinus Torvalds substream->proc_status_entry = NULL; 577746d4a02STakashi Iwai snd_info_free_entry(substream->proc_root); 5781da177e4SLinus Torvalds substream->proc_root = NULL; 5791da177e4SLinus Torvalds return 0; 5801da177e4SLinus Torvalds } 581b7d90a35STakashi Iwai #else /* !CONFIG_SND_VERBOSE_PROCFS */ 582e28563ccSTakashi Iwai static inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; } 583e28563ccSTakashi Iwai static inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; } 584e28563ccSTakashi Iwai static inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; } 585e28563ccSTakashi Iwai static inline int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) { return 0; } 586b7d90a35STakashi Iwai #endif /* CONFIG_SND_VERBOSE_PROCFS */ 5871da177e4SLinus Torvalds 5881da177e4SLinus Torvalds /** 5891da177e4SLinus Torvalds * snd_pcm_new_stream - create a new PCM stream 5901da177e4SLinus Torvalds * @pcm: the pcm instance 5911da177e4SLinus Torvalds * @stream: the stream direction, SNDRV_PCM_STREAM_XXX 5921da177e4SLinus Torvalds * @substream_count: the number of substreams 5931da177e4SLinus Torvalds * 5941da177e4SLinus Torvalds * Creates a new stream for the pcm. 5951da177e4SLinus Torvalds * The corresponding stream on the pcm must have been empty before 5961da177e4SLinus Torvalds * calling this, i.e. zero must be given to the argument of 5971da177e4SLinus Torvalds * snd_pcm_new(). 5981da177e4SLinus Torvalds * 5991da177e4SLinus Torvalds * Returns zero if successful, or a negative error code on failure. 6001da177e4SLinus Torvalds */ 601877211f5STakashi Iwai int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) 6021da177e4SLinus Torvalds { 6031da177e4SLinus Torvalds int idx, err; 604877211f5STakashi Iwai struct snd_pcm_str *pstr = &pcm->streams[stream]; 605877211f5STakashi Iwai struct snd_pcm_substream *substream, *prev; 6061da177e4SLinus Torvalds 6071da177e4SLinus Torvalds #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) 6081a60d4c5SIngo Molnar mutex_init(&pstr->oss.setup_mutex); 6091da177e4SLinus Torvalds #endif 6101da177e4SLinus Torvalds pstr->stream = stream; 6111da177e4SLinus Torvalds pstr->pcm = pcm; 6121da177e4SLinus Torvalds pstr->substream_count = substream_count; 6131da177e4SLinus Torvalds if (substream_count > 0) { 6141da177e4SLinus Torvalds err = snd_pcm_stream_proc_init(pstr); 61573e77ba0STakashi Iwai if (err < 0) { 61673e77ba0STakashi Iwai snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n"); 6171da177e4SLinus Torvalds return err; 6181da177e4SLinus Torvalds } 61973e77ba0STakashi Iwai } 6201da177e4SLinus Torvalds prev = NULL; 6211da177e4SLinus Torvalds for (idx = 0, prev = NULL; idx < substream_count; idx++) { 622ca2c0966STakashi Iwai substream = kzalloc(sizeof(*substream), GFP_KERNEL); 62373e77ba0STakashi Iwai if (substream == NULL) { 62473e77ba0STakashi Iwai snd_printk(KERN_ERR "Cannot allocate PCM substream\n"); 6251da177e4SLinus Torvalds return -ENOMEM; 62673e77ba0STakashi Iwai } 6271da177e4SLinus Torvalds substream->pcm = pcm; 6281da177e4SLinus Torvalds substream->pstr = pstr; 6291da177e4SLinus Torvalds substream->number = idx; 6301da177e4SLinus Torvalds substream->stream = stream; 6311da177e4SLinus Torvalds sprintf(substream->name, "subdevice #%i", idx); 6321da177e4SLinus Torvalds substream->buffer_bytes_max = UINT_MAX; 6331da177e4SLinus Torvalds if (prev == NULL) 6341da177e4SLinus Torvalds pstr->substream = substream; 6351da177e4SLinus Torvalds else 6361da177e4SLinus Torvalds prev->next = substream; 6371da177e4SLinus Torvalds err = snd_pcm_substream_proc_init(substream); 6381da177e4SLinus Torvalds if (err < 0) { 63973e77ba0STakashi Iwai snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n"); 6401da177e4SLinus Torvalds kfree(substream); 6411da177e4SLinus Torvalds return err; 6421da177e4SLinus Torvalds } 6431da177e4SLinus Torvalds substream->group = &substream->self_group; 6441da177e4SLinus Torvalds spin_lock_init(&substream->self_group.lock); 6451da177e4SLinus Torvalds INIT_LIST_HEAD(&substream->self_group.substreams); 6461da177e4SLinus Torvalds list_add_tail(&substream->link_list, &substream->self_group.substreams); 6471da177e4SLinus Torvalds spin_lock_init(&substream->timer_lock); 6489c323fcbSTakashi Iwai atomic_set(&substream->mmap_count, 0); 6491da177e4SLinus Torvalds prev = substream; 6501da177e4SLinus Torvalds } 6511da177e4SLinus Torvalds return 0; 6521da177e4SLinus Torvalds } 6531da177e4SLinus Torvalds 654e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_new_stream); 655e88e8ae6STakashi Iwai 6561da177e4SLinus Torvalds /** 6571da177e4SLinus Torvalds * snd_pcm_new - create a new PCM instance 6581da177e4SLinus Torvalds * @card: the card instance 6591da177e4SLinus Torvalds * @id: the id string 6601da177e4SLinus Torvalds * @device: the device index (zero based) 6611da177e4SLinus Torvalds * @playback_count: the number of substreams for playback 6621da177e4SLinus Torvalds * @capture_count: the number of substreams for capture 6631da177e4SLinus Torvalds * @rpcm: the pointer to store the new pcm instance 6641da177e4SLinus Torvalds * 6651da177e4SLinus Torvalds * Creates a new PCM instance. 6661da177e4SLinus Torvalds * 6671da177e4SLinus Torvalds * The pcm operators have to be set afterwards to the new instance 6681da177e4SLinus Torvalds * via snd_pcm_set_ops(). 6691da177e4SLinus Torvalds * 6701da177e4SLinus Torvalds * Returns zero if successful, or a negative error code on failure. 6711da177e4SLinus Torvalds */ 672877211f5STakashi Iwai int snd_pcm_new(struct snd_card *card, char *id, int device, 6731da177e4SLinus Torvalds int playback_count, int capture_count, 674877211f5STakashi Iwai struct snd_pcm ** rpcm) 6751da177e4SLinus Torvalds { 676877211f5STakashi Iwai struct snd_pcm *pcm; 6771da177e4SLinus Torvalds int err; 678877211f5STakashi Iwai static struct snd_device_ops ops = { 6791da177e4SLinus Torvalds .dev_free = snd_pcm_dev_free, 6801da177e4SLinus Torvalds .dev_register = snd_pcm_dev_register, 6811da177e4SLinus Torvalds .dev_disconnect = snd_pcm_dev_disconnect, 6821da177e4SLinus Torvalds }; 6831da177e4SLinus Torvalds 6841da177e4SLinus Torvalds snd_assert(rpcm != NULL, return -EINVAL); 6851da177e4SLinus Torvalds *rpcm = NULL; 6861da177e4SLinus Torvalds snd_assert(card != NULL, return -ENXIO); 687ca2c0966STakashi Iwai pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); 68873e77ba0STakashi Iwai if (pcm == NULL) { 68973e77ba0STakashi Iwai snd_printk(KERN_ERR "Cannot allocate PCM\n"); 6901da177e4SLinus Torvalds return -ENOMEM; 69173e77ba0STakashi Iwai } 6921da177e4SLinus Torvalds pcm->card = card; 6931da177e4SLinus Torvalds pcm->device = device; 69473e77ba0STakashi Iwai if (id) 6951da177e4SLinus Torvalds strlcpy(pcm->id, id, sizeof(pcm->id)); 6961da177e4SLinus Torvalds if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) { 6971da177e4SLinus Torvalds snd_pcm_free(pcm); 6981da177e4SLinus Torvalds return err; 6991da177e4SLinus Torvalds } 7001da177e4SLinus Torvalds if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) { 7011da177e4SLinus Torvalds snd_pcm_free(pcm); 7021da177e4SLinus Torvalds return err; 7031da177e4SLinus Torvalds } 7041a60d4c5SIngo Molnar mutex_init(&pcm->open_mutex); 7051da177e4SLinus Torvalds init_waitqueue_head(&pcm->open_wait); 7061da177e4SLinus Torvalds if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) { 7071da177e4SLinus Torvalds snd_pcm_free(pcm); 7081da177e4SLinus Torvalds return err; 7091da177e4SLinus Torvalds } 7101da177e4SLinus Torvalds *rpcm = pcm; 7111da177e4SLinus Torvalds return 0; 7121da177e4SLinus Torvalds } 7131da177e4SLinus Torvalds 714e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_new); 715e88e8ae6STakashi Iwai 716877211f5STakashi Iwai static void snd_pcm_free_stream(struct snd_pcm_str * pstr) 7171da177e4SLinus Torvalds { 718877211f5STakashi Iwai struct snd_pcm_substream *substream, *substream_next; 7191da177e4SLinus Torvalds #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) 720877211f5STakashi Iwai struct snd_pcm_oss_setup *setup, *setupn; 7211da177e4SLinus Torvalds #endif 7221da177e4SLinus Torvalds substream = pstr->substream; 7231da177e4SLinus Torvalds while (substream) { 7241da177e4SLinus Torvalds substream_next = substream->next; 725c461482cSTakashi Iwai snd_pcm_timer_done(substream); 7261da177e4SLinus Torvalds snd_pcm_substream_proc_done(substream); 7271da177e4SLinus Torvalds kfree(substream); 7281da177e4SLinus Torvalds substream = substream_next; 7291da177e4SLinus Torvalds } 7301da177e4SLinus Torvalds snd_pcm_stream_proc_done(pstr); 7311da177e4SLinus Torvalds #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) 7321da177e4SLinus Torvalds for (setup = pstr->oss.setup_list; setup; setup = setupn) { 7331da177e4SLinus Torvalds setupn = setup->next; 7341da177e4SLinus Torvalds kfree(setup->task_name); 7351da177e4SLinus Torvalds kfree(setup); 7361da177e4SLinus Torvalds } 7371da177e4SLinus Torvalds #endif 7381da177e4SLinus Torvalds } 7391da177e4SLinus Torvalds 740877211f5STakashi Iwai static int snd_pcm_free(struct snd_pcm *pcm) 7411da177e4SLinus Torvalds { 742c461482cSTakashi Iwai struct snd_pcm_notify *notify; 743c461482cSTakashi Iwai 7441da177e4SLinus Torvalds snd_assert(pcm != NULL, return -ENXIO); 745c461482cSTakashi Iwai list_for_each_entry(notify, &snd_pcm_notify_list, list) { 746c461482cSTakashi Iwai notify->n_unregister(pcm); 747c461482cSTakashi Iwai } 7481da177e4SLinus Torvalds if (pcm->private_free) 7491da177e4SLinus Torvalds pcm->private_free(pcm); 7501da177e4SLinus Torvalds snd_pcm_lib_preallocate_free_for_all(pcm); 7511da177e4SLinus Torvalds snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]); 7521da177e4SLinus Torvalds snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]); 7531da177e4SLinus Torvalds kfree(pcm); 7541da177e4SLinus Torvalds return 0; 7551da177e4SLinus Torvalds } 7561da177e4SLinus Torvalds 757877211f5STakashi Iwai static int snd_pcm_dev_free(struct snd_device *device) 7581da177e4SLinus Torvalds { 759877211f5STakashi Iwai struct snd_pcm *pcm = device->device_data; 7601da177e4SLinus Torvalds return snd_pcm_free(pcm); 7611da177e4SLinus Torvalds } 7621da177e4SLinus Torvalds 7631da177e4SLinus Torvalds static void snd_pcm_tick_timer_func(unsigned long data) 7641da177e4SLinus Torvalds { 765877211f5STakashi Iwai struct snd_pcm_substream *substream = (struct snd_pcm_substream *) data; 7661da177e4SLinus Torvalds snd_pcm_tick_elapsed(substream); 7671da177e4SLinus Torvalds } 7681da177e4SLinus Torvalds 7693bf75f9bSTakashi Iwai int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, 7703bf75f9bSTakashi Iwai struct file *file, 771877211f5STakashi Iwai struct snd_pcm_substream **rsubstream) 7721da177e4SLinus Torvalds { 773877211f5STakashi Iwai struct snd_pcm_str * pstr; 774877211f5STakashi Iwai struct snd_pcm_substream *substream; 775877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 776877211f5STakashi Iwai struct snd_ctl_file *kctl; 777877211f5STakashi Iwai struct snd_card *card; 7781da177e4SLinus Torvalds struct list_head *list; 7791da177e4SLinus Torvalds int prefer_subdevice = -1; 7801da177e4SLinus Torvalds size_t size; 7811da177e4SLinus Torvalds 7821da177e4SLinus Torvalds snd_assert(rsubstream != NULL, return -EINVAL); 7831da177e4SLinus Torvalds *rsubstream = NULL; 7841da177e4SLinus Torvalds snd_assert(pcm != NULL, return -ENXIO); 7851da177e4SLinus Torvalds pstr = &pcm->streams[stream]; 7863bf75f9bSTakashi Iwai if (pstr->substream == NULL || pstr->substream_count == 0) 7871da177e4SLinus Torvalds return -ENODEV; 7881da177e4SLinus Torvalds 7891da177e4SLinus Torvalds card = pcm->card; 7901da177e4SLinus Torvalds down_read(&card->controls_rwsem); 7911da177e4SLinus Torvalds list_for_each(list, &card->ctl_files) { 7921da177e4SLinus Torvalds kctl = snd_ctl_file(list); 7931da177e4SLinus Torvalds if (kctl->pid == current->pid) { 7941da177e4SLinus Torvalds prefer_subdevice = kctl->prefer_pcm_subdevice; 7952529bba7STakashi Iwai if (prefer_subdevice != -1) 7961da177e4SLinus Torvalds break; 7971da177e4SLinus Torvalds } 7981da177e4SLinus Torvalds } 7991da177e4SLinus Torvalds up_read(&card->controls_rwsem); 8001da177e4SLinus Torvalds 8011da177e4SLinus Torvalds switch (stream) { 8021da177e4SLinus Torvalds case SNDRV_PCM_STREAM_PLAYBACK: 8031da177e4SLinus Torvalds if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { 8041da177e4SLinus Torvalds for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) { 8051da177e4SLinus Torvalds if (SUBSTREAM_BUSY(substream)) 8061da177e4SLinus Torvalds return -EAGAIN; 8071da177e4SLinus Torvalds } 8081da177e4SLinus Torvalds } 8091da177e4SLinus Torvalds break; 8101da177e4SLinus Torvalds case SNDRV_PCM_STREAM_CAPTURE: 8111da177e4SLinus Torvalds if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { 8121da177e4SLinus Torvalds for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) { 8131da177e4SLinus Torvalds if (SUBSTREAM_BUSY(substream)) 8141da177e4SLinus Torvalds return -EAGAIN; 8151da177e4SLinus Torvalds } 8161da177e4SLinus Torvalds } 8171da177e4SLinus Torvalds break; 8181da177e4SLinus Torvalds default: 8191da177e4SLinus Torvalds return -EINVAL; 8201da177e4SLinus Torvalds } 8211da177e4SLinus Torvalds 8220df63e44STakashi Iwai if (file->f_flags & O_APPEND) { 8230df63e44STakashi Iwai if (prefer_subdevice < 0) { 8240df63e44STakashi Iwai if (pstr->substream_count > 1) 8250df63e44STakashi Iwai return -EINVAL; /* must be unique */ 8260df63e44STakashi Iwai substream = pstr->substream; 8270df63e44STakashi Iwai } else { 8280df63e44STakashi Iwai for (substream = pstr->substream; substream; 8290df63e44STakashi Iwai substream = substream->next) 8300df63e44STakashi Iwai if (substream->number == prefer_subdevice) 8310df63e44STakashi Iwai break; 8320df63e44STakashi Iwai } 8330df63e44STakashi Iwai if (! substream) 8340df63e44STakashi Iwai return -ENODEV; 8350df63e44STakashi Iwai if (! SUBSTREAM_BUSY(substream)) 8360df63e44STakashi Iwai return -EBADFD; 8370df63e44STakashi Iwai substream->ref_count++; 8380df63e44STakashi Iwai *rsubstream = substream; 8390df63e44STakashi Iwai return 0; 8400df63e44STakashi Iwai } 8410df63e44STakashi Iwai 8421da177e4SLinus Torvalds if (prefer_subdevice >= 0) { 8431da177e4SLinus Torvalds for (substream = pstr->substream; substream; substream = substream->next) 8441da177e4SLinus Torvalds if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice) 8451da177e4SLinus Torvalds goto __ok; 8461da177e4SLinus Torvalds } 8471da177e4SLinus Torvalds for (substream = pstr->substream; substream; substream = substream->next) 8481da177e4SLinus Torvalds if (!SUBSTREAM_BUSY(substream)) 8491da177e4SLinus Torvalds break; 8501da177e4SLinus Torvalds __ok: 8511da177e4SLinus Torvalds if (substream == NULL) 8521da177e4SLinus Torvalds return -EAGAIN; 8531da177e4SLinus Torvalds 854ca2c0966STakashi Iwai runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); 8551da177e4SLinus Torvalds if (runtime == NULL) 8561da177e4SLinus Torvalds return -ENOMEM; 8571da177e4SLinus Torvalds 858877211f5STakashi Iwai size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)); 8591da177e4SLinus Torvalds runtime->status = snd_malloc_pages(size, GFP_KERNEL); 8601da177e4SLinus Torvalds if (runtime->status == NULL) { 8611da177e4SLinus Torvalds kfree(runtime); 8621da177e4SLinus Torvalds return -ENOMEM; 8631da177e4SLinus Torvalds } 8641da177e4SLinus Torvalds memset((void*)runtime->status, 0, size); 8651da177e4SLinus Torvalds 866877211f5STakashi Iwai size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)); 8671da177e4SLinus Torvalds runtime->control = snd_malloc_pages(size, GFP_KERNEL); 8681da177e4SLinus Torvalds if (runtime->control == NULL) { 869877211f5STakashi Iwai snd_free_pages((void*)runtime->status, 870877211f5STakashi Iwai PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); 8711da177e4SLinus Torvalds kfree(runtime); 8721da177e4SLinus Torvalds return -ENOMEM; 8731da177e4SLinus Torvalds } 8741da177e4SLinus Torvalds memset((void*)runtime->control, 0, size); 8751da177e4SLinus Torvalds 8761da177e4SLinus Torvalds init_waitqueue_head(&runtime->sleep); 8771da177e4SLinus Torvalds init_timer(&runtime->tick_timer); 8781da177e4SLinus Torvalds runtime->tick_timer.function = snd_pcm_tick_timer_func; 8791da177e4SLinus Torvalds runtime->tick_timer.data = (unsigned long) substream; 8801da177e4SLinus Torvalds 8811da177e4SLinus Torvalds runtime->status->state = SNDRV_PCM_STATE_OPEN; 8821da177e4SLinus Torvalds 8831da177e4SLinus Torvalds substream->runtime = runtime; 8841da177e4SLinus Torvalds substream->private_data = pcm->private_data; 8850df63e44STakashi Iwai substream->ref_count = 1; 8860df63e44STakashi Iwai substream->f_flags = file->f_flags; 8871da177e4SLinus Torvalds pstr->substream_opened++; 8881da177e4SLinus Torvalds *rsubstream = substream; 8891da177e4SLinus Torvalds return 0; 8901da177e4SLinus Torvalds } 8911da177e4SLinus Torvalds 8923bf75f9bSTakashi Iwai void snd_pcm_detach_substream(struct snd_pcm_substream *substream) 8931da177e4SLinus Torvalds { 894877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 8950df63e44STakashi Iwai 8961da177e4SLinus Torvalds runtime = substream->runtime; 8971da177e4SLinus Torvalds snd_assert(runtime != NULL, return); 8981da177e4SLinus Torvalds if (runtime->private_free != NULL) 8991da177e4SLinus Torvalds runtime->private_free(runtime); 900877211f5STakashi Iwai snd_free_pages((void*)runtime->status, 901877211f5STakashi Iwai PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); 902877211f5STakashi Iwai snd_free_pages((void*)runtime->control, 903877211f5STakashi Iwai PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); 9041da177e4SLinus Torvalds kfree(runtime->hw_constraints.rules); 9051da177e4SLinus Torvalds kfree(runtime); 9061da177e4SLinus Torvalds substream->runtime = NULL; 9071da177e4SLinus Torvalds substream->pstr->substream_opened--; 9081da177e4SLinus Torvalds } 9091da177e4SLinus Torvalds 9109d19f48cSTakashi Iwai static ssize_t show_pcm_class(struct class_device *class_device, char *buf) 9119d19f48cSTakashi Iwai { 9129d19f48cSTakashi Iwai struct snd_pcm *pcm; 9139d19f48cSTakashi Iwai const char *str; 9149d19f48cSTakashi Iwai static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = { 9159d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_GENERIC] = "generic", 9169d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_MULTI] = "multi", 9179d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_MODEM] = "modem", 9189d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_DIGITIZER] = "digitizer", 9199d19f48cSTakashi Iwai }; 9209d19f48cSTakashi Iwai 9219d19f48cSTakashi Iwai if (! (pcm = class_get_devdata(class_device)) || 9229d19f48cSTakashi Iwai pcm->dev_class > SNDRV_PCM_CLASS_LAST) 9239d19f48cSTakashi Iwai str = "none"; 9249d19f48cSTakashi Iwai else 9259d19f48cSTakashi Iwai str = strs[pcm->dev_class]; 9269d19f48cSTakashi Iwai return snprintf(buf, PAGE_SIZE, "%s\n", str); 9279d19f48cSTakashi Iwai } 9289d19f48cSTakashi Iwai 9299d19f48cSTakashi Iwai static struct class_device_attribute pcm_attrs = 9309d19f48cSTakashi Iwai __ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL); 9319d19f48cSTakashi Iwai 932877211f5STakashi Iwai static int snd_pcm_dev_register(struct snd_device *device) 9331da177e4SLinus Torvalds { 934f87135f5SClemens Ladisch int cidx, err; 935877211f5STakashi Iwai struct snd_pcm_substream *substream; 9361da177e4SLinus Torvalds struct list_head *list; 9371da177e4SLinus Torvalds char str[16]; 938877211f5STakashi Iwai struct snd_pcm *pcm = device->device_data; 9391da177e4SLinus Torvalds 9401da177e4SLinus Torvalds snd_assert(pcm != NULL && device != NULL, return -ENXIO); 9411a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 942f87135f5SClemens Ladisch if (snd_pcm_search(pcm->card, pcm->device)) { 9431a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 9441da177e4SLinus Torvalds return -EBUSY; 9451da177e4SLinus Torvalds } 946f87135f5SClemens Ladisch list_add_tail(&pcm->list, &snd_pcm_devices); 9471da177e4SLinus Torvalds for (cidx = 0; cidx < 2; cidx++) { 9481da177e4SLinus Torvalds int devtype = -1; 9491da177e4SLinus Torvalds if (pcm->streams[cidx].substream == NULL) 9501da177e4SLinus Torvalds continue; 9511da177e4SLinus Torvalds switch (cidx) { 9521da177e4SLinus Torvalds case SNDRV_PCM_STREAM_PLAYBACK: 9531da177e4SLinus Torvalds sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device); 9541da177e4SLinus Torvalds devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; 9551da177e4SLinus Torvalds break; 9561da177e4SLinus Torvalds case SNDRV_PCM_STREAM_CAPTURE: 9571da177e4SLinus Torvalds sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device); 9581da177e4SLinus Torvalds devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; 9591da177e4SLinus Torvalds break; 9601da177e4SLinus Torvalds } 9612af677fcSClemens Ladisch if ((err = snd_register_device(devtype, pcm->card, 9622af677fcSClemens Ladisch pcm->device, 963f87135f5SClemens Ladisch &snd_pcm_f_ops[cidx], 964f87135f5SClemens Ladisch pcm, str)) < 0) 9652af677fcSClemens Ladisch { 966f87135f5SClemens Ladisch list_del(&pcm->list); 9671a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 9681da177e4SLinus Torvalds return err; 9691da177e4SLinus Torvalds } 9709d19f48cSTakashi Iwai snd_add_device_sysfs_file(devtype, pcm->card, pcm->device, 9719d19f48cSTakashi Iwai &pcm_attrs); 9721da177e4SLinus Torvalds for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) 9731da177e4SLinus Torvalds snd_pcm_timer_init(substream); 9741da177e4SLinus Torvalds } 9751da177e4SLinus Torvalds list_for_each(list, &snd_pcm_notify_list) { 976877211f5STakashi Iwai struct snd_pcm_notify *notify; 977877211f5STakashi Iwai notify = list_entry(list, struct snd_pcm_notify, list); 9781da177e4SLinus Torvalds notify->n_register(pcm); 9791da177e4SLinus Torvalds } 9801a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 9811da177e4SLinus Torvalds return 0; 9821da177e4SLinus Torvalds } 9831da177e4SLinus Torvalds 984877211f5STakashi Iwai static int snd_pcm_dev_disconnect(struct snd_device *device) 9851da177e4SLinus Torvalds { 986877211f5STakashi Iwai struct snd_pcm *pcm = device->device_data; 987c461482cSTakashi Iwai struct snd_pcm_notify *notify; 988877211f5STakashi Iwai struct snd_pcm_substream *substream; 989c461482cSTakashi Iwai int cidx, devtype; 9901da177e4SLinus Torvalds 9911a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 992c461482cSTakashi Iwai if (list_empty(&pcm->list)) 993c461482cSTakashi Iwai goto unlock; 994c461482cSTakashi Iwai 995f87135f5SClemens Ladisch list_del_init(&pcm->list); 9961da177e4SLinus Torvalds for (cidx = 0; cidx < 2; cidx++) 9971da177e4SLinus Torvalds for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) 9981da177e4SLinus Torvalds if (substream->runtime) 9991da177e4SLinus Torvalds substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; 1000c461482cSTakashi Iwai list_for_each_entry(notify, &snd_pcm_notify_list, list) { 10011da177e4SLinus Torvalds notify->n_disconnect(pcm); 10021da177e4SLinus Torvalds } 10031da177e4SLinus Torvalds for (cidx = 0; cidx < 2; cidx++) { 10041da177e4SLinus Torvalds devtype = -1; 10051da177e4SLinus Torvalds switch (cidx) { 10061da177e4SLinus Torvalds case SNDRV_PCM_STREAM_PLAYBACK: 10071da177e4SLinus Torvalds devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; 10081da177e4SLinus Torvalds break; 10091da177e4SLinus Torvalds case SNDRV_PCM_STREAM_CAPTURE: 10101da177e4SLinus Torvalds devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; 10111da177e4SLinus Torvalds break; 10121da177e4SLinus Torvalds } 10131da177e4SLinus Torvalds snd_unregister_device(devtype, pcm->card, pcm->device); 10141da177e4SLinus Torvalds } 1015c461482cSTakashi Iwai unlock: 10161a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 1017c461482cSTakashi Iwai return 0; 10181da177e4SLinus Torvalds } 10191da177e4SLinus Torvalds 1020877211f5STakashi Iwai int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) 10211da177e4SLinus Torvalds { 1022f87135f5SClemens Ladisch struct list_head *p; 10231da177e4SLinus Torvalds 1024c461482cSTakashi Iwai snd_assert(notify != NULL && 1025c461482cSTakashi Iwai notify->n_register != NULL && 1026c461482cSTakashi Iwai notify->n_unregister != NULL && 1027c461482cSTakashi Iwai notify->n_disconnect, return -EINVAL); 10281a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 10291da177e4SLinus Torvalds if (nfree) { 10301da177e4SLinus Torvalds list_del(¬ify->list); 1031f87135f5SClemens Ladisch list_for_each(p, &snd_pcm_devices) 1032f87135f5SClemens Ladisch notify->n_unregister(list_entry(p, 1033f87135f5SClemens Ladisch struct snd_pcm, list)); 10341da177e4SLinus Torvalds } else { 10351da177e4SLinus Torvalds list_add_tail(¬ify->list, &snd_pcm_notify_list); 1036f87135f5SClemens Ladisch list_for_each(p, &snd_pcm_devices) 1037f87135f5SClemens Ladisch notify->n_register(list_entry(p, struct snd_pcm, list)); 10381da177e4SLinus Torvalds } 10391a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 10401da177e4SLinus Torvalds return 0; 10411da177e4SLinus Torvalds } 10421da177e4SLinus Torvalds 1043e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_notify); 1044e88e8ae6STakashi Iwai 1045e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS 10461da177e4SLinus Torvalds /* 10471da177e4SLinus Torvalds * Info interface 10481da177e4SLinus Torvalds */ 10491da177e4SLinus Torvalds 1050877211f5STakashi Iwai static void snd_pcm_proc_read(struct snd_info_entry *entry, 1051877211f5STakashi Iwai struct snd_info_buffer *buffer) 10521da177e4SLinus Torvalds { 1053f87135f5SClemens Ladisch struct list_head *p; 1054877211f5STakashi Iwai struct snd_pcm *pcm; 10551da177e4SLinus Torvalds 10561a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 1057f87135f5SClemens Ladisch list_for_each(p, &snd_pcm_devices) { 1058f87135f5SClemens Ladisch pcm = list_entry(p, struct snd_pcm, list); 1059f87135f5SClemens Ladisch snd_iprintf(buffer, "%02i-%02i: %s : %s", 1060f87135f5SClemens Ladisch pcm->card->number, pcm->device, pcm->id, pcm->name); 10611da177e4SLinus Torvalds if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) 1062877211f5STakashi Iwai snd_iprintf(buffer, " : playback %i", 1063877211f5STakashi Iwai pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count); 10641da177e4SLinus Torvalds if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) 1065877211f5STakashi Iwai snd_iprintf(buffer, " : capture %i", 1066877211f5STakashi Iwai pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count); 10671da177e4SLinus Torvalds snd_iprintf(buffer, "\n"); 10681da177e4SLinus Torvalds } 10691a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 10701da177e4SLinus Torvalds } 10711da177e4SLinus Torvalds 10726581f4e7STakashi Iwai static struct snd_info_entry *snd_pcm_proc_entry; 10731da177e4SLinus Torvalds 1074e28563ccSTakashi Iwai static void snd_pcm_proc_init(void) 10751da177e4SLinus Torvalds { 1076877211f5STakashi Iwai struct snd_info_entry *entry; 10771da177e4SLinus Torvalds 10781da177e4SLinus Torvalds if ((entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL)) != NULL) { 1079bf850204STakashi Iwai snd_info_set_text_ops(entry, NULL, snd_pcm_proc_read); 10801da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 10811da177e4SLinus Torvalds snd_info_free_entry(entry); 10821da177e4SLinus Torvalds entry = NULL; 10831da177e4SLinus Torvalds } 10841da177e4SLinus Torvalds } 10851da177e4SLinus Torvalds snd_pcm_proc_entry = entry; 1086e28563ccSTakashi Iwai } 1087e28563ccSTakashi Iwai 1088e28563ccSTakashi Iwai static void snd_pcm_proc_done(void) 1089e28563ccSTakashi Iwai { 1090746d4a02STakashi Iwai snd_info_free_entry(snd_pcm_proc_entry); 1091e28563ccSTakashi Iwai } 1092e28563ccSTakashi Iwai 1093e28563ccSTakashi Iwai #else /* !CONFIG_PROC_FS */ 1094e28563ccSTakashi Iwai #define snd_pcm_proc_init() 1095e28563ccSTakashi Iwai #define snd_pcm_proc_done() 1096e28563ccSTakashi Iwai #endif /* CONFIG_PROC_FS */ 1097e28563ccSTakashi Iwai 1098e28563ccSTakashi Iwai 1099e28563ccSTakashi Iwai /* 1100e28563ccSTakashi Iwai * ENTRY functions 1101e28563ccSTakashi Iwai */ 1102e28563ccSTakashi Iwai 1103e28563ccSTakashi Iwai static int __init alsa_pcm_init(void) 1104e28563ccSTakashi Iwai { 1105e28563ccSTakashi Iwai snd_ctl_register_ioctl(snd_pcm_control_ioctl); 1106e28563ccSTakashi Iwai snd_ctl_register_ioctl_compat(snd_pcm_control_ioctl); 1107e28563ccSTakashi Iwai snd_pcm_proc_init(); 11081da177e4SLinus Torvalds return 0; 11091da177e4SLinus Torvalds } 11101da177e4SLinus Torvalds 11111da177e4SLinus Torvalds static void __exit alsa_pcm_exit(void) 11121da177e4SLinus Torvalds { 11131da177e4SLinus Torvalds snd_ctl_unregister_ioctl(snd_pcm_control_ioctl); 11141da177e4SLinus Torvalds snd_ctl_unregister_ioctl_compat(snd_pcm_control_ioctl); 1115e28563ccSTakashi Iwai snd_pcm_proc_done(); 11161da177e4SLinus Torvalds } 11171da177e4SLinus Torvalds 11181da177e4SLinus Torvalds module_init(alsa_pcm_init) 11191da177e4SLinus Torvalds module_exit(alsa_pcm_exit) 1120