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