1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Digital Audio (PCM) abstract layer |
4 | * Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
5 | */ |
6 | |
7 | #include <linux/init.h> |
8 | #include <linux/slab.h> |
9 | #include <linux/module.h> |
10 | #include <linux/time.h> |
11 | #include <linux/mutex.h> |
12 | #include <linux/device.h> |
13 | #include <linux/nospec.h> |
14 | #include <sound/core.h> |
15 | #include <sound/minors.h> |
16 | #include <sound/pcm.h> |
17 | #include <sound/timer.h> |
18 | #include <sound/control.h> |
19 | #include <sound/info.h> |
20 | |
21 | #include "pcm_local.h" |
22 | |
23 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>" ); |
24 | MODULE_DESCRIPTION("Midlevel PCM code for ALSA." ); |
25 | MODULE_LICENSE("GPL" ); |
26 | |
27 | static LIST_HEAD(snd_pcm_devices); |
28 | static DEFINE_MUTEX(register_mutex); |
29 | #if IS_ENABLED(CONFIG_SND_PCM_OSS) |
30 | static LIST_HEAD(snd_pcm_notify_list); |
31 | #endif |
32 | |
33 | static int snd_pcm_free(struct snd_pcm *pcm); |
34 | static int snd_pcm_dev_free(struct snd_device *device); |
35 | static int snd_pcm_dev_register(struct snd_device *device); |
36 | static int snd_pcm_dev_disconnect(struct snd_device *device); |
37 | |
38 | static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device) |
39 | { |
40 | struct snd_pcm *pcm; |
41 | |
42 | list_for_each_entry(pcm, &snd_pcm_devices, list) { |
43 | if (pcm->card == card && pcm->device == device) |
44 | return pcm; |
45 | } |
46 | return NULL; |
47 | } |
48 | |
49 | static int snd_pcm_next(struct snd_card *card, int device) |
50 | { |
51 | struct snd_pcm *pcm; |
52 | |
53 | list_for_each_entry(pcm, &snd_pcm_devices, list) { |
54 | if (pcm->card == card && pcm->device > device) |
55 | return pcm->device; |
56 | else if (pcm->card->number > card->number) |
57 | return -1; |
58 | } |
59 | return -1; |
60 | } |
61 | |
62 | static int snd_pcm_add(struct snd_pcm *newpcm) |
63 | { |
64 | struct snd_pcm *pcm; |
65 | |
66 | if (newpcm->internal) |
67 | return 0; |
68 | |
69 | list_for_each_entry(pcm, &snd_pcm_devices, list) { |
70 | if (pcm->card == newpcm->card && pcm->device == newpcm->device) |
71 | return -EBUSY; |
72 | if (pcm->card->number > newpcm->card->number || |
73 | (pcm->card == newpcm->card && |
74 | pcm->device > newpcm->device)) { |
75 | list_add(new: &newpcm->list, head: pcm->list.prev); |
76 | return 0; |
77 | } |
78 | } |
79 | list_add_tail(new: &newpcm->list, head: &snd_pcm_devices); |
80 | return 0; |
81 | } |
82 | |
83 | static int snd_pcm_control_ioctl(struct snd_card *card, |
84 | struct snd_ctl_file *control, |
85 | unsigned int cmd, unsigned long arg) |
86 | { |
87 | switch (cmd) { |
88 | case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE: |
89 | { |
90 | int device; |
91 | |
92 | if (get_user(device, (int __user *)arg)) |
93 | return -EFAULT; |
94 | mutex_lock(®ister_mutex); |
95 | device = snd_pcm_next(card, device); |
96 | mutex_unlock(lock: ®ister_mutex); |
97 | if (put_user(device, (int __user *)arg)) |
98 | return -EFAULT; |
99 | return 0; |
100 | } |
101 | case SNDRV_CTL_IOCTL_PCM_INFO: |
102 | { |
103 | struct snd_pcm_info __user *info; |
104 | unsigned int device, subdevice; |
105 | int stream; |
106 | struct snd_pcm *pcm; |
107 | struct snd_pcm_str *pstr; |
108 | struct snd_pcm_substream *substream; |
109 | int err; |
110 | |
111 | info = (struct snd_pcm_info __user *)arg; |
112 | if (get_user(device, &info->device)) |
113 | return -EFAULT; |
114 | if (get_user(stream, &info->stream)) |
115 | return -EFAULT; |
116 | if (stream < 0 || stream > 1) |
117 | return -EINVAL; |
118 | stream = array_index_nospec(stream, 2); |
119 | if (get_user(subdevice, &info->subdevice)) |
120 | return -EFAULT; |
121 | mutex_lock(®ister_mutex); |
122 | pcm = snd_pcm_get(card, device); |
123 | if (pcm == NULL) { |
124 | err = -ENXIO; |
125 | goto _error; |
126 | } |
127 | pstr = &pcm->streams[stream]; |
128 | if (pstr->substream_count == 0) { |
129 | err = -ENOENT; |
130 | goto _error; |
131 | } |
132 | if (subdevice >= pstr->substream_count) { |
133 | err = -ENXIO; |
134 | goto _error; |
135 | } |
136 | for (substream = pstr->substream; substream; |
137 | substream = substream->next) |
138 | if (substream->number == (int)subdevice) |
139 | break; |
140 | if (substream == NULL) { |
141 | err = -ENXIO; |
142 | goto _error; |
143 | } |
144 | mutex_lock(&pcm->open_mutex); |
145 | err = snd_pcm_info_user(substream, info); |
146 | mutex_unlock(lock: &pcm->open_mutex); |
147 | _error: |
148 | mutex_unlock(lock: ®ister_mutex); |
149 | return err; |
150 | } |
151 | case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE: |
152 | { |
153 | int val; |
154 | |
155 | if (get_user(val, (int __user *)arg)) |
156 | return -EFAULT; |
157 | control->preferred_subdevice[SND_CTL_SUBDEV_PCM] = val; |
158 | return 0; |
159 | } |
160 | } |
161 | return -ENOIOCTLCMD; |
162 | } |
163 | |
164 | #define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v |
165 | |
166 | static const char * const snd_pcm_format_names[] = { |
167 | FORMAT(S8), |
168 | FORMAT(U8), |
169 | FORMAT(S16_LE), |
170 | FORMAT(S16_BE), |
171 | FORMAT(U16_LE), |
172 | FORMAT(U16_BE), |
173 | FORMAT(S24_LE), |
174 | FORMAT(S24_BE), |
175 | FORMAT(U24_LE), |
176 | FORMAT(U24_BE), |
177 | FORMAT(S32_LE), |
178 | FORMAT(S32_BE), |
179 | FORMAT(U32_LE), |
180 | FORMAT(U32_BE), |
181 | FORMAT(FLOAT_LE), |
182 | FORMAT(FLOAT_BE), |
183 | FORMAT(FLOAT64_LE), |
184 | FORMAT(FLOAT64_BE), |
185 | FORMAT(IEC958_SUBFRAME_LE), |
186 | FORMAT(IEC958_SUBFRAME_BE), |
187 | FORMAT(MU_LAW), |
188 | FORMAT(A_LAW), |
189 | FORMAT(IMA_ADPCM), |
190 | FORMAT(MPEG), |
191 | FORMAT(GSM), |
192 | FORMAT(SPECIAL), |
193 | FORMAT(S24_3LE), |
194 | FORMAT(S24_3BE), |
195 | FORMAT(U24_3LE), |
196 | FORMAT(U24_3BE), |
197 | FORMAT(S20_3LE), |
198 | FORMAT(S20_3BE), |
199 | FORMAT(U20_3LE), |
200 | FORMAT(U20_3BE), |
201 | FORMAT(S18_3LE), |
202 | FORMAT(S18_3BE), |
203 | FORMAT(U18_3LE), |
204 | FORMAT(U18_3BE), |
205 | FORMAT(G723_24), |
206 | FORMAT(G723_24_1B), |
207 | FORMAT(G723_40), |
208 | FORMAT(G723_40_1B), |
209 | FORMAT(DSD_U8), |
210 | FORMAT(DSD_U16_LE), |
211 | FORMAT(DSD_U32_LE), |
212 | FORMAT(DSD_U16_BE), |
213 | FORMAT(DSD_U32_BE), |
214 | }; |
215 | |
216 | /** |
217 | * snd_pcm_format_name - Return a name string for the given PCM format |
218 | * @format: PCM format |
219 | * |
220 | * Return: the format name string |
221 | */ |
222 | const char *snd_pcm_format_name(snd_pcm_format_t format) |
223 | { |
224 | if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names)) |
225 | return "Unknown" ; |
226 | return snd_pcm_format_names[(__force unsigned int)format]; |
227 | } |
228 | EXPORT_SYMBOL_GPL(snd_pcm_format_name); |
229 | |
230 | #ifdef CONFIG_SND_VERBOSE_PROCFS |
231 | |
232 | #define STATE(v) [SNDRV_PCM_STATE_##v] = #v |
233 | #define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v |
234 | #define READY(v) [SNDRV_PCM_READY_##v] = #v |
235 | #define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v |
236 | #define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v |
237 | #define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v |
238 | #define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v |
239 | #define START(v) [SNDRV_PCM_START_##v] = #v |
240 | #define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v |
241 | |
242 | static const char * const snd_pcm_stream_names[] = { |
243 | STREAM(PLAYBACK), |
244 | STREAM(CAPTURE), |
245 | }; |
246 | |
247 | static const char * const snd_pcm_state_names[] = { |
248 | STATE(OPEN), |
249 | STATE(SETUP), |
250 | STATE(PREPARED), |
251 | STATE(RUNNING), |
252 | STATE(XRUN), |
253 | STATE(DRAINING), |
254 | STATE(PAUSED), |
255 | STATE(SUSPENDED), |
256 | }; |
257 | |
258 | static const char * const snd_pcm_access_names[] = { |
259 | ACCESS(MMAP_INTERLEAVED), |
260 | ACCESS(MMAP_NONINTERLEAVED), |
261 | ACCESS(MMAP_COMPLEX), |
262 | ACCESS(RW_INTERLEAVED), |
263 | ACCESS(RW_NONINTERLEAVED), |
264 | }; |
265 | |
266 | static const char * const snd_pcm_subformat_names[] = { |
267 | SUBFORMAT(STD), |
268 | }; |
269 | |
270 | static const char * const snd_pcm_tstamp_mode_names[] = { |
271 | TSTAMP(NONE), |
272 | TSTAMP(ENABLE), |
273 | }; |
274 | |
275 | static const char *snd_pcm_stream_name(int stream) |
276 | { |
277 | return snd_pcm_stream_names[stream]; |
278 | } |
279 | |
280 | static const char *snd_pcm_access_name(snd_pcm_access_t access) |
281 | { |
282 | return snd_pcm_access_names[(__force int)access]; |
283 | } |
284 | |
285 | static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat) |
286 | { |
287 | return snd_pcm_subformat_names[(__force int)subformat]; |
288 | } |
289 | |
290 | static const char *snd_pcm_tstamp_mode_name(int mode) |
291 | { |
292 | return snd_pcm_tstamp_mode_names[mode]; |
293 | } |
294 | |
295 | static const char *snd_pcm_state_name(snd_pcm_state_t state) |
296 | { |
297 | return snd_pcm_state_names[(__force int)state]; |
298 | } |
299 | |
300 | #if IS_ENABLED(CONFIG_SND_PCM_OSS) |
301 | #include <linux/soundcard.h> |
302 | |
303 | static const char *snd_pcm_oss_format_name(int format) |
304 | { |
305 | switch (format) { |
306 | case AFMT_MU_LAW: |
307 | return "MU_LAW" ; |
308 | case AFMT_A_LAW: |
309 | return "A_LAW" ; |
310 | case AFMT_IMA_ADPCM: |
311 | return "IMA_ADPCM" ; |
312 | case AFMT_U8: |
313 | return "U8" ; |
314 | case AFMT_S16_LE: |
315 | return "S16_LE" ; |
316 | case AFMT_S16_BE: |
317 | return "S16_BE" ; |
318 | case AFMT_S8: |
319 | return "S8" ; |
320 | case AFMT_U16_LE: |
321 | return "U16_LE" ; |
322 | case AFMT_U16_BE: |
323 | return "U16_BE" ; |
324 | case AFMT_MPEG: |
325 | return "MPEG" ; |
326 | default: |
327 | return "unknown" ; |
328 | } |
329 | } |
330 | #endif |
331 | |
332 | static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream, |
333 | struct snd_info_buffer *buffer) |
334 | { |
335 | struct snd_pcm_info *info; |
336 | int err; |
337 | |
338 | if (! substream) |
339 | return; |
340 | |
341 | info = kmalloc(size: sizeof(*info), GFP_KERNEL); |
342 | if (!info) |
343 | return; |
344 | |
345 | err = snd_pcm_info(substream, info); |
346 | if (err < 0) { |
347 | snd_iprintf(buffer, "error %d\n" , err); |
348 | kfree(objp: info); |
349 | return; |
350 | } |
351 | snd_iprintf(buffer, "card: %d\n" , info->card); |
352 | snd_iprintf(buffer, "device: %d\n" , info->device); |
353 | snd_iprintf(buffer, "subdevice: %d\n" , info->subdevice); |
354 | snd_iprintf(buffer, "stream: %s\n" , snd_pcm_stream_name(info->stream)); |
355 | snd_iprintf(buffer, "id: %s\n" , info->id); |
356 | snd_iprintf(buffer, "name: %s\n" , info->name); |
357 | snd_iprintf(buffer, "subname: %s\n" , info->subname); |
358 | snd_iprintf(buffer, "class: %d\n" , info->dev_class); |
359 | snd_iprintf(buffer, "subclass: %d\n" , info->dev_subclass); |
360 | snd_iprintf(buffer, "subdevices_count: %d\n" , info->subdevices_count); |
361 | snd_iprintf(buffer, "subdevices_avail: %d\n" , info->subdevices_avail); |
362 | kfree(objp: info); |
363 | } |
364 | |
365 | static void snd_pcm_stream_proc_info_read(struct snd_info_entry *entry, |
366 | struct snd_info_buffer *buffer) |
367 | { |
368 | snd_pcm_proc_info_read(substream: ((struct snd_pcm_str *)entry->private_data)->substream, |
369 | buffer); |
370 | } |
371 | |
372 | static void snd_pcm_substream_proc_info_read(struct snd_info_entry *entry, |
373 | struct snd_info_buffer *buffer) |
374 | { |
375 | snd_pcm_proc_info_read(substream: entry->private_data, buffer); |
376 | } |
377 | |
378 | static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry, |
379 | struct snd_info_buffer *buffer) |
380 | { |
381 | struct snd_pcm_substream *substream = entry->private_data; |
382 | struct snd_pcm_runtime *runtime; |
383 | |
384 | mutex_lock(&substream->pcm->open_mutex); |
385 | runtime = substream->runtime; |
386 | if (!runtime) { |
387 | snd_iprintf(buffer, "closed\n" ); |
388 | goto unlock; |
389 | } |
390 | if (runtime->state == SNDRV_PCM_STATE_OPEN) { |
391 | snd_iprintf(buffer, "no setup\n" ); |
392 | goto unlock; |
393 | } |
394 | snd_iprintf(buffer, "access: %s\n" , snd_pcm_access_name(runtime->access)); |
395 | snd_iprintf(buffer, "format: %s\n" , snd_pcm_format_name(runtime->format)); |
396 | snd_iprintf(buffer, "subformat: %s\n" , snd_pcm_subformat_name(runtime->subformat)); |
397 | snd_iprintf(buffer, "channels: %u\n" , runtime->channels); |
398 | snd_iprintf(buffer, "rate: %u (%u/%u)\n" , runtime->rate, runtime->rate_num, runtime->rate_den); |
399 | snd_iprintf(buffer, "period_size: %lu\n" , runtime->period_size); |
400 | snd_iprintf(buffer, "buffer_size: %lu\n" , runtime->buffer_size); |
401 | #if IS_ENABLED(CONFIG_SND_PCM_OSS) |
402 | if (substream->oss.oss) { |
403 | snd_iprintf(buffer, "OSS format: %s\n" , snd_pcm_oss_format_name(runtime->oss.format)); |
404 | snd_iprintf(buffer, "OSS channels: %u\n" , runtime->oss.channels); |
405 | snd_iprintf(buffer, "OSS rate: %u\n" , runtime->oss.rate); |
406 | snd_iprintf(buffer, "OSS period bytes: %lu\n" , (unsigned long)runtime->oss.period_bytes); |
407 | snd_iprintf(buffer, "OSS periods: %u\n" , runtime->oss.periods); |
408 | snd_iprintf(buffer, "OSS period frames: %lu\n" , (unsigned long)runtime->oss.period_frames); |
409 | } |
410 | #endif |
411 | unlock: |
412 | mutex_unlock(lock: &substream->pcm->open_mutex); |
413 | } |
414 | |
415 | static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, |
416 | struct snd_info_buffer *buffer) |
417 | { |
418 | struct snd_pcm_substream *substream = entry->private_data; |
419 | struct snd_pcm_runtime *runtime; |
420 | |
421 | mutex_lock(&substream->pcm->open_mutex); |
422 | runtime = substream->runtime; |
423 | if (!runtime) { |
424 | snd_iprintf(buffer, "closed\n" ); |
425 | goto unlock; |
426 | } |
427 | if (runtime->state == SNDRV_PCM_STATE_OPEN) { |
428 | snd_iprintf(buffer, "no setup\n" ); |
429 | goto unlock; |
430 | } |
431 | snd_iprintf(buffer, "tstamp_mode: %s\n" , snd_pcm_tstamp_mode_name(runtime->tstamp_mode)); |
432 | snd_iprintf(buffer, "period_step: %u\n" , runtime->period_step); |
433 | snd_iprintf(buffer, "avail_min: %lu\n" , runtime->control->avail_min); |
434 | snd_iprintf(buffer, "start_threshold: %lu\n" , runtime->start_threshold); |
435 | snd_iprintf(buffer, "stop_threshold: %lu\n" , runtime->stop_threshold); |
436 | snd_iprintf(buffer, "silence_threshold: %lu\n" , runtime->silence_threshold); |
437 | snd_iprintf(buffer, "silence_size: %lu\n" , runtime->silence_size); |
438 | snd_iprintf(buffer, "boundary: %lu\n" , runtime->boundary); |
439 | unlock: |
440 | mutex_unlock(lock: &substream->pcm->open_mutex); |
441 | } |
442 | |
443 | static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, |
444 | struct snd_info_buffer *buffer) |
445 | { |
446 | struct snd_pcm_substream *substream = entry->private_data; |
447 | struct snd_pcm_runtime *runtime; |
448 | struct snd_pcm_status64 status; |
449 | int err; |
450 | |
451 | mutex_lock(&substream->pcm->open_mutex); |
452 | runtime = substream->runtime; |
453 | if (!runtime) { |
454 | snd_iprintf(buffer, "closed\n" ); |
455 | goto unlock; |
456 | } |
457 | memset(&status, 0, sizeof(status)); |
458 | err = snd_pcm_status64(substream, status: &status); |
459 | if (err < 0) { |
460 | snd_iprintf(buffer, "error %d\n" , err); |
461 | goto unlock; |
462 | } |
463 | snd_iprintf(buffer, "state: %s\n" , snd_pcm_state_name(status.state)); |
464 | snd_iprintf(buffer, "owner_pid : %d\n" , pid_vnr(substream->pid)); |
465 | snd_iprintf(buffer, "trigger_time: %lld.%09lld\n" , |
466 | status.trigger_tstamp_sec, status.trigger_tstamp_nsec); |
467 | snd_iprintf(buffer, "tstamp : %lld.%09lld\n" , |
468 | status.tstamp_sec, status.tstamp_nsec); |
469 | snd_iprintf(buffer, "delay : %ld\n" , status.delay); |
470 | snd_iprintf(buffer, "avail : %ld\n" , status.avail); |
471 | snd_iprintf(buffer, "avail_max : %ld\n" , status.avail_max); |
472 | snd_iprintf(buffer, "-----\n" ); |
473 | snd_iprintf(buffer, "hw_ptr : %ld\n" , runtime->status->hw_ptr); |
474 | snd_iprintf(buffer, "appl_ptr : %ld\n" , runtime->control->appl_ptr); |
475 | unlock: |
476 | mutex_unlock(lock: &substream->pcm->open_mutex); |
477 | } |
478 | |
479 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG |
480 | static void snd_pcm_xrun_injection_write(struct snd_info_entry *entry, |
481 | struct snd_info_buffer *buffer) |
482 | { |
483 | struct snd_pcm_substream *substream = entry->private_data; |
484 | |
485 | snd_pcm_stop_xrun(substream); |
486 | } |
487 | |
488 | static void snd_pcm_xrun_debug_read(struct snd_info_entry *entry, |
489 | struct snd_info_buffer *buffer) |
490 | { |
491 | struct snd_pcm_str *pstr = entry->private_data; |
492 | snd_iprintf(buffer, "%d\n" , pstr->xrun_debug); |
493 | } |
494 | |
495 | static void snd_pcm_xrun_debug_write(struct snd_info_entry *entry, |
496 | struct snd_info_buffer *buffer) |
497 | { |
498 | struct snd_pcm_str *pstr = entry->private_data; |
499 | char line[64]; |
500 | if (!snd_info_get_line(buffer, line, len: sizeof(line))) |
501 | pstr->xrun_debug = simple_strtoul(line, NULL, 10); |
502 | } |
503 | #endif |
504 | |
505 | static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) |
506 | { |
507 | struct snd_pcm *pcm = pstr->pcm; |
508 | struct snd_info_entry *entry; |
509 | char name[16]; |
510 | |
511 | sprintf(buf: name, fmt: "pcm%i%c" , pcm->device, |
512 | pstr->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); |
513 | entry = snd_info_create_card_entry(card: pcm->card, name, |
514 | parent: pcm->card->proc_root); |
515 | if (!entry) |
516 | return -ENOMEM; |
517 | entry->mode = S_IFDIR | 0555; |
518 | pstr->proc_root = entry; |
519 | entry = snd_info_create_card_entry(card: pcm->card, name: "info" , parent: pstr->proc_root); |
520 | if (entry) |
521 | snd_info_set_text_ops(entry, private_data: pstr, read: snd_pcm_stream_proc_info_read); |
522 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG |
523 | entry = snd_info_create_card_entry(card: pcm->card, name: "xrun_debug" , |
524 | parent: pstr->proc_root); |
525 | if (entry) { |
526 | snd_info_set_text_ops(entry, private_data: pstr, read: snd_pcm_xrun_debug_read); |
527 | entry->c.text.write = snd_pcm_xrun_debug_write; |
528 | entry->mode |= 0200; |
529 | } |
530 | #endif |
531 | return 0; |
532 | } |
533 | |
534 | static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) |
535 | { |
536 | snd_info_free_entry(entry: pstr->proc_root); |
537 | pstr->proc_root = NULL; |
538 | return 0; |
539 | } |
540 | |
541 | static struct snd_info_entry * |
542 | create_substream_info_entry(struct snd_pcm_substream *substream, |
543 | const char *name, |
544 | void (*read)(struct snd_info_entry *, |
545 | struct snd_info_buffer *)) |
546 | { |
547 | struct snd_info_entry *entry; |
548 | |
549 | entry = snd_info_create_card_entry(card: substream->pcm->card, name, |
550 | parent: substream->proc_root); |
551 | if (entry) |
552 | snd_info_set_text_ops(entry, private_data: substream, read); |
553 | return entry; |
554 | } |
555 | |
556 | static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) |
557 | { |
558 | struct snd_info_entry *entry; |
559 | struct snd_card *card; |
560 | char name[16]; |
561 | |
562 | card = substream->pcm->card; |
563 | |
564 | sprintf(buf: name, fmt: "sub%i" , substream->number); |
565 | entry = snd_info_create_card_entry(card, name, |
566 | parent: substream->pstr->proc_root); |
567 | if (!entry) |
568 | return -ENOMEM; |
569 | entry->mode = S_IFDIR | 0555; |
570 | substream->proc_root = entry; |
571 | |
572 | create_substream_info_entry(substream, name: "info" , |
573 | read: snd_pcm_substream_proc_info_read); |
574 | create_substream_info_entry(substream, name: "hw_params" , |
575 | read: snd_pcm_substream_proc_hw_params_read); |
576 | create_substream_info_entry(substream, name: "sw_params" , |
577 | read: snd_pcm_substream_proc_sw_params_read); |
578 | create_substream_info_entry(substream, name: "status" , |
579 | read: snd_pcm_substream_proc_status_read); |
580 | |
581 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG |
582 | entry = create_substream_info_entry(substream, name: "xrun_injection" , NULL); |
583 | if (entry) { |
584 | entry->c.text.write = snd_pcm_xrun_injection_write; |
585 | entry->mode = S_IFREG | 0200; |
586 | } |
587 | #endif /* CONFIG_SND_PCM_XRUN_DEBUG */ |
588 | |
589 | return 0; |
590 | } |
591 | |
592 | #else /* !CONFIG_SND_VERBOSE_PROCFS */ |
593 | static inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; } |
594 | static inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; } |
595 | static inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; } |
596 | #endif /* CONFIG_SND_VERBOSE_PROCFS */ |
597 | |
598 | static const struct attribute_group *pcm_dev_attr_groups[]; |
599 | |
600 | /* |
601 | * PM callbacks: we need to deal only with suspend here, as the resume is |
602 | * triggered either from user-space or the driver's resume callback |
603 | */ |
604 | #ifdef CONFIG_PM_SLEEP |
605 | static int do_pcm_suspend(struct device *dev) |
606 | { |
607 | struct snd_pcm_str *pstr = dev_get_drvdata(dev); |
608 | |
609 | if (!pstr->pcm->no_device_suspend) |
610 | snd_pcm_suspend_all(pcm: pstr->pcm); |
611 | return 0; |
612 | } |
613 | #endif |
614 | |
615 | static const struct dev_pm_ops pcm_dev_pm_ops = { |
616 | SET_SYSTEM_SLEEP_PM_OPS(do_pcm_suspend, NULL) |
617 | }; |
618 | |
619 | /* device type for PCM -- basically only for passing PM callbacks */ |
620 | static const struct device_type pcm_dev_type = { |
621 | .name = "pcm" , |
622 | .pm = &pcm_dev_pm_ops, |
623 | }; |
624 | |
625 | /** |
626 | * snd_pcm_new_stream - create a new PCM stream |
627 | * @pcm: the pcm instance |
628 | * @stream: the stream direction, SNDRV_PCM_STREAM_XXX |
629 | * @substream_count: the number of substreams |
630 | * |
631 | * Creates a new stream for the pcm. |
632 | * The corresponding stream on the pcm must have been empty before |
633 | * calling this, i.e. zero must be given to the argument of |
634 | * snd_pcm_new(). |
635 | * |
636 | * Return: Zero if successful, or a negative error code on failure. |
637 | */ |
638 | int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) |
639 | { |
640 | int idx, err; |
641 | struct snd_pcm_str *pstr = &pcm->streams[stream]; |
642 | struct snd_pcm_substream *substream, *prev; |
643 | |
644 | #if IS_ENABLED(CONFIG_SND_PCM_OSS) |
645 | mutex_init(&pstr->oss.setup_mutex); |
646 | #endif |
647 | pstr->stream = stream; |
648 | pstr->pcm = pcm; |
649 | pstr->substream_count = substream_count; |
650 | if (!substream_count) |
651 | return 0; |
652 | |
653 | err = snd_device_alloc(dev_p: &pstr->dev, card: pcm->card); |
654 | if (err < 0) |
655 | return err; |
656 | dev_set_name(dev: pstr->dev, name: "pcmC%iD%i%c" , pcm->card->number, pcm->device, |
657 | stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); |
658 | pstr->dev->groups = pcm_dev_attr_groups; |
659 | pstr->dev->type = &pcm_dev_type; |
660 | dev_set_drvdata(dev: pstr->dev, data: pstr); |
661 | |
662 | if (!pcm->internal) { |
663 | err = snd_pcm_stream_proc_init(pstr); |
664 | if (err < 0) { |
665 | pcm_err(pcm, "Error in snd_pcm_stream_proc_init\n" ); |
666 | return err; |
667 | } |
668 | } |
669 | prev = NULL; |
670 | for (idx = 0, prev = NULL; idx < substream_count; idx++) { |
671 | substream = kzalloc(size: sizeof(*substream), GFP_KERNEL); |
672 | if (!substream) |
673 | return -ENOMEM; |
674 | substream->pcm = pcm; |
675 | substream->pstr = pstr; |
676 | substream->number = idx; |
677 | substream->stream = stream; |
678 | sprintf(buf: substream->name, fmt: "subdevice #%i" , idx); |
679 | substream->buffer_bytes_max = UINT_MAX; |
680 | if (prev == NULL) |
681 | pstr->substream = substream; |
682 | else |
683 | prev->next = substream; |
684 | |
685 | if (!pcm->internal) { |
686 | err = snd_pcm_substream_proc_init(substream); |
687 | if (err < 0) { |
688 | pcm_err(pcm, |
689 | "Error in snd_pcm_stream_proc_init\n" ); |
690 | if (prev == NULL) |
691 | pstr->substream = NULL; |
692 | else |
693 | prev->next = NULL; |
694 | kfree(objp: substream); |
695 | return err; |
696 | } |
697 | } |
698 | substream->group = &substream->self_group; |
699 | snd_pcm_group_init(group: &substream->self_group); |
700 | list_add_tail(new: &substream->link_list, head: &substream->self_group.substreams); |
701 | atomic_set(v: &substream->mmap_count, i: 0); |
702 | prev = substream; |
703 | } |
704 | return 0; |
705 | } |
706 | EXPORT_SYMBOL(snd_pcm_new_stream); |
707 | |
708 | static int _snd_pcm_new(struct snd_card *card, const char *id, int device, |
709 | int playback_count, int capture_count, bool internal, |
710 | struct snd_pcm **rpcm) |
711 | { |
712 | struct snd_pcm *pcm; |
713 | int err; |
714 | static const struct snd_device_ops ops = { |
715 | .dev_free = snd_pcm_dev_free, |
716 | .dev_register = snd_pcm_dev_register, |
717 | .dev_disconnect = snd_pcm_dev_disconnect, |
718 | }; |
719 | static const struct snd_device_ops internal_ops = { |
720 | .dev_free = snd_pcm_dev_free, |
721 | }; |
722 | |
723 | if (snd_BUG_ON(!card)) |
724 | return -ENXIO; |
725 | if (rpcm) |
726 | *rpcm = NULL; |
727 | pcm = kzalloc(size: sizeof(*pcm), GFP_KERNEL); |
728 | if (!pcm) |
729 | return -ENOMEM; |
730 | pcm->card = card; |
731 | pcm->device = device; |
732 | pcm->internal = internal; |
733 | mutex_init(&pcm->open_mutex); |
734 | init_waitqueue_head(&pcm->open_wait); |
735 | INIT_LIST_HEAD(list: &pcm->list); |
736 | if (id) |
737 | strscpy(p: pcm->id, q: id, size: sizeof(pcm->id)); |
738 | |
739 | err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, |
740 | playback_count); |
741 | if (err < 0) |
742 | goto free_pcm; |
743 | |
744 | err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count); |
745 | if (err < 0) |
746 | goto free_pcm; |
747 | |
748 | err = snd_device_new(card, type: SNDRV_DEV_PCM, device_data: pcm, |
749 | ops: internal ? &internal_ops : &ops); |
750 | if (err < 0) |
751 | goto free_pcm; |
752 | |
753 | if (rpcm) |
754 | *rpcm = pcm; |
755 | return 0; |
756 | |
757 | free_pcm: |
758 | snd_pcm_free(pcm); |
759 | return err; |
760 | } |
761 | |
762 | /** |
763 | * snd_pcm_new - create a new PCM instance |
764 | * @card: the card instance |
765 | * @id: the id string |
766 | * @device: the device index (zero based) |
767 | * @playback_count: the number of substreams for playback |
768 | * @capture_count: the number of substreams for capture |
769 | * @rpcm: the pointer to store the new pcm instance |
770 | * |
771 | * Creates a new PCM instance. |
772 | * |
773 | * The pcm operators have to be set afterwards to the new instance |
774 | * via snd_pcm_set_ops(). |
775 | * |
776 | * Return: Zero if successful, or a negative error code on failure. |
777 | */ |
778 | int snd_pcm_new(struct snd_card *card, const char *id, int device, |
779 | int playback_count, int capture_count, struct snd_pcm **rpcm) |
780 | { |
781 | return _snd_pcm_new(card, id, device, playback_count, capture_count, |
782 | internal: false, rpcm); |
783 | } |
784 | EXPORT_SYMBOL(snd_pcm_new); |
785 | |
786 | /** |
787 | * snd_pcm_new_internal - create a new internal PCM instance |
788 | * @card: the card instance |
789 | * @id: the id string |
790 | * @device: the device index (zero based - shared with normal PCMs) |
791 | * @playback_count: the number of substreams for playback |
792 | * @capture_count: the number of substreams for capture |
793 | * @rpcm: the pointer to store the new pcm instance |
794 | * |
795 | * Creates a new internal PCM instance with no userspace device or procfs |
796 | * entries. This is used by ASoC Back End PCMs in order to create a PCM that |
797 | * will only be used internally by kernel drivers. i.e. it cannot be opened |
798 | * by userspace. It provides existing ASoC components drivers with a substream |
799 | * and access to any private data. |
800 | * |
801 | * The pcm operators have to be set afterwards to the new instance |
802 | * via snd_pcm_set_ops(). |
803 | * |
804 | * Return: Zero if successful, or a negative error code on failure. |
805 | */ |
806 | int snd_pcm_new_internal(struct snd_card *card, const char *id, int device, |
807 | int playback_count, int capture_count, |
808 | struct snd_pcm **rpcm) |
809 | { |
810 | return _snd_pcm_new(card, id, device, playback_count, capture_count, |
811 | internal: true, rpcm); |
812 | } |
813 | EXPORT_SYMBOL(snd_pcm_new_internal); |
814 | |
815 | static void free_chmap(struct snd_pcm_str *pstr) |
816 | { |
817 | if (pstr->chmap_kctl) { |
818 | struct snd_card *card = pstr->pcm->card; |
819 | |
820 | snd_ctl_remove(card, kcontrol: pstr->chmap_kctl); |
821 | pstr->chmap_kctl = NULL; |
822 | } |
823 | } |
824 | |
825 | static void snd_pcm_free_stream(struct snd_pcm_str * pstr) |
826 | { |
827 | struct snd_pcm_substream *substream, *substream_next; |
828 | #if IS_ENABLED(CONFIG_SND_PCM_OSS) |
829 | struct snd_pcm_oss_setup *setup, *setupn; |
830 | #endif |
831 | |
832 | /* free all proc files under the stream */ |
833 | snd_pcm_stream_proc_done(pstr); |
834 | |
835 | substream = pstr->substream; |
836 | while (substream) { |
837 | substream_next = substream->next; |
838 | snd_pcm_timer_done(substream); |
839 | kfree(objp: substream); |
840 | substream = substream_next; |
841 | } |
842 | #if IS_ENABLED(CONFIG_SND_PCM_OSS) |
843 | for (setup = pstr->oss.setup_list; setup; setup = setupn) { |
844 | setupn = setup->next; |
845 | kfree(objp: setup->task_name); |
846 | kfree(objp: setup); |
847 | } |
848 | #endif |
849 | free_chmap(pstr); |
850 | if (pstr->substream_count) |
851 | put_device(dev: pstr->dev); |
852 | } |
853 | |
854 | #if IS_ENABLED(CONFIG_SND_PCM_OSS) |
855 | #define pcm_call_notify(pcm, call) \ |
856 | do { \ |
857 | struct snd_pcm_notify *_notify; \ |
858 | list_for_each_entry(_notify, &snd_pcm_notify_list, list) \ |
859 | _notify->call(pcm); \ |
860 | } while (0) |
861 | #else |
862 | #define pcm_call_notify(pcm, call) do {} while (0) |
863 | #endif |
864 | |
865 | static int snd_pcm_free(struct snd_pcm *pcm) |
866 | { |
867 | if (!pcm) |
868 | return 0; |
869 | if (!pcm->internal) |
870 | pcm_call_notify(pcm, n_unregister); |
871 | if (pcm->private_free) |
872 | pcm->private_free(pcm); |
873 | snd_pcm_lib_preallocate_free_for_all(pcm); |
874 | snd_pcm_free_stream(pstr: &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]); |
875 | snd_pcm_free_stream(pstr: &pcm->streams[SNDRV_PCM_STREAM_CAPTURE]); |
876 | kfree(objp: pcm); |
877 | return 0; |
878 | } |
879 | |
880 | static int snd_pcm_dev_free(struct snd_device *device) |
881 | { |
882 | struct snd_pcm *pcm = device->device_data; |
883 | return snd_pcm_free(pcm); |
884 | } |
885 | |
886 | int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, |
887 | struct file *file, |
888 | struct snd_pcm_substream **rsubstream) |
889 | { |
890 | struct snd_pcm_str * pstr; |
891 | struct snd_pcm_substream *substream; |
892 | struct snd_pcm_runtime *runtime; |
893 | struct snd_card *card; |
894 | int prefer_subdevice; |
895 | size_t size; |
896 | |
897 | if (snd_BUG_ON(!pcm || !rsubstream)) |
898 | return -ENXIO; |
899 | if (snd_BUG_ON(stream != SNDRV_PCM_STREAM_PLAYBACK && |
900 | stream != SNDRV_PCM_STREAM_CAPTURE)) |
901 | return -EINVAL; |
902 | *rsubstream = NULL; |
903 | pstr = &pcm->streams[stream]; |
904 | if (pstr->substream == NULL || pstr->substream_count == 0) |
905 | return -ENODEV; |
906 | |
907 | card = pcm->card; |
908 | prefer_subdevice = snd_ctl_get_preferred_subdevice(card, type: SND_CTL_SUBDEV_PCM); |
909 | |
910 | if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { |
911 | int opposite = !stream; |
912 | |
913 | for (substream = pcm->streams[opposite].substream; substream; |
914 | substream = substream->next) { |
915 | if (SUBSTREAM_BUSY(substream)) |
916 | return -EAGAIN; |
917 | } |
918 | } |
919 | |
920 | if (file->f_flags & O_APPEND) { |
921 | if (prefer_subdevice < 0) { |
922 | if (pstr->substream_count > 1) |
923 | return -EINVAL; /* must be unique */ |
924 | substream = pstr->substream; |
925 | } else { |
926 | for (substream = pstr->substream; substream; |
927 | substream = substream->next) |
928 | if (substream->number == prefer_subdevice) |
929 | break; |
930 | } |
931 | if (! substream) |
932 | return -ENODEV; |
933 | if (! SUBSTREAM_BUSY(substream)) |
934 | return -EBADFD; |
935 | substream->ref_count++; |
936 | *rsubstream = substream; |
937 | return 0; |
938 | } |
939 | |
940 | for (substream = pstr->substream; substream; substream = substream->next) { |
941 | if (!SUBSTREAM_BUSY(substream) && |
942 | (prefer_subdevice == -1 || |
943 | substream->number == prefer_subdevice)) |
944 | break; |
945 | } |
946 | if (substream == NULL) |
947 | return -EAGAIN; |
948 | |
949 | runtime = kzalloc(size: sizeof(*runtime), GFP_KERNEL); |
950 | if (runtime == NULL) |
951 | return -ENOMEM; |
952 | |
953 | size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)); |
954 | runtime->status = alloc_pages_exact(size, GFP_KERNEL); |
955 | if (runtime->status == NULL) { |
956 | kfree(objp: runtime); |
957 | return -ENOMEM; |
958 | } |
959 | memset(runtime->status, 0, size); |
960 | |
961 | size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)); |
962 | runtime->control = alloc_pages_exact(size, GFP_KERNEL); |
963 | if (runtime->control == NULL) { |
964 | free_pages_exact(virt: runtime->status, |
965 | PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); |
966 | kfree(objp: runtime); |
967 | return -ENOMEM; |
968 | } |
969 | memset(runtime->control, 0, size); |
970 | |
971 | init_waitqueue_head(&runtime->sleep); |
972 | init_waitqueue_head(&runtime->tsleep); |
973 | |
974 | __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_OPEN); |
975 | mutex_init(&runtime->buffer_mutex); |
976 | atomic_set(v: &runtime->buffer_accessing, i: 0); |
977 | |
978 | substream->runtime = runtime; |
979 | substream->private_data = pcm->private_data; |
980 | substream->ref_count = 1; |
981 | substream->f_flags = file->f_flags; |
982 | substream->pid = get_pid(pid: task_pid(current)); |
983 | pstr->substream_opened++; |
984 | *rsubstream = substream; |
985 | return 0; |
986 | } |
987 | |
988 | void snd_pcm_detach_substream(struct snd_pcm_substream *substream) |
989 | { |
990 | struct snd_pcm_runtime *runtime; |
991 | |
992 | if (PCM_RUNTIME_CHECK(substream)) |
993 | return; |
994 | runtime = substream->runtime; |
995 | if (runtime->private_free != NULL) |
996 | runtime->private_free(runtime); |
997 | free_pages_exact(virt: runtime->status, |
998 | PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); |
999 | free_pages_exact(virt: runtime->control, |
1000 | PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); |
1001 | kfree(objp: runtime->hw_constraints.rules); |
1002 | /* Avoid concurrent access to runtime via PCM timer interface */ |
1003 | if (substream->timer) { |
1004 | spin_lock_irq(lock: &substream->timer->lock); |
1005 | substream->runtime = NULL; |
1006 | spin_unlock_irq(lock: &substream->timer->lock); |
1007 | } else { |
1008 | substream->runtime = NULL; |
1009 | } |
1010 | mutex_destroy(lock: &runtime->buffer_mutex); |
1011 | snd_fasync_free(fasync: runtime->fasync); |
1012 | kfree(objp: runtime); |
1013 | put_pid(pid: substream->pid); |
1014 | substream->pid = NULL; |
1015 | substream->pstr->substream_opened--; |
1016 | } |
1017 | |
1018 | static ssize_t pcm_class_show(struct device *dev, |
1019 | struct device_attribute *attr, char *buf) |
1020 | { |
1021 | struct snd_pcm_str *pstr = dev_get_drvdata(dev); |
1022 | struct snd_pcm *pcm = pstr->pcm; |
1023 | const char *str; |
1024 | static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = { |
1025 | [SNDRV_PCM_CLASS_GENERIC] = "generic" , |
1026 | [SNDRV_PCM_CLASS_MULTI] = "multi" , |
1027 | [SNDRV_PCM_CLASS_MODEM] = "modem" , |
1028 | [SNDRV_PCM_CLASS_DIGITIZER] = "digitizer" , |
1029 | }; |
1030 | |
1031 | if (pcm->dev_class > SNDRV_PCM_CLASS_LAST) |
1032 | str = "none" ; |
1033 | else |
1034 | str = strs[pcm->dev_class]; |
1035 | return sysfs_emit(buf, fmt: "%s\n" , str); |
1036 | } |
1037 | |
1038 | static DEVICE_ATTR_RO(pcm_class); |
1039 | static struct attribute *pcm_dev_attrs[] = { |
1040 | &dev_attr_pcm_class.attr, |
1041 | NULL |
1042 | }; |
1043 | |
1044 | static const struct attribute_group pcm_dev_attr_group = { |
1045 | .attrs = pcm_dev_attrs, |
1046 | }; |
1047 | |
1048 | static const struct attribute_group *pcm_dev_attr_groups[] = { |
1049 | &pcm_dev_attr_group, |
1050 | NULL |
1051 | }; |
1052 | |
1053 | static int snd_pcm_dev_register(struct snd_device *device) |
1054 | { |
1055 | int cidx, err; |
1056 | struct snd_pcm_substream *substream; |
1057 | struct snd_pcm *pcm; |
1058 | |
1059 | if (snd_BUG_ON(!device || !device->device_data)) |
1060 | return -ENXIO; |
1061 | pcm = device->device_data; |
1062 | |
1063 | mutex_lock(®ister_mutex); |
1064 | err = snd_pcm_add(newpcm: pcm); |
1065 | if (err) |
1066 | goto unlock; |
1067 | for (cidx = 0; cidx < 2; cidx++) { |
1068 | int devtype = -1; |
1069 | if (pcm->streams[cidx].substream == NULL) |
1070 | continue; |
1071 | switch (cidx) { |
1072 | case SNDRV_PCM_STREAM_PLAYBACK: |
1073 | devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; |
1074 | break; |
1075 | case SNDRV_PCM_STREAM_CAPTURE: |
1076 | devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; |
1077 | break; |
1078 | } |
1079 | /* register pcm */ |
1080 | err = snd_register_device(type: devtype, card: pcm->card, dev: pcm->device, |
1081 | f_ops: &snd_pcm_f_ops[cidx], private_data: pcm, |
1082 | device: pcm->streams[cidx].dev); |
1083 | if (err < 0) { |
1084 | list_del_init(entry: &pcm->list); |
1085 | goto unlock; |
1086 | } |
1087 | |
1088 | for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) |
1089 | snd_pcm_timer_init(substream); |
1090 | } |
1091 | |
1092 | pcm_call_notify(pcm, n_register); |
1093 | |
1094 | unlock: |
1095 | mutex_unlock(lock: ®ister_mutex); |
1096 | return err; |
1097 | } |
1098 | |
1099 | static int snd_pcm_dev_disconnect(struct snd_device *device) |
1100 | { |
1101 | struct snd_pcm *pcm = device->device_data; |
1102 | struct snd_pcm_substream *substream; |
1103 | int cidx; |
1104 | |
1105 | mutex_lock(®ister_mutex); |
1106 | mutex_lock(&pcm->open_mutex); |
1107 | wake_up(&pcm->open_wait); |
1108 | list_del_init(entry: &pcm->list); |
1109 | |
1110 | for_each_pcm_substream(pcm, cidx, substream) { |
1111 | snd_pcm_stream_lock_irq(substream); |
1112 | if (substream->runtime) { |
1113 | if (snd_pcm_running(substream)) |
1114 | snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); |
1115 | /* to be sure, set the state unconditionally */ |
1116 | __snd_pcm_set_state(runtime: substream->runtime, |
1117 | SNDRV_PCM_STATE_DISCONNECTED); |
1118 | wake_up(&substream->runtime->sleep); |
1119 | wake_up(&substream->runtime->tsleep); |
1120 | } |
1121 | snd_pcm_stream_unlock_irq(substream); |
1122 | } |
1123 | |
1124 | for_each_pcm_substream(pcm, cidx, substream) |
1125 | snd_pcm_sync_stop(substream, sync_irq: false); |
1126 | |
1127 | pcm_call_notify(pcm, n_disconnect); |
1128 | for (cidx = 0; cidx < 2; cidx++) { |
1129 | if (pcm->streams[cidx].dev) |
1130 | snd_unregister_device(dev: pcm->streams[cidx].dev); |
1131 | free_chmap(pstr: &pcm->streams[cidx]); |
1132 | } |
1133 | mutex_unlock(lock: &pcm->open_mutex); |
1134 | mutex_unlock(lock: ®ister_mutex); |
1135 | return 0; |
1136 | } |
1137 | |
1138 | #if IS_ENABLED(CONFIG_SND_PCM_OSS) |
1139 | /** |
1140 | * snd_pcm_notify - Add/remove the notify list |
1141 | * @notify: PCM notify list |
1142 | * @nfree: 0 = register, 1 = unregister |
1143 | * |
1144 | * This adds the given notifier to the global list so that the callback is |
1145 | * called for each registered PCM devices. This exists only for PCM OSS |
1146 | * emulation, so far. |
1147 | * |
1148 | * Return: zero if successful, or a negative error code |
1149 | */ |
1150 | int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) |
1151 | { |
1152 | struct snd_pcm *pcm; |
1153 | |
1154 | if (snd_BUG_ON(!notify || |
1155 | !notify->n_register || |
1156 | !notify->n_unregister || |
1157 | !notify->n_disconnect)) |
1158 | return -EINVAL; |
1159 | mutex_lock(®ister_mutex); |
1160 | if (nfree) { |
1161 | list_del(entry: ¬ify->list); |
1162 | list_for_each_entry(pcm, &snd_pcm_devices, list) |
1163 | notify->n_unregister(pcm); |
1164 | } else { |
1165 | list_add_tail(new: ¬ify->list, head: &snd_pcm_notify_list); |
1166 | list_for_each_entry(pcm, &snd_pcm_devices, list) |
1167 | notify->n_register(pcm); |
1168 | } |
1169 | mutex_unlock(lock: ®ister_mutex); |
1170 | return 0; |
1171 | } |
1172 | EXPORT_SYMBOL(snd_pcm_notify); |
1173 | #endif /* CONFIG_SND_PCM_OSS */ |
1174 | |
1175 | #ifdef CONFIG_SND_PROC_FS |
1176 | /* |
1177 | * Info interface |
1178 | */ |
1179 | |
1180 | static void snd_pcm_proc_read(struct snd_info_entry *entry, |
1181 | struct snd_info_buffer *buffer) |
1182 | { |
1183 | struct snd_pcm *pcm; |
1184 | |
1185 | mutex_lock(®ister_mutex); |
1186 | list_for_each_entry(pcm, &snd_pcm_devices, list) { |
1187 | snd_iprintf(buffer, "%02i-%02i: %s : %s" , |
1188 | pcm->card->number, pcm->device, pcm->id, pcm->name); |
1189 | if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) |
1190 | snd_iprintf(buffer, " : playback %i" , |
1191 | pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count); |
1192 | if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) |
1193 | snd_iprintf(buffer, " : capture %i" , |
1194 | pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count); |
1195 | snd_iprintf(buffer, "\n" ); |
1196 | } |
1197 | mutex_unlock(lock: ®ister_mutex); |
1198 | } |
1199 | |
1200 | static struct snd_info_entry *snd_pcm_proc_entry; |
1201 | |
1202 | static void snd_pcm_proc_init(void) |
1203 | { |
1204 | struct snd_info_entry *entry; |
1205 | |
1206 | entry = snd_info_create_module_entry(THIS_MODULE, name: "pcm" , NULL); |
1207 | if (entry) { |
1208 | snd_info_set_text_ops(entry, NULL, read: snd_pcm_proc_read); |
1209 | if (snd_info_register(entry) < 0) { |
1210 | snd_info_free_entry(entry); |
1211 | entry = NULL; |
1212 | } |
1213 | } |
1214 | snd_pcm_proc_entry = entry; |
1215 | } |
1216 | |
1217 | static void snd_pcm_proc_done(void) |
1218 | { |
1219 | snd_info_free_entry(entry: snd_pcm_proc_entry); |
1220 | } |
1221 | |
1222 | #else /* !CONFIG_SND_PROC_FS */ |
1223 | #define snd_pcm_proc_init() |
1224 | #define snd_pcm_proc_done() |
1225 | #endif /* CONFIG_SND_PROC_FS */ |
1226 | |
1227 | |
1228 | /* |
1229 | * ENTRY functions |
1230 | */ |
1231 | |
1232 | static int __init alsa_pcm_init(void) |
1233 | { |
1234 | snd_ctl_register_ioctl(fcn: snd_pcm_control_ioctl); |
1235 | snd_ctl_register_ioctl_compat(fcn: snd_pcm_control_ioctl); |
1236 | snd_pcm_proc_init(); |
1237 | return 0; |
1238 | } |
1239 | |
1240 | static void __exit alsa_pcm_exit(void) |
1241 | { |
1242 | snd_ctl_unregister_ioctl(fcn: snd_pcm_control_ioctl); |
1243 | snd_ctl_unregister_ioctl_compat(fcn: snd_pcm_control_ioctl); |
1244 | snd_pcm_proc_done(); |
1245 | } |
1246 | |
1247 | module_init(alsa_pcm_init) |
1248 | module_exit(alsa_pcm_exit) |
1249 | |