blob: 24acbfb5c53160fac850b05ece6af94caa0842c9 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Digital Audio (PCM) abstract layer
Jaroslav Kyselac1017a42007-10-15 09:50:19 +02003 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/init.h>
23#include <linux/slab.h>
Paul Gortmakerda155d52011-07-15 12:38:28 -040024#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#include <linux/time.h>
Ingo Molnar1a60d4c2006-01-16 16:29:08 +010026#include <linux/mutex.h>
Paul Gortmaker51990e82012-01-22 11:23:42 -050027#include <linux/device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <sound/core.h>
29#include <sound/minors.h>
30#include <sound/pcm.h>
31#include <sound/control.h>
32#include <sound/info.h>
33
Jaroslav Kyselac1017a42007-10-15 09:50:19 +020034MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>");
Linus Torvalds1da177e2005-04-16 15:20:36 -070035MODULE_DESCRIPTION("Midlevel PCM code for ALSA.");
36MODULE_LICENSE("GPL");
37
Clemens Ladischf87135f2005-11-20 14:06:59 +010038static LIST_HEAD(snd_pcm_devices);
Ingo Molnar1a60d4c2006-01-16 16:29:08 +010039static DEFINE_MUTEX(register_mutex);
Takashi Iwai58f30d62017-05-12 11:35:17 +020040#if IS_ENABLED(CONFIG_SND_PCM_OSS)
41static LIST_HEAD(snd_pcm_notify_list);
42#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
Takashi Iwai877211f2005-11-17 13:59:38 +010044static int snd_pcm_free(struct snd_pcm *pcm);
45static int snd_pcm_dev_free(struct snd_device *device);
46static int snd_pcm_dev_register(struct snd_device *device);
47static int snd_pcm_dev_disconnect(struct snd_device *device);
Linus Torvalds1da177e2005-04-16 15:20:36 -070048
Pawel MOLLf90c06a2008-07-30 12:46:40 +010049static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device)
Clemens Ladischf87135f2005-11-20 14:06:59 +010050{
Clemens Ladischf87135f2005-11-20 14:06:59 +010051 struct snd_pcm *pcm;
52
Johannes Berg9244b2c2006-10-05 16:02:22 +020053 list_for_each_entry(pcm, &snd_pcm_devices, list) {
Clemens Ladischf87135f2005-11-20 14:06:59 +010054 if (pcm->card == card && pcm->device == device)
55 return pcm;
56 }
57 return NULL;
58}
59
Pawel MOLLf90c06a2008-07-30 12:46:40 +010060static int snd_pcm_next(struct snd_card *card, int device)
61{
62 struct snd_pcm *pcm;
63
64 list_for_each_entry(pcm, &snd_pcm_devices, list) {
65 if (pcm->card == card && pcm->device > device)
66 return pcm->device;
67 else if (pcm->card->number > card->number)
68 return -1;
69 }
70 return -1;
71}
72
73static int snd_pcm_add(struct snd_pcm *newpcm)
74{
75 struct snd_pcm *pcm;
76
Takashi Iwaib95bd3a2015-02-20 16:49:04 +010077 if (newpcm->internal)
78 return 0;
79
Pawel MOLLf90c06a2008-07-30 12:46:40 +010080 list_for_each_entry(pcm, &snd_pcm_devices, list) {
81 if (pcm->card == newpcm->card && pcm->device == newpcm->device)
82 return -EBUSY;
83 if (pcm->card->number > newpcm->card->number ||
84 (pcm->card == newpcm->card &&
85 pcm->device > newpcm->device)) {
86 list_add(&newpcm->list, pcm->list.prev);
87 return 0;
88 }
89 }
90 list_add_tail(&newpcm->list, &snd_pcm_devices);
91 return 0;
92}
93
Takashi Iwai877211f2005-11-17 13:59:38 +010094static int snd_pcm_control_ioctl(struct snd_card *card,
95 struct snd_ctl_file *control,
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 unsigned int cmd, unsigned long arg)
97{
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 switch (cmd) {
99 case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE:
100 {
101 int device;
102
103 if (get_user(device, (int __user *)arg))
104 return -EFAULT;
Ingo Molnar1a60d4c2006-01-16 16:29:08 +0100105 mutex_lock(&register_mutex);
Pawel MOLLf90c06a2008-07-30 12:46:40 +0100106 device = snd_pcm_next(card, device);
Ingo Molnar1a60d4c2006-01-16 16:29:08 +0100107 mutex_unlock(&register_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 if (put_user(device, (int __user *)arg))
109 return -EFAULT;
110 return 0;
111 }
112 case SNDRV_CTL_IOCTL_PCM_INFO:
113 {
Takashi Iwai877211f2005-11-17 13:59:38 +0100114 struct snd_pcm_info __user *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 unsigned int device, subdevice;
Takashi Iwai877211f2005-11-17 13:59:38 +0100116 int stream;
117 struct snd_pcm *pcm;
118 struct snd_pcm_str *pstr;
119 struct snd_pcm_substream *substream;
Clemens Ladischf87135f2005-11-20 14:06:59 +0100120 int err;
121
Takashi Iwai877211f2005-11-17 13:59:38 +0100122 info = (struct snd_pcm_info __user *)arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 if (get_user(device, &info->device))
124 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 if (get_user(stream, &info->stream))
126 return -EFAULT;
127 if (stream < 0 || stream > 1)
128 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 if (get_user(subdevice, &info->subdevice))
130 return -EFAULT;
Ingo Molnar1a60d4c2006-01-16 16:29:08 +0100131 mutex_lock(&register_mutex);
Pawel MOLLf90c06a2008-07-30 12:46:40 +0100132 pcm = snd_pcm_get(card, device);
Clemens Ladischf87135f2005-11-20 14:06:59 +0100133 if (pcm == NULL) {
134 err = -ENXIO;
135 goto _error;
136 }
137 pstr = &pcm->streams[stream];
138 if (pstr->substream_count == 0) {
139 err = -ENOENT;
140 goto _error;
141 }
142 if (subdevice >= pstr->substream_count) {
143 err = -ENXIO;
144 goto _error;
145 }
146 for (substream = pstr->substream; substream;
147 substream = substream->next)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 if (substream->number == (int)subdevice)
149 break;
Clemens Ladischf87135f2005-11-20 14:06:59 +0100150 if (substream == NULL) {
151 err = -ENXIO;
152 goto _error;
153 }
154 err = snd_pcm_info_user(substream, info);
155 _error:
Ingo Molnar1a60d4c2006-01-16 16:29:08 +0100156 mutex_unlock(&register_mutex);
Clemens Ladischf87135f2005-11-20 14:06:59 +0100157 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 }
159 case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE:
160 {
161 int val;
162
163 if (get_user(val, (int __user *)arg))
164 return -EFAULT;
Takashi Iwai23c18d42014-02-19 14:30:29 +0100165 control->preferred_subdevice[SND_CTL_SUBDEV_PCM] = val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 return 0;
167 }
168 }
169 return -ENOIOCTLCMD;
170}
Jaroslav Kysela21a34792006-01-13 09:12:11 +0100171
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172#define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174static char *snd_pcm_format_names[] = {
175 FORMAT(S8),
176 FORMAT(U8),
177 FORMAT(S16_LE),
178 FORMAT(S16_BE),
179 FORMAT(U16_LE),
180 FORMAT(U16_BE),
181 FORMAT(S24_LE),
182 FORMAT(S24_BE),
183 FORMAT(U24_LE),
184 FORMAT(U24_BE),
185 FORMAT(S32_LE),
186 FORMAT(S32_BE),
187 FORMAT(U32_LE),
188 FORMAT(U32_BE),
189 FORMAT(FLOAT_LE),
190 FORMAT(FLOAT_BE),
191 FORMAT(FLOAT64_LE),
192 FORMAT(FLOAT64_BE),
193 FORMAT(IEC958_SUBFRAME_LE),
194 FORMAT(IEC958_SUBFRAME_BE),
195 FORMAT(MU_LAW),
196 FORMAT(A_LAW),
197 FORMAT(IMA_ADPCM),
198 FORMAT(MPEG),
199 FORMAT(GSM),
200 FORMAT(SPECIAL),
201 FORMAT(S24_3LE),
202 FORMAT(S24_3BE),
203 FORMAT(U24_3LE),
204 FORMAT(U24_3BE),
205 FORMAT(S20_3LE),
206 FORMAT(S20_3BE),
207 FORMAT(U20_3LE),
208 FORMAT(U20_3BE),
209 FORMAT(S18_3LE),
210 FORMAT(S18_3BE),
211 FORMAT(U18_3LE),
212 FORMAT(U18_3BE),
Dan Carpenter7a288262010-08-27 22:02:15 +0200213 FORMAT(G723_24),
214 FORMAT(G723_24_1B),
215 FORMAT(G723_40),
216 FORMAT(G723_40_1B),
Daniel Mackef7a4f92013-04-17 00:01:36 +0800217 FORMAT(DSD_U8),
218 FORMAT(DSD_U16_LE),
Jurgen Kramerd4288d32014-09-05 10:47:56 +0200219 FORMAT(DSD_U32_LE),
Jussi Laakod42472e2014-11-21 16:04:46 +0200220 FORMAT(DSD_U16_BE),
221 FORMAT(DSD_U32_BE),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222};
223
Takashi Iwai30b771c2014-10-30 15:02:50 +0100224/**
225 * snd_pcm_format_name - Return a name string for the given PCM format
226 * @format: PCM format
227 */
Takashi Iwai6e5265e2009-09-08 14:26:51 +0200228const char *snd_pcm_format_name(snd_pcm_format_t format)
Takashi Iwaie28563c2005-12-01 10:42:42 +0100229{
Clemens Ladischfea952e2011-02-14 11:00:47 +0100230 if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names))
Dan Carpenter7a288262010-08-27 22:02:15 +0200231 return "Unknown";
Clemens Ladischfea952e2011-02-14 11:00:47 +0100232 return snd_pcm_format_names[(__force unsigned int)format];
Takashi Iwaie28563c2005-12-01 10:42:42 +0100233}
Takashi Iwai6e5265e2009-09-08 14:26:51 +0200234EXPORT_SYMBOL_GPL(snd_pcm_format_name);
235
236#ifdef CONFIG_SND_VERBOSE_PROCFS
237
238#define STATE(v) [SNDRV_PCM_STATE_##v] = #v
239#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v
240#define READY(v) [SNDRV_PCM_READY_##v] = #v
241#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v
242#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v
243#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v
244#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v
245#define START(v) [SNDRV_PCM_START_##v] = #v
246#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v
Takashi Iwaie28563c2005-12-01 10:42:42 +0100247
Takashi Iwaie28563c2005-12-01 10:42:42 +0100248static char *snd_pcm_stream_names[] = {
249 STREAM(PLAYBACK),
250 STREAM(CAPTURE),
251};
252
253static char *snd_pcm_state_names[] = {
254 STATE(OPEN),
255 STATE(SETUP),
256 STATE(PREPARED),
257 STATE(RUNNING),
258 STATE(XRUN),
259 STATE(DRAINING),
260 STATE(PAUSED),
261 STATE(SUSPENDED),
262};
263
264static char *snd_pcm_access_names[] = {
265 ACCESS(MMAP_INTERLEAVED),
266 ACCESS(MMAP_NONINTERLEAVED),
267 ACCESS(MMAP_COMPLEX),
268 ACCESS(RW_INTERLEAVED),
269 ACCESS(RW_NONINTERLEAVED),
270};
271
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272static char *snd_pcm_subformat_names[] = {
273 SUBFORMAT(STD),
274};
275
276static char *snd_pcm_tstamp_mode_names[] = {
277 TSTAMP(NONE),
Jaroslav Kysela8c121582008-01-11 08:45:08 +0100278 TSTAMP(ENABLE),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279};
280
Takashi Iwai877211f2005-11-17 13:59:38 +0100281static const char *snd_pcm_stream_name(int stream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 return snd_pcm_stream_names[stream];
284}
285
286static const char *snd_pcm_access_name(snd_pcm_access_t access)
287{
Clemens Ladischfea952e2011-02-14 11:00:47 +0100288 return snd_pcm_access_names[(__force int)access];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289}
290
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat)
292{
Clemens Ladischfea952e2011-02-14 11:00:47 +0100293 return snd_pcm_subformat_names[(__force int)subformat];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294}
295
Takashi Iwai877211f2005-11-17 13:59:38 +0100296static const char *snd_pcm_tstamp_mode_name(int mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 return snd_pcm_tstamp_mode_names[mode];
299}
300
301static const char *snd_pcm_state_name(snd_pcm_state_t state)
302{
Clemens Ladischfea952e2011-02-14 11:00:47 +0100303 return snd_pcm_state_names[(__force int)state];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304}
305
Takashi Iwai8eeaa2f2014-02-10 09:48:47 +0100306#if IS_ENABLED(CONFIG_SND_PCM_OSS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307#include <linux/soundcard.h>
Ingo Molnar1a60d4c2006-01-16 16:29:08 +0100308
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309static const char *snd_pcm_oss_format_name(int format)
310{
311 switch (format) {
312 case AFMT_MU_LAW:
313 return "MU_LAW";
314 case AFMT_A_LAW:
315 return "A_LAW";
316 case AFMT_IMA_ADPCM:
317 return "IMA_ADPCM";
318 case AFMT_U8:
319 return "U8";
320 case AFMT_S16_LE:
321 return "S16_LE";
322 case AFMT_S16_BE:
323 return "S16_BE";
324 case AFMT_S8:
325 return "S8";
326 case AFMT_U16_LE:
327 return "U16_LE";
328 case AFMT_U16_BE:
329 return "U16_BE";
330 case AFMT_MPEG:
331 return "MPEG";
332 default:
333 return "unknown";
334 }
335}
336#endif
337
Takashi Iwai877211f2005-11-17 13:59:38 +0100338static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream,
339 struct snd_info_buffer *buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340{
Takashi Iwai877211f2005-11-17 13:59:38 +0100341 struct snd_pcm_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 int err;
343
Takashi Iwai7c22f1a2005-10-10 11:46:31 +0200344 if (! substream)
345 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346
347 info = kmalloc(sizeof(*info), GFP_KERNEL);
Takashi Iwaiec0e9932015-03-10 15:42:14 +0100348 if (!info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350
351 err = snd_pcm_info(substream, info);
352 if (err < 0) {
353 snd_iprintf(buffer, "error %d\n", err);
354 kfree(info);
355 return;
356 }
357 snd_iprintf(buffer, "card: %d\n", info->card);
358 snd_iprintf(buffer, "device: %d\n", info->device);
359 snd_iprintf(buffer, "subdevice: %d\n", info->subdevice);
360 snd_iprintf(buffer, "stream: %s\n", snd_pcm_stream_name(info->stream));
361 snd_iprintf(buffer, "id: %s\n", info->id);
362 snd_iprintf(buffer, "name: %s\n", info->name);
363 snd_iprintf(buffer, "subname: %s\n", info->subname);
364 snd_iprintf(buffer, "class: %d\n", info->dev_class);
365 snd_iprintf(buffer, "subclass: %d\n", info->dev_subclass);
366 snd_iprintf(buffer, "subdevices_count: %d\n", info->subdevices_count);
367 snd_iprintf(buffer, "subdevices_avail: %d\n", info->subdevices_avail);
368 kfree(info);
369}
370
Takashi Iwai877211f2005-11-17 13:59:38 +0100371static void snd_pcm_stream_proc_info_read(struct snd_info_entry *entry,
372 struct snd_info_buffer *buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373{
Takashi Iwai877211f2005-11-17 13:59:38 +0100374 snd_pcm_proc_info_read(((struct snd_pcm_str *)entry->private_data)->substream,
375 buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376}
377
Takashi Iwai877211f2005-11-17 13:59:38 +0100378static void snd_pcm_substream_proc_info_read(struct snd_info_entry *entry,
379 struct snd_info_buffer *buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380{
Joe Perches9fe856e2010-09-04 18:52:54 -0700381 snd_pcm_proc_info_read(entry->private_data, buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382}
383
Takashi Iwai877211f2005-11-17 13:59:38 +0100384static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry,
385 struct snd_info_buffer *buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386{
Takashi Iwai877211f2005-11-17 13:59:38 +0100387 struct snd_pcm_substream *substream = entry->private_data;
Takashi Iwai901d46d2010-09-16 23:06:50 +0200388 struct snd_pcm_runtime *runtime;
389
390 mutex_lock(&substream->pcm->open_mutex);
391 runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 if (!runtime) {
393 snd_iprintf(buffer, "closed\n");
Takashi Iwai901d46d2010-09-16 23:06:50 +0200394 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
397 snd_iprintf(buffer, "no setup\n");
Takashi Iwai901d46d2010-09-16 23:06:50 +0200398 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 }
400 snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access));
401 snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format));
402 snd_iprintf(buffer, "subformat: %s\n", snd_pcm_subformat_name(runtime->subformat));
403 snd_iprintf(buffer, "channels: %u\n", runtime->channels);
404 snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den);
405 snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size);
406 snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size);
Takashi Iwai8eeaa2f2014-02-10 09:48:47 +0100407#if IS_ENABLED(CONFIG_SND_PCM_OSS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 if (substream->oss.oss) {
409 snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format));
410 snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels);
411 snd_iprintf(buffer, "OSS rate: %u\n", runtime->oss.rate);
412 snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes);
413 snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods);
414 snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames);
415 }
416#endif
Takashi Iwai901d46d2010-09-16 23:06:50 +0200417 unlock:
418 mutex_unlock(&substream->pcm->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419}
420
Takashi Iwai877211f2005-11-17 13:59:38 +0100421static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry,
422 struct snd_info_buffer *buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423{
Takashi Iwai877211f2005-11-17 13:59:38 +0100424 struct snd_pcm_substream *substream = entry->private_data;
Takashi Iwai901d46d2010-09-16 23:06:50 +0200425 struct snd_pcm_runtime *runtime;
426
427 mutex_lock(&substream->pcm->open_mutex);
428 runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 if (!runtime) {
430 snd_iprintf(buffer, "closed\n");
Takashi Iwai901d46d2010-09-16 23:06:50 +0200431 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {
434 snd_iprintf(buffer, "no setup\n");
Takashi Iwai901d46d2010-09-16 23:06:50 +0200435 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 }
437 snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode));
438 snd_iprintf(buffer, "period_step: %u\n", runtime->period_step);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 snd_iprintf(buffer, "start_threshold: %lu\n", runtime->start_threshold);
441 snd_iprintf(buffer, "stop_threshold: %lu\n", runtime->stop_threshold);
442 snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold);
443 snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size);
444 snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary);
Takashi Iwai901d46d2010-09-16 23:06:50 +0200445 unlock:
446 mutex_unlock(&substream->pcm->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447}
448
Takashi Iwai877211f2005-11-17 13:59:38 +0100449static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry,
450 struct snd_info_buffer *buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451{
Takashi Iwai877211f2005-11-17 13:59:38 +0100452 struct snd_pcm_substream *substream = entry->private_data;
Takashi Iwai901d46d2010-09-16 23:06:50 +0200453 struct snd_pcm_runtime *runtime;
Takashi Iwai877211f2005-11-17 13:59:38 +0100454 struct snd_pcm_status status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 int err;
Takashi Iwai901d46d2010-09-16 23:06:50 +0200456
457 mutex_lock(&substream->pcm->open_mutex);
458 runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 if (!runtime) {
460 snd_iprintf(buffer, "closed\n");
Takashi Iwai901d46d2010-09-16 23:06:50 +0200461 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 }
463 memset(&status, 0, sizeof(status));
464 err = snd_pcm_status(substream, &status);
465 if (err < 0) {
466 snd_iprintf(buffer, "error %d\n", err);
Takashi Iwai901d46d2010-09-16 23:06:50 +0200467 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 }
469 snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state));
Clemens Ladische7373b72009-11-10 10:13:30 +0100470 snd_iprintf(buffer, "owner_pid : %d\n", pid_vnr(substream->pid));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 snd_iprintf(buffer, "trigger_time: %ld.%09ld\n",
472 status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec);
473 snd_iprintf(buffer, "tstamp : %ld.%09ld\n",
474 status.tstamp.tv_sec, status.tstamp.tv_nsec);
475 snd_iprintf(buffer, "delay : %ld\n", status.delay);
476 snd_iprintf(buffer, "avail : %ld\n", status.avail);
477 snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max);
478 snd_iprintf(buffer, "-----\n");
479 snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr);
480 snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr);
Takashi Iwai901d46d2010-09-16 23:06:50 +0200481 unlock:
482 mutex_unlock(&substream->pcm->open_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484
Jaroslav Kysela61fb63c2006-04-24 21:57:16 +0200485#ifdef CONFIG_SND_PCM_XRUN_DEBUG
Takashi Iwai2b30d412014-11-04 14:02:40 +0100486static void snd_pcm_xrun_injection_write(struct snd_info_entry *entry,
487 struct snd_info_buffer *buffer)
488{
489 struct snd_pcm_substream *substream = entry->private_data;
490 struct snd_pcm_runtime *runtime;
491
492 snd_pcm_stream_lock_irq(substream);
493 runtime = substream->runtime;
494 if (runtime && runtime->status->state == SNDRV_PCM_STATE_RUNNING)
495 snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
496 snd_pcm_stream_unlock_irq(substream);
497}
498
Takashi Iwai877211f2005-11-17 13:59:38 +0100499static void snd_pcm_xrun_debug_read(struct snd_info_entry *entry,
500 struct snd_info_buffer *buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501{
Takashi Iwai877211f2005-11-17 13:59:38 +0100502 struct snd_pcm_str *pstr = entry->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 snd_iprintf(buffer, "%d\n", pstr->xrun_debug);
504}
505
Takashi Iwai877211f2005-11-17 13:59:38 +0100506static void snd_pcm_xrun_debug_write(struct snd_info_entry *entry,
507 struct snd_info_buffer *buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508{
Takashi Iwai877211f2005-11-17 13:59:38 +0100509 struct snd_pcm_str *pstr = entry->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 char line[64];
511 if (!snd_info_get_line(buffer, line, sizeof(line)))
512 pstr->xrun_debug = simple_strtoul(line, NULL, 10);
513}
514#endif
515
Takashi Iwai877211f2005-11-17 13:59:38 +0100516static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517{
Takashi Iwai877211f2005-11-17 13:59:38 +0100518 struct snd_pcm *pcm = pstr->pcm;
519 struct snd_info_entry *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 char name[16];
521
522 sprintf(name, "pcm%i%c", pcm->device,
523 pstr->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c');
524 if ((entry = snd_info_create_card_entry(pcm->card, name, pcm->card->proc_root)) == NULL)
525 return -ENOMEM;
526 entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
527 if (snd_info_register(entry) < 0) {
528 snd_info_free_entry(entry);
529 return -ENOMEM;
530 }
531 pstr->proc_root = entry;
532
533 if ((entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root)) != NULL) {
Takashi Iwaibf850202006-04-28 15:13:41 +0200534 snd_info_set_text_ops(entry, pstr, snd_pcm_stream_proc_info_read);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 if (snd_info_register(entry) < 0) {
536 snd_info_free_entry(entry);
537 entry = NULL;
538 }
539 }
540 pstr->proc_info_entry = entry;
541
Jaroslav Kysela61fb63c2006-04-24 21:57:16 +0200542#ifdef CONFIG_SND_PCM_XRUN_DEBUG
Takashi Iwai877211f2005-11-17 13:59:38 +0100543 if ((entry = snd_info_create_card_entry(pcm->card, "xrun_debug",
544 pstr->proc_root)) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 entry->c.text.read = snd_pcm_xrun_debug_read;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 entry->c.text.write = snd_pcm_xrun_debug_write;
Takashi Iwaibd7bf042005-04-12 16:27:28 +0200547 entry->mode |= S_IWUSR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 entry->private_data = pstr;
549 if (snd_info_register(entry) < 0) {
550 snd_info_free_entry(entry);
551 entry = NULL;
552 }
553 }
554 pstr->proc_xrun_debug_entry = entry;
555#endif
556 return 0;
557}
558
Takashi Iwai877211f2005-11-17 13:59:38 +0100559static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560{
Jaroslav Kysela61fb63c2006-04-24 21:57:16 +0200561#ifdef CONFIG_SND_PCM_XRUN_DEBUG
Takashi Iwai746d4a02006-06-23 14:37:59 +0200562 snd_info_free_entry(pstr->proc_xrun_debug_entry);
563 pstr->proc_xrun_debug_entry = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564#endif
Takashi Iwai746d4a02006-06-23 14:37:59 +0200565 snd_info_free_entry(pstr->proc_info_entry);
566 pstr->proc_info_entry = NULL;
567 snd_info_free_entry(pstr->proc_root);
568 pstr->proc_root = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 return 0;
570}
571
Takashi Iwai877211f2005-11-17 13:59:38 +0100572static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573{
Takashi Iwai877211f2005-11-17 13:59:38 +0100574 struct snd_info_entry *entry;
575 struct snd_card *card;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 char name[16];
577
578 card = substream->pcm->card;
579
580 sprintf(name, "sub%i", substream->number);
581 if ((entry = snd_info_create_card_entry(card, name, substream->pstr->proc_root)) == NULL)
582 return -ENOMEM;
583 entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
584 if (snd_info_register(entry) < 0) {
585 snd_info_free_entry(entry);
586 return -ENOMEM;
587 }
588 substream->proc_root = entry;
589
590 if ((entry = snd_info_create_card_entry(card, "info", substream->proc_root)) != NULL) {
Takashi Iwaibf850202006-04-28 15:13:41 +0200591 snd_info_set_text_ops(entry, substream,
592 snd_pcm_substream_proc_info_read);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 if (snd_info_register(entry) < 0) {
594 snd_info_free_entry(entry);
595 entry = NULL;
596 }
597 }
598 substream->proc_info_entry = entry;
599
600 if ((entry = snd_info_create_card_entry(card, "hw_params", substream->proc_root)) != NULL) {
Takashi Iwaibf850202006-04-28 15:13:41 +0200601 snd_info_set_text_ops(entry, substream,
602 snd_pcm_substream_proc_hw_params_read);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 if (snd_info_register(entry) < 0) {
604 snd_info_free_entry(entry);
605 entry = NULL;
606 }
607 }
608 substream->proc_hw_params_entry = entry;
609
610 if ((entry = snd_info_create_card_entry(card, "sw_params", substream->proc_root)) != NULL) {
Takashi Iwaibf850202006-04-28 15:13:41 +0200611 snd_info_set_text_ops(entry, substream,
612 snd_pcm_substream_proc_sw_params_read);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 if (snd_info_register(entry) < 0) {
614 snd_info_free_entry(entry);
615 entry = NULL;
616 }
617 }
618 substream->proc_sw_params_entry = entry;
619
620 if ((entry = snd_info_create_card_entry(card, "status", substream->proc_root)) != NULL) {
Takashi Iwaibf850202006-04-28 15:13:41 +0200621 snd_info_set_text_ops(entry, substream,
622 snd_pcm_substream_proc_status_read);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 if (snd_info_register(entry) < 0) {
624 snd_info_free_entry(entry);
625 entry = NULL;
626 }
627 }
628 substream->proc_status_entry = entry;
629
Takashi Iwai2b30d412014-11-04 14:02:40 +0100630#ifdef CONFIG_SND_PCM_XRUN_DEBUG
631 entry = snd_info_create_card_entry(card, "xrun_injection",
632 substream->proc_root);
633 if (entry) {
634 entry->private_data = substream;
635 entry->c.text.read = NULL;
636 entry->c.text.write = snd_pcm_xrun_injection_write;
637 entry->mode = S_IFREG | S_IWUSR;
638 if (snd_info_register(entry) < 0) {
639 snd_info_free_entry(entry);
640 entry = NULL;
641 }
642 }
643 substream->proc_xrun_injection_entry = entry;
644#endif /* CONFIG_SND_PCM_XRUN_DEBUG */
645
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 return 0;
647}
Takashi Iwai746d4a02006-06-23 14:37:59 +0200648
Takashi Iwai877211f2005-11-17 13:59:38 +0100649static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650{
Takashi Iwai746d4a02006-06-23 14:37:59 +0200651 snd_info_free_entry(substream->proc_info_entry);
652 substream->proc_info_entry = NULL;
653 snd_info_free_entry(substream->proc_hw_params_entry);
654 substream->proc_hw_params_entry = NULL;
655 snd_info_free_entry(substream->proc_sw_params_entry);
656 substream->proc_sw_params_entry = NULL;
657 snd_info_free_entry(substream->proc_status_entry);
658 substream->proc_status_entry = NULL;
Takashi Iwai2b30d412014-11-04 14:02:40 +0100659#ifdef CONFIG_SND_PCM_XRUN_DEBUG
660 snd_info_free_entry(substream->proc_xrun_injection_entry);
661 substream->proc_xrun_injection_entry = NULL;
662#endif
Takashi Iwai746d4a02006-06-23 14:37:59 +0200663 snd_info_free_entry(substream->proc_root);
664 substream->proc_root = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 return 0;
666}
Takashi Iwaib7d90a32006-04-25 12:56:04 +0200667#else /* !CONFIG_SND_VERBOSE_PROCFS */
Takashi Iwaie28563c2005-12-01 10:42:42 +0100668static inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; }
669static inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; }
670static inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; }
671static inline int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) { return 0; }
Takashi Iwaib7d90a32006-04-25 12:56:04 +0200672#endif /* CONFIG_SND_VERBOSE_PROCFS */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673
Takashi Iwaief46c7a2015-01-29 17:32:26 +0100674static const struct attribute_group *pcm_dev_attr_groups[];
675
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676/**
677 * snd_pcm_new_stream - create a new PCM stream
678 * @pcm: the pcm instance
679 * @stream: the stream direction, SNDRV_PCM_STREAM_XXX
680 * @substream_count: the number of substreams
681 *
682 * Creates a new stream for the pcm.
683 * The corresponding stream on the pcm must have been empty before
684 * calling this, i.e. zero must be given to the argument of
685 * snd_pcm_new().
686 *
Yacine Belkadieb7c06e2013-03-11 22:05:14 +0100687 * Return: Zero if successful, or a negative error code on failure.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 */
Takashi Iwai877211f2005-11-17 13:59:38 +0100689int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690{
691 int idx, err;
Takashi Iwai877211f2005-11-17 13:59:38 +0100692 struct snd_pcm_str *pstr = &pcm->streams[stream];
693 struct snd_pcm_substream *substream, *prev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694
Takashi Iwai8eeaa2f2014-02-10 09:48:47 +0100695#if IS_ENABLED(CONFIG_SND_PCM_OSS)
Ingo Molnar1a60d4c2006-01-16 16:29:08 +0100696 mutex_init(&pstr->oss.setup_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697#endif
698 pstr->stream = stream;
699 pstr->pcm = pcm;
700 pstr->substream_count = substream_count;
Takashi Iwaief46c7a2015-01-29 17:32:26 +0100701 if (!substream_count)
702 return 0;
703
704 snd_device_initialize(&pstr->dev, pcm->card);
705 pstr->dev.groups = pcm_dev_attr_groups;
706 dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device,
707 stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c');
708
709 if (!pcm->internal) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 err = snd_pcm_stream_proc_init(pstr);
Takashi Iwai73e77ba2005-11-17 17:44:01 +0100711 if (err < 0) {
Takashi Iwai09e56df2014-02-04 18:19:48 +0100712 pcm_err(pcm, "Error in snd_pcm_stream_proc_init\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 return err;
Takashi Iwai73e77ba2005-11-17 17:44:01 +0100714 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 }
716 prev = NULL;
717 for (idx = 0, prev = NULL; idx < substream_count; idx++) {
Takashi Iwaica2c0962005-09-09 14:20:23 +0200718 substream = kzalloc(sizeof(*substream), GFP_KERNEL);
Takashi Iwaiec0e9932015-03-10 15:42:14 +0100719 if (!substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 return -ENOMEM;
721 substream->pcm = pcm;
722 substream->pstr = pstr;
723 substream->number = idx;
724 substream->stream = stream;
725 sprintf(substream->name, "subdevice #%i", idx);
726 substream->buffer_bytes_max = UINT_MAX;
727 if (prev == NULL)
728 pstr->substream = substream;
729 else
730 prev->next = substream;
Liam Girdwood945e5032012-02-08 20:33:31 +0000731
732 if (!pcm->internal) {
733 err = snd_pcm_substream_proc_init(substream);
734 if (err < 0) {
Takashi Iwai09e56df2014-02-04 18:19:48 +0100735 pcm_err(pcm,
736 "Error in snd_pcm_stream_proc_init\n");
Liam Girdwood945e5032012-02-08 20:33:31 +0000737 if (prev == NULL)
738 pstr->substream = NULL;
739 else
740 prev->next = NULL;
741 kfree(substream);
742 return err;
743 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 }
745 substream->group = &substream->self_group;
746 spin_lock_init(&substream->self_group.lock);
Takashi Iwai257f8cc2014-08-29 15:32:29 +0200747 mutex_init(&substream->self_group.mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 INIT_LIST_HEAD(&substream->self_group.substreams);
749 list_add_tail(&substream->link_list, &substream->self_group.substreams);
Takashi Iwai9c323fc2006-04-28 15:13:41 +0200750 atomic_set(&substream->mmap_count, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 prev = substream;
752 }
753 return 0;
754}
Takashi Iwaie88e8ae62006-04-28 15:13:40 +0200755EXPORT_SYMBOL(snd_pcm_new_stream);
756
Liam Girdwood945e5032012-02-08 20:33:31 +0000757static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
758 int playback_count, int capture_count, bool internal,
759 struct snd_pcm **rpcm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760{
Takashi Iwai877211f2005-11-17 13:59:38 +0100761 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 int err;
Takashi Iwai877211f2005-11-17 13:59:38 +0100763 static struct snd_device_ops ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 .dev_free = snd_pcm_dev_free,
765 .dev_register = snd_pcm_dev_register,
766 .dev_disconnect = snd_pcm_dev_disconnect,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 };
768
Takashi Iwai7eaa9432008-08-08 17:09:09 +0200769 if (snd_BUG_ON(!card))
770 return -ENXIO;
771 if (rpcm)
772 *rpcm = NULL;
Takashi Iwaica2c0962005-09-09 14:20:23 +0200773 pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
Takashi Iwaiec0e9932015-03-10 15:42:14 +0100774 if (!pcm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 return -ENOMEM;
776 pcm->card = card;
777 pcm->device = device;
Liam Girdwood945e5032012-02-08 20:33:31 +0000778 pcm->internal = internal;
Takashi Iwaib95bd3a2015-02-20 16:49:04 +0100779 mutex_init(&pcm->open_mutex);
780 init_waitqueue_head(&pcm->open_wait);
781 INIT_LIST_HEAD(&pcm->list);
Takashi Iwai73e77ba2005-11-17 17:44:01 +0100782 if (id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 strlcpy(pcm->id, id, sizeof(pcm->id));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {
785 snd_pcm_free(pcm);
786 return err;
787 }
788 if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) {
789 snd_pcm_free(pcm);
790 return err;
791 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {
793 snd_pcm_free(pcm);
794 return err;
795 }
Takashi Iwai7eaa9432008-08-08 17:09:09 +0200796 if (rpcm)
797 *rpcm = pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 return 0;
799}
800
Liam Girdwood945e5032012-02-08 20:33:31 +0000801/**
802 * snd_pcm_new - create a new PCM instance
803 * @card: the card instance
804 * @id: the id string
805 * @device: the device index (zero based)
806 * @playback_count: the number of substreams for playback
807 * @capture_count: the number of substreams for capture
808 * @rpcm: the pointer to store the new pcm instance
809 *
810 * Creates a new PCM instance.
811 *
812 * The pcm operators have to be set afterwards to the new instance
813 * via snd_pcm_set_ops().
814 *
Yacine Belkadieb7c06e2013-03-11 22:05:14 +0100815 * Return: Zero if successful, or a negative error code on failure.
Liam Girdwood945e5032012-02-08 20:33:31 +0000816 */
817int snd_pcm_new(struct snd_card *card, const char *id, int device,
818 int playback_count, int capture_count, struct snd_pcm **rpcm)
819{
820 return _snd_pcm_new(card, id, device, playback_count, capture_count,
821 false, rpcm);
822}
Takashi Iwaie88e8ae62006-04-28 15:13:40 +0200823EXPORT_SYMBOL(snd_pcm_new);
824
Liam Girdwood945e5032012-02-08 20:33:31 +0000825/**
826 * snd_pcm_new_internal - create a new internal PCM instance
827 * @card: the card instance
828 * @id: the id string
829 * @device: the device index (zero based - shared with normal PCMs)
830 * @playback_count: the number of substreams for playback
831 * @capture_count: the number of substreams for capture
832 * @rpcm: the pointer to store the new pcm instance
833 *
834 * Creates a new internal PCM instance with no userspace device or procfs
835 * entries. This is used by ASoC Back End PCMs in order to create a PCM that
836 * will only be used internally by kernel drivers. i.e. it cannot be opened
837 * by userspace. It provides existing ASoC components drivers with a substream
838 * and access to any private data.
839 *
840 * The pcm operators have to be set afterwards to the new instance
841 * via snd_pcm_set_ops().
842 *
Yacine Belkadieb7c06e2013-03-11 22:05:14 +0100843 * Return: Zero if successful, or a negative error code on failure.
Liam Girdwood945e5032012-02-08 20:33:31 +0000844 */
845int snd_pcm_new_internal(struct snd_card *card, const char *id, int device,
846 int playback_count, int capture_count,
847 struct snd_pcm **rpcm)
848{
849 return _snd_pcm_new(card, id, device, playback_count, capture_count,
850 true, rpcm);
851}
852EXPORT_SYMBOL(snd_pcm_new_internal);
853
Takashi Iwaia8ff48c2016-07-08 08:23:43 +0200854static void free_chmap(struct snd_pcm_str *pstr)
855{
856 if (pstr->chmap_kctl) {
857 snd_ctl_remove(pstr->pcm->card, pstr->chmap_kctl);
858 pstr->chmap_kctl = NULL;
859 }
860}
861
Takashi Iwai877211f2005-11-17 13:59:38 +0100862static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863{
Takashi Iwai877211f2005-11-17 13:59:38 +0100864 struct snd_pcm_substream *substream, *substream_next;
Takashi Iwai8eeaa2f2014-02-10 09:48:47 +0100865#if IS_ENABLED(CONFIG_SND_PCM_OSS)
Takashi Iwai877211f2005-11-17 13:59:38 +0100866 struct snd_pcm_oss_setup *setup, *setupn;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867#endif
868 substream = pstr->substream;
869 while (substream) {
870 substream_next = substream->next;
Takashi Iwaic4614822006-06-23 14:38:23 +0200871 snd_pcm_timer_done(substream);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 snd_pcm_substream_proc_done(substream);
873 kfree(substream);
874 substream = substream_next;
875 }
876 snd_pcm_stream_proc_done(pstr);
Takashi Iwai8eeaa2f2014-02-10 09:48:47 +0100877#if IS_ENABLED(CONFIG_SND_PCM_OSS)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 for (setup = pstr->oss.setup_list; setup; setup = setupn) {
879 setupn = setup->next;
880 kfree(setup->task_name);
881 kfree(setup);
882 }
883#endif
Takashi Iwaia8ff48c2016-07-08 08:23:43 +0200884 free_chmap(pstr);
Takashi Iwaief46c7a2015-01-29 17:32:26 +0100885 if (pstr->substream_count)
886 put_device(&pstr->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887}
888
Takashi Iwai58f30d62017-05-12 11:35:17 +0200889#if IS_ENABLED(CONFIG_SND_PCM_OSS)
890#define pcm_call_notify(pcm, call) \
891 do { \
892 struct snd_pcm_notify *_notify; \
893 list_for_each_entry(_notify, &snd_pcm_notify_list, list) \
894 _notify->call(pcm); \
895 } while (0)
896#else
897#define pcm_call_notify(pcm, call) /* NOP */
898#endif
899
Takashi Iwai877211f2005-11-17 13:59:38 +0100900static int snd_pcm_free(struct snd_pcm *pcm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901{
Takashi Iwai7eaa9432008-08-08 17:09:09 +0200902 if (!pcm)
903 return 0;
Takashi Iwai58f30d62017-05-12 11:35:17 +0200904 if (!pcm->internal)
905 pcm_call_notify(pcm, n_unregister);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 if (pcm->private_free)
907 pcm->private_free(pcm);
908 snd_pcm_lib_preallocate_free_for_all(pcm);
909 snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]);
910 snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]);
911 kfree(pcm);
912 return 0;
913}
914
Takashi Iwai877211f2005-11-17 13:59:38 +0100915static int snd_pcm_dev_free(struct snd_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916{
Takashi Iwai877211f2005-11-17 13:59:38 +0100917 struct snd_pcm *pcm = device->device_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 return snd_pcm_free(pcm);
919}
920
Takashi Iwai3bf75f92006-03-27 16:40:49 +0200921int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
922 struct file *file,
923 struct snd_pcm_substream **rsubstream)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924{
Takashi Iwai877211f2005-11-17 13:59:38 +0100925 struct snd_pcm_str * pstr;
926 struct snd_pcm_substream *substream;
927 struct snd_pcm_runtime *runtime;
Takashi Iwai877211f2005-11-17 13:59:38 +0100928 struct snd_card *card;
Takashi Iwai23c18d42014-02-19 14:30:29 +0100929 int prefer_subdevice;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 size_t size;
931
Takashi Iwai7eaa9432008-08-08 17:09:09 +0200932 if (snd_BUG_ON(!pcm || !rsubstream))
933 return -ENXIO;
Takashi Iwaiad876c82015-02-20 16:26:00 +0100934 if (snd_BUG_ON(stream != SNDRV_PCM_STREAM_PLAYBACK &&
935 stream != SNDRV_PCM_STREAM_CAPTURE))
936 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 *rsubstream = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 pstr = &pcm->streams[stream];
Takashi Iwai3bf75f92006-03-27 16:40:49 +0200939 if (pstr->substream == NULL || pstr->substream_count == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 return -ENODEV;
941
942 card = pcm->card;
Takashi Iwai23c18d42014-02-19 14:30:29 +0100943 prefer_subdevice = snd_ctl_get_preferred_subdevice(card, SND_CTL_SUBDEV_PCM);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944
Takashi Iwaiad876c82015-02-20 16:26:00 +0100945 if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
946 int opposite = !stream;
947
948 for (substream = pcm->streams[opposite].substream; substream;
949 substream = substream->next) {
950 if (SUBSTREAM_BUSY(substream))
951 return -EAGAIN;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 }
954
Takashi Iwai0df63e42006-04-28 15:13:41 +0200955 if (file->f_flags & O_APPEND) {
956 if (prefer_subdevice < 0) {
957 if (pstr->substream_count > 1)
958 return -EINVAL; /* must be unique */
959 substream = pstr->substream;
960 } else {
961 for (substream = pstr->substream; substream;
962 substream = substream->next)
963 if (substream->number == prefer_subdevice)
964 break;
965 }
966 if (! substream)
967 return -ENODEV;
968 if (! SUBSTREAM_BUSY(substream))
969 return -EBADFD;
970 substream->ref_count++;
971 *rsubstream = substream;
972 return 0;
973 }
974
Takashi Iwaiad876c82015-02-20 16:26:00 +0100975 for (substream = pstr->substream; substream; substream = substream->next) {
976 if (!SUBSTREAM_BUSY(substream) &&
977 (prefer_subdevice == -1 ||
978 substream->number == prefer_subdevice))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 break;
Takashi Iwaiad876c82015-02-20 16:26:00 +0100980 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 if (substream == NULL)
982 return -EAGAIN;
983
Takashi Iwaica2c0962005-09-09 14:20:23 +0200984 runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 if (runtime == NULL)
986 return -ENOMEM;
987
Takashi Iwai877211f2005-11-17 13:59:38 +0100988 size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989 runtime->status = snd_malloc_pages(size, GFP_KERNEL);
990 if (runtime->status == NULL) {
991 kfree(runtime);
992 return -ENOMEM;
993 }
994 memset((void*)runtime->status, 0, size);
995
Takashi Iwai877211f2005-11-17 13:59:38 +0100996 size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 runtime->control = snd_malloc_pages(size, GFP_KERNEL);
998 if (runtime->control == NULL) {
Takashi Iwai877211f2005-11-17 13:59:38 +0100999 snd_free_pages((void*)runtime->status,
1000 PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 kfree(runtime);
1002 return -ENOMEM;
1003 }
1004 memset((void*)runtime->control, 0, size);
1005
1006 init_waitqueue_head(&runtime->sleep);
Jaroslav Kyselac91a9882010-01-21 10:32:15 +01001007 init_waitqueue_head(&runtime->tsleep);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008
1009 runtime->status->state = SNDRV_PCM_STATE_OPEN;
1010
1011 substream->runtime = runtime;
1012 substream->private_data = pcm->private_data;
Takashi Iwai0df63e42006-04-28 15:13:41 +02001013 substream->ref_count = 1;
1014 substream->f_flags = file->f_flags;
Clemens Ladische7373b72009-11-10 10:13:30 +01001015 substream->pid = get_pid(task_pid(current));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016 pstr->substream_opened++;
1017 *rsubstream = substream;
1018 return 0;
1019}
1020
Takashi Iwai3bf75f92006-03-27 16:40:49 +02001021void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022{
Takashi Iwai877211f2005-11-17 13:59:38 +01001023 struct snd_pcm_runtime *runtime;
Takashi Iwai0df63e42006-04-28 15:13:41 +02001024
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001025 if (PCM_RUNTIME_CHECK(substream))
1026 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 runtime = substream->runtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 if (runtime->private_free != NULL)
1029 runtime->private_free(runtime);
Takashi Iwai877211f2005-11-17 13:59:38 +01001030 snd_free_pages((void*)runtime->status,
1031 PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)));
1032 snd_free_pages((void*)runtime->control,
1033 PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 kfree(runtime->hw_constraints.rules);
1035 kfree(runtime);
1036 substream->runtime = NULL;
Clemens Ladische7373b72009-11-10 10:13:30 +01001037 put_pid(substream->pid);
1038 substream->pid = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 substream->pstr->substream_opened--;
1040}
1041
Greg Kroah-Hartmand80f19f2006-08-07 22:19:37 -07001042static ssize_t show_pcm_class(struct device *dev,
1043 struct device_attribute *attr, char *buf)
Takashi Iwai9d19f482006-09-06 14:27:46 +02001044{
Takashi Iwai60b93032015-06-23 11:56:22 +02001045 struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev);
1046 struct snd_pcm *pcm = pstr->pcm;
Takashi Iwai9d19f482006-09-06 14:27:46 +02001047 const char *str;
1048 static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = {
1049 [SNDRV_PCM_CLASS_GENERIC] = "generic",
1050 [SNDRV_PCM_CLASS_MULTI] = "multi",
1051 [SNDRV_PCM_CLASS_MODEM] = "modem",
1052 [SNDRV_PCM_CLASS_DIGITIZER] = "digitizer",
1053 };
1054
Takashi Iwai60b93032015-06-23 11:56:22 +02001055 if (pcm->dev_class > SNDRV_PCM_CLASS_LAST)
Takashi Iwai9d19f482006-09-06 14:27:46 +02001056 str = "none";
1057 else
1058 str = strs[pcm->dev_class];
1059 return snprintf(buf, PAGE_SIZE, "%s\n", str);
1060}
1061
Takashi Iwaicaa751b2014-02-25 08:30:50 +01001062static DEVICE_ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL);
1063static struct attribute *pcm_dev_attrs[] = {
1064 &dev_attr_pcm_class.attr,
1065 NULL
1066};
1067
1068static struct attribute_group pcm_dev_attr_group = {
1069 .attrs = pcm_dev_attrs,
1070};
1071
1072static const struct attribute_group *pcm_dev_attr_groups[] = {
1073 &pcm_dev_attr_group,
1074 NULL
1075};
Takashi Iwai9d19f482006-09-06 14:27:46 +02001076
Takashi Iwai877211f2005-11-17 13:59:38 +01001077static int snd_pcm_dev_register(struct snd_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078{
Clemens Ladischf87135f2005-11-20 14:06:59 +01001079 int cidx, err;
Takashi Iwai877211f2005-11-17 13:59:38 +01001080 struct snd_pcm_substream *substream;
Julia Lawall4b3be6a2009-10-17 08:33:22 +02001081 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082
Julia Lawall4b3be6a2009-10-17 08:33:22 +02001083 if (snd_BUG_ON(!device || !device->device_data))
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001084 return -ENXIO;
Julia Lawall4b3be6a2009-10-17 08:33:22 +02001085 pcm = device->device_data;
Takashi Iwaib95bd3a2015-02-20 16:49:04 +01001086 if (pcm->internal)
1087 return 0;
1088
Ingo Molnar1a60d4c2006-01-16 16:29:08 +01001089 mutex_lock(&register_mutex);
Pawel MOLLf90c06a2008-07-30 12:46:40 +01001090 err = snd_pcm_add(pcm);
Takashi Iwaib95bd3a2015-02-20 16:49:04 +01001091 if (err)
1092 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 for (cidx = 0; cidx < 2; cidx++) {
1094 int devtype = -1;
Takashi Iwaib95bd3a2015-02-20 16:49:04 +01001095 if (pcm->streams[cidx].substream == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 continue;
1097 switch (cidx) {
1098 case SNDRV_PCM_STREAM_PLAYBACK:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
1100 break;
1101 case SNDRV_PCM_STREAM_CAPTURE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
1103 break;
1104 }
Johannes Bergc78085f2006-10-05 15:06:34 +02001105 /* register pcm */
Takashi Iwai40a4b262015-01-30 08:34:58 +01001106 err = snd_register_device(devtype, pcm->card, pcm->device,
1107 &snd_pcm_f_ops[cidx], pcm,
1108 &pcm->streams[cidx].dev);
Johannes Bergc78085f2006-10-05 15:06:34 +02001109 if (err < 0) {
Takashi Iwaib95bd3a2015-02-20 16:49:04 +01001110 list_del_init(&pcm->list);
1111 goto unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 }
Takashi Iwaicaa751b2014-02-25 08:30:50 +01001113
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
1115 snd_pcm_timer_init(substream);
1116 }
Johannes Berg9244b2c2006-10-05 16:02:22 +02001117
Takashi Iwai58f30d62017-05-12 11:35:17 +02001118 pcm_call_notify(pcm, n_register);
Johannes Berg9244b2c2006-10-05 16:02:22 +02001119
Takashi Iwaib95bd3a2015-02-20 16:49:04 +01001120 unlock:
Ingo Molnar1a60d4c2006-01-16 16:29:08 +01001121 mutex_unlock(&register_mutex);
Takashi Iwaib95bd3a2015-02-20 16:49:04 +01001122 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123}
1124
Takashi Iwai877211f2005-11-17 13:59:38 +01001125static int snd_pcm_dev_disconnect(struct snd_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126{
Takashi Iwai877211f2005-11-17 13:59:38 +01001127 struct snd_pcm *pcm = device->device_data;
Takashi Iwai877211f2005-11-17 13:59:38 +01001128 struct snd_pcm_substream *substream;
Takashi Iwai40a4b262015-01-30 08:34:58 +01001129 int cidx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130
Ingo Molnar1a60d4c2006-01-16 16:29:08 +01001131 mutex_lock(&register_mutex);
Takashi Iwai9b0573c02012-10-12 15:07:34 +02001132 mutex_lock(&pcm->open_mutex);
Takashi Iwai0914f792012-10-16 16:43:39 +02001133 wake_up(&pcm->open_wait);
Clemens Ladischf87135f2005-11-20 14:06:59 +01001134 list_del_init(&pcm->list);
Takashi Iwai646e1dd2015-02-20 17:04:08 +01001135 for (cidx = 0; cidx < 2; cidx++) {
Takashi Iwai9b0573c02012-10-12 15:07:34 +02001136 for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) {
1137 snd_pcm_stream_lock_irq(substream);
Takashi Iwai0914f792012-10-16 16:43:39 +02001138 if (substream->runtime) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED;
Takashi Iwai0914f792012-10-16 16:43:39 +02001140 wake_up(&substream->runtime->sleep);
1141 wake_up(&substream->runtime->tsleep);
1142 }
Takashi Iwai9b0573c02012-10-12 15:07:34 +02001143 snd_pcm_stream_unlock_irq(substream);
1144 }
Takashi Iwai646e1dd2015-02-20 17:04:08 +01001145 }
1146 if (!pcm->internal) {
Takashi Iwai58f30d62017-05-12 11:35:17 +02001147 pcm_call_notify(pcm, n_disconnect);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 for (cidx = 0; cidx < 2; cidx++) {
Takashi Iwaib2022132015-02-20 17:05:27 +01001150 if (!pcm->internal)
1151 snd_unregister_device(&pcm->streams[cidx].dev);
Takashi Iwaia8ff48c2016-07-08 08:23:43 +02001152 free_chmap(&pcm->streams[cidx]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 }
Takashi Iwai9b0573c02012-10-12 15:07:34 +02001154 mutex_unlock(&pcm->open_mutex);
Ingo Molnar1a60d4c2006-01-16 16:29:08 +01001155 mutex_unlock(&register_mutex);
Takashi Iwaic4614822006-06-23 14:38:23 +02001156 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157}
1158
Takashi Iwai58f30d62017-05-12 11:35:17 +02001159#if IS_ENABLED(CONFIG_SND_PCM_OSS)
Takashi Iwai30b771c2014-10-30 15:02:50 +01001160/**
1161 * snd_pcm_notify - Add/remove the notify list
1162 * @notify: PCM notify list
1163 * @nfree: 0 = register, 1 = unregister
1164 *
1165 * This adds the given notifier to the global list so that the callback is
1166 * called for each registered PCM devices. This exists only for PCM OSS
1167 * emulation, so far.
1168 */
Takashi Iwai877211f2005-11-17 13:59:38 +01001169int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170{
Johannes Berg9244b2c2006-10-05 16:02:22 +02001171 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172
Takashi Iwai7eaa9432008-08-08 17:09:09 +02001173 if (snd_BUG_ON(!notify ||
1174 !notify->n_register ||
1175 !notify->n_unregister ||
1176 !notify->n_disconnect))
1177 return -EINVAL;
Ingo Molnar1a60d4c2006-01-16 16:29:08 +01001178 mutex_lock(&register_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 if (nfree) {
1180 list_del(&notify->list);
Johannes Berg9244b2c2006-10-05 16:02:22 +02001181 list_for_each_entry(pcm, &snd_pcm_devices, list)
1182 notify->n_unregister(pcm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 } else {
1184 list_add_tail(&notify->list, &snd_pcm_notify_list);
Johannes Berg9244b2c2006-10-05 16:02:22 +02001185 list_for_each_entry(pcm, &snd_pcm_devices, list)
1186 notify->n_register(pcm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 }
Ingo Molnar1a60d4c2006-01-16 16:29:08 +01001188 mutex_unlock(&register_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 return 0;
1190}
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001191EXPORT_SYMBOL(snd_pcm_notify);
Takashi Iwai58f30d62017-05-12 11:35:17 +02001192#endif /* CONFIG_SND_PCM_OSS */
Takashi Iwaie88e8ae62006-04-28 15:13:40 +02001193
Jie Yangcd6a6502015-05-27 19:45:45 +08001194#ifdef CONFIG_SND_PROC_FS
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195/*
1196 * Info interface
1197 */
1198
Takashi Iwai877211f2005-11-17 13:59:38 +01001199static void snd_pcm_proc_read(struct snd_info_entry *entry,
1200 struct snd_info_buffer *buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201{
Takashi Iwai877211f2005-11-17 13:59:38 +01001202 struct snd_pcm *pcm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203
Ingo Molnar1a60d4c2006-01-16 16:29:08 +01001204 mutex_lock(&register_mutex);
Johannes Berg9244b2c2006-10-05 16:02:22 +02001205 list_for_each_entry(pcm, &snd_pcm_devices, list) {
Clemens Ladischf87135f2005-11-20 14:06:59 +01001206 snd_iprintf(buffer, "%02i-%02i: %s : %s",
1207 pcm->card->number, pcm->device, pcm->id, pcm->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
Takashi Iwai877211f2005-11-17 13:59:38 +01001209 snd_iprintf(buffer, " : playback %i",
1210 pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
Takashi Iwai877211f2005-11-17 13:59:38 +01001212 snd_iprintf(buffer, " : capture %i",
1213 pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 snd_iprintf(buffer, "\n");
1215 }
Ingo Molnar1a60d4c2006-01-16 16:29:08 +01001216 mutex_unlock(&register_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217}
1218
Takashi Iwai6581f4e2006-05-17 17:14:51 +02001219static struct snd_info_entry *snd_pcm_proc_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220
Takashi Iwaie28563c2005-12-01 10:42:42 +01001221static void snd_pcm_proc_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222{
Takashi Iwai877211f2005-11-17 13:59:38 +01001223 struct snd_info_entry *entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 if ((entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL)) != NULL) {
Takashi Iwaibf850202006-04-28 15:13:41 +02001226 snd_info_set_text_ops(entry, NULL, snd_pcm_proc_read);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 if (snd_info_register(entry) < 0) {
1228 snd_info_free_entry(entry);
1229 entry = NULL;
1230 }
1231 }
1232 snd_pcm_proc_entry = entry;
Takashi Iwaie28563c2005-12-01 10:42:42 +01001233}
1234
1235static void snd_pcm_proc_done(void)
1236{
Takashi Iwai746d4a02006-06-23 14:37:59 +02001237 snd_info_free_entry(snd_pcm_proc_entry);
Takashi Iwaie28563c2005-12-01 10:42:42 +01001238}
1239
Jie Yangcd6a6502015-05-27 19:45:45 +08001240#else /* !CONFIG_SND_PROC_FS */
Takashi Iwaie28563c2005-12-01 10:42:42 +01001241#define snd_pcm_proc_init()
1242#define snd_pcm_proc_done()
Jie Yangcd6a6502015-05-27 19:45:45 +08001243#endif /* CONFIG_SND_PROC_FS */
Takashi Iwaie28563c2005-12-01 10:42:42 +01001244
1245
1246/*
1247 * ENTRY functions
1248 */
1249
1250static int __init alsa_pcm_init(void)
1251{
1252 snd_ctl_register_ioctl(snd_pcm_control_ioctl);
1253 snd_ctl_register_ioctl_compat(snd_pcm_control_ioctl);
1254 snd_pcm_proc_init();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 return 0;
1256}
1257
1258static void __exit alsa_pcm_exit(void)
1259{
1260 snd_ctl_unregister_ioctl(snd_pcm_control_ioctl);
1261 snd_ctl_unregister_ioctl_compat(snd_pcm_control_ioctl);
Takashi Iwaie28563c2005-12-01 10:42:42 +01001262 snd_pcm_proc_done();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263}
1264
1265module_init(alsa_pcm_init)
1266module_exit(alsa_pcm_exit)